refactor(card-item): tokenize list-mode palette (Brief 1)
Brief 1 of cleanup-card-item-list-and-share-modal-palette convoy.
Pure token sweep across lines ~181-295 of components/CardItem.js
(the list-mode branch only — grid-mode at L290+ stays untouched
per Brief 1 § Known constraints).
Migrations:
- Outer row container:
- `border-purple-500 bg-purple-50 shadow-md` (selected) →
`borderColor: 'var(--accent-ember)' + backgroundColor:
'rgba(255, 110, 0, 0.08)' + boxShadow: 'var(--rim-light-inner)'`.
- `border-gray-200 hover:border-gray-300 hover:shadow-sm` (default) →
`borderColor: 'var(--border)' + nav-item-hover` class for the
ember-tinted hover state from the unify convoy.
- `rounded-lg` → `rounded-xl` (convoy-wide rounding consistency).
- Image placeholder:
- `bg-gray-200` → `var(--bg-tertiary)`.
- `text-gray-400` on the "No Image" fallback → `var(--text-secondary)`.
- Card info text:
- `text-gray-900` → `var(--text-primary)` (card name).
- `text-gray-600` / `text-gray-500` → `var(--text-secondary)` (set/rarity/type).
- Game badge:
- `bg-blue-100 text-blue-800` → `var(--bg-tertiary)` + `var(--text-primary)`
+ `1px solid var(--border)` for visible delimiter in both themes.
- Price:
- `text-green-600` → `var(--accent-flame)`. Decision: chose brand-warm
over semantic-green because the rest of the row is on the ember
palette and a single warm-tone price tag reads as "primary value"
rather than "positive delta from baseline". Easy to revert to
`#16a34a` literal if dark-mode reviewers prefer the green.
- Favorite button:
- `text-red-500 hover:text-red-600` (favorited) →
`var(--accent-ember)`.
- `text-gray-400 hover:text-red-500` (default) →
`var(--text-secondary)` + nav-item-hover.
- Add-to-collection button:
- `text-blue-600 hover:text-blue-700 hover:bg-blue-50` →
`var(--accent-ember)` + nav-item-hover.
- Add-to-deck button:
- `text-green-600 hover:text-green-700 hover:bg-green-50` →
`var(--accent-flame)` + nav-item-hover. Distinguishes from
add-to-collection by warm-tier (flame vs ember).
- `rounded-lg` on all action buttons → `rounded-xl`.
Verification:
- `sed -n '181,295p' components/CardItem.js | grep -nE
'bg-(purple|blue|gray|red|green)-[0-9]|text-(...)|border-(...)'`
→ 0 matches. ✅
- `npm run lint` passes (1 pre-existing unrelated warning).
- `npm run test:run`: 118/118 tests pass.
Grid-mode (L290+) intentionally untouched per Brief 1 scope.
The 5 remaining palette hardcodes in CardItem.js are all in
grid-mode getRarityEffects() at L150/157/172/350/359 — out of
scope for this brief; the convoy didn't target those because the
grid render path has its own visual treatment.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
b82fd6cb6a
commit
fa0f6b3a43
1 changed files with 52 additions and 20 deletions
|
|
@ -181,11 +181,15 @@ export default function CardItem({
|
||||||
if (viewMode === 'list') {
|
if (viewMode === 'list') {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`flex items-center p-4 border rounded-lg transition-all duration-200 cursor-pointer ${
|
className="flex items-center p-4 rounded-xl transition-all duration-200 cursor-pointer nav-item-hover"
|
||||||
isSelected
|
style={{
|
||||||
? 'border-purple-500 bg-purple-50 shadow-md'
|
backgroundColor: isSelected
|
||||||
: 'border-gray-200 hover:border-gray-300 hover:shadow-sm'
|
? 'rgba(255, 110, 0, 0.08)'
|
||||||
}`}
|
: 'transparent',
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: isSelected ? 'var(--accent-ember)' : 'var(--border)',
|
||||||
|
boxShadow: isSelected ? 'var(--rim-light-inner)' : 'none',
|
||||||
|
}}
|
||||||
onClick={handleCardClick}
|
onClick={handleCardClick}
|
||||||
>
|
>
|
||||||
{/* Selection Checkbox */}
|
{/* Selection Checkbox */}
|
||||||
|
|
@ -204,7 +208,10 @@ export default function CardItem({
|
||||||
|
|
||||||
{/* Card Image */}
|
{/* Card Image */}
|
||||||
<div className="flex-shrink-0 mr-4">
|
<div className="flex-shrink-0 mr-4">
|
||||||
<div className="w-16 h-22 bg-gray-200 rounded-lg overflow-hidden">
|
<div
|
||||||
|
className="w-16 h-22 rounded-lg overflow-hidden"
|
||||||
|
style={{ backgroundColor: 'var(--bg-tertiary)' }}
|
||||||
|
>
|
||||||
{!imageError ? (
|
{!imageError ? (
|
||||||
<img
|
<img
|
||||||
src={card.image_url}
|
src={card.image_url}
|
||||||
|
|
@ -213,7 +220,10 @@ export default function CardItem({
|
||||||
onError={handleImageError}
|
onError={handleImageError}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-full h-full flex items-center justify-center text-gray-400 text-xs">
|
<div
|
||||||
|
className="w-full h-full flex items-center justify-center text-xs"
|
||||||
|
style={{ color: 'var(--text-secondary)' }}
|
||||||
|
>
|
||||||
No Image
|
No Image
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -222,21 +232,40 @@ export default function CardItem({
|
||||||
|
|
||||||
{/* Card Info */}
|
{/* Card Info */}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<h3 className="font-semibold text-gray-900 truncate">{card.name}</h3>
|
<h3
|
||||||
<p className="text-sm text-gray-600">{card.set_name} • {card.rarity}</p>
|
className="font-semibold truncate"
|
||||||
<p className="text-sm text-gray-500">{card.card_type}</p>
|
style={{ color: 'var(--text-primary)' }}
|
||||||
|
>
|
||||||
|
{card.name}
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm" style={{ color: 'var(--text-secondary)' }}>
|
||||||
|
{card.set_name} • {card.rarity}
|
||||||
|
</p>
|
||||||
|
<p className="text-sm" style={{ color: 'var(--text-secondary)' }}>
|
||||||
|
{card.card_type}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Game Badge */}
|
{/* Game Badge */}
|
||||||
<div className="flex-shrink-0 mx-4">
|
<div className="flex-shrink-0 mx-4">
|
||||||
<span className="px-2 py-1 text-xs font-medium rounded-full bg-blue-100 text-blue-800">
|
<span
|
||||||
|
className="px-2 py-1 text-xs font-medium rounded-full"
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'var(--bg-tertiary)',
|
||||||
|
color: 'var(--text-primary)',
|
||||||
|
border: '1px solid var(--border)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
{card.game}
|
{card.game}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Price */}
|
{/* Price */}
|
||||||
<div className="flex-shrink-0 mx-4">
|
<div className="flex-shrink-0 mx-4">
|
||||||
<span className="text-lg font-bold text-green-600">
|
<span
|
||||||
|
className="text-lg font-bold"
|
||||||
|
style={{ color: 'var(--accent-flame)' }}
|
||||||
|
>
|
||||||
${card.market_price}
|
${card.market_price}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -248,11 +277,12 @@ export default function CardItem({
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onToggleFavorite(card);
|
onToggleFavorite(card);
|
||||||
}}
|
}}
|
||||||
className={`p-2 rounded-lg transition-colors ${
|
className="nav-item-hover p-2 rounded-xl transition-colors"
|
||||||
isFavorited
|
style={{
|
||||||
? 'text-red-500 hover:text-red-600'
|
color: isFavorited
|
||||||
: 'text-gray-400 hover:text-red-500'
|
? 'var(--accent-ember)'
|
||||||
}`}
|
: 'var(--text-secondary)',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill={isFavorited ? "currentColor" : "none"} stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5" fill={isFavorited ? "currentColor" : "none"} stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
|
||||||
|
|
@ -264,7 +294,8 @@ export default function CardItem({
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onAddToCollection(card);
|
onAddToCollection(card);
|
||||||
}}
|
}}
|
||||||
className="p-2 text-blue-600 hover:text-blue-700 hover:bg-blue-50 rounded-lg transition-colors"
|
className="nav-item-hover p-2 rounded-xl transition-colors"
|
||||||
|
style={{ color: 'var(--accent-ember)' }}
|
||||||
title={VOCAB.ADD_TO_LIST}
|
title={VOCAB.ADD_TO_LIST}
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
|
@ -277,7 +308,8 @@ export default function CardItem({
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onAddToDeck(card);
|
onAddToDeck(card);
|
||||||
}}
|
}}
|
||||||
className="p-2 text-green-600 hover:text-green-700 hover:bg-green-50 rounded-lg transition-colors"
|
className="nav-item-hover p-2 rounded-xl transition-colors"
|
||||||
|
style={{ color: 'var(--accent-flame)' }}
|
||||||
title="Add to Deck"
|
title="Add to Deck"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue