Comprehensive design sweep across the rest of the app following the shipped Liquid Glass + corner-border-light system (#116). ## Three classes of finding ### 1. Broken Tailwind token classes (HIGH — pages were unstyled) The decks / deck-builder / deck-detail cluster relied on Tailwind classes that don't exist in `tailwind.config.js` (no `bg-bg-*`, `text-text-*`, `border-border`, `bg-accent-ember`, `focus:ring-accent-ember`, `hover:bg-accent-ember-dark`). Those classes produced ZERO CSS — backgrounds were transparent, borders invisible, hover states absent. Rewrote with inline `style={{ ... CSS vars ... }}` + the `<Button>` / `<SearchBar>` primitives + `glass-panel` surfaces: - `pages/decks.js` (full page) - `pages/deck/[id].js` (header, stats sidebar, group-by controls, card list) - `pages/deck-builder.js` (loading spinner) - `components/DeckBuilderView.js` (toolbar + main panel) - `components/DeckBuilderCardBrowser.js` (full rewrite; integrated `<SearchBar>` for the card-picker input) - `components/DeckBuilderDeckList.js` (full rewrite) - `components/DeckBuilderStatsBar.js` - `components/ManaSymbolSettings.js` - `components/ManaSymbols.js` (single `text-text-secondary`) - `pages/admin/card-editor.js` cluster was already clean ### 2. Duplicative / stale page searches Replaced raw `<input>` search controls with the `<SearchBar>` primitive (adds clear button, ember focus ring, system-consistent rounded corners). Kept page-specific filter searches (they filter the visible list — distinct from the global TopSearchBar command palette): - `pages/my-cards.js` - `pages/community/collections.js` - `components/CardsPageView.js` - `components/CollectionPageView.js` - `components/DeckBuilderCardBrowser.js` `pages/my-cards.js` filter wrapper also lifted into a `glass-panel` chip instead of a solid `var(--bg-primary)` band. ### 3. Square corners + stale palette in shared views - `components/CollectionPageView.js`: 10 action buttons (`rounded-lg` + `hover:bg-gray-50`) → `rounded-xl` + `nav-item-hover`; 4 filter selects (`focus:ring-purple-500 rounded-lg`) → `.input-field`; view-mode toggle (`bg-white text-gray-900` — invisible in dark mode) → tokenised; SYSTEM badge gradient (`from-blue-500 to-purple-600`) → ember↔flame; tooltip (`bg-gray-900`) → `glass-panel-strong`; search-results dropdown (`bg-white border-gray-200` — invisible in dark mode) → `glass-panel-strong`; Activity / game-count / TCG-game badges palette-aligned. - `components/CardsPageView.js`: "Load More Cards" button (`bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg`) → `<Button variant="primary" size="lg">`. - `components/CollectionsPageView.js`: matching SYSTEM badge + tooltip cleanup. - `components/ShareModal.js`: user-search dropdown (`border-gray-200 hover:bg-gray-50`) and email-invite card moved onto `glass-panel` + `nav-item-hover`; social-share buttons `rounded-lg hover:bg-gray-50` → `rounded-xl nav-item-hover`. - `components/Layout.js`: profile-menu dropdown row (`hover:bg-gray-50 dark:hover:bg-gray-700`) → `nav-item-hover`. - `components/CardItem.js`: bulk-select checkbox `focus:ring-purple-500` → ember. ### 4. `dark:` modifier classes (broken with `[data-theme]` theming) This app uses `[data-theme="dark"]` CSS selector theming, not Tailwind's `class` strategy, so `dark:bg-green-900/20` etc. produced no CSS in dark mode. Affected alerts on `pages/settings.js` and `pages/profile.js` — replaced with `glass-panel` + semantic border colour (flame for success, #dc2626 for error). `pages/settings.js` sidebar nav also moved off its hardcoded full-ember fill onto the system `nav-item` / `nav-item-active` / `nav-item-hover` pattern for consistency with the global sidebar. ## Verification - `npm run build` — green (Next 16 + Turbopack) - `npm run lint` — 0 errors, 1 unrelated pre-existing warning - `npm run test:run` — 113/113 pass (no test changes needed) Co-authored-by: Cursor <cursoragent@cursor.com>
143 lines
5.3 KiB
JavaScript
143 lines
5.3 KiB
JavaScript
/* eslint-disable @next/next/no-img-element -- External card image URLs; next/image migration is out of scope. */
|
||
import { ManaCost } from './ManaSymbols';
|
||
import { Button } from './ui';
|
||
|
||
/* 2026-06-04 design-sweep pass: same broken Tailwind tokens
|
||
(bg-bg-*, text-text-*, bg-accent-ember, hover:bg-accent-ember-dark)
|
||
that the other deck builder files had — none of those classes are
|
||
defined in tailwind.config.js, so this list rendered with no
|
||
visible card surfaces or hover affordances. Sweep replaces with
|
||
inline CSS variables + Button primitive. Rarity badges reuse the
|
||
neutral palette from DeckBuilderCardBrowser. */
|
||
|
||
const rarityBadgeStyle = (rarity) => {
|
||
switch (rarity) {
|
||
case 'mythic':
|
||
return { backgroundColor: 'var(--accent-ember)', color: '#ffffff' };
|
||
case 'rare':
|
||
return { backgroundColor: 'var(--accent-flame)', color: '#ffffff' };
|
||
case 'uncommon':
|
||
return { backgroundColor: 'var(--bg-tertiary)', color: 'var(--text-primary)' };
|
||
default:
|
||
return { backgroundColor: 'var(--bg-tertiary)', color: 'var(--text-secondary)' };
|
||
}
|
||
};
|
||
|
||
export default function DeckBuilderDeckList({
|
||
deckCards,
|
||
sidebarOpen,
|
||
onOpenSidebar,
|
||
manaSymbolSettings,
|
||
onAddCard,
|
||
onRemoveCard,
|
||
}) {
|
||
return (
|
||
<div className="flex-1 overflow-y-auto space-y-2">
|
||
{deckCards.length === 0 ? (
|
||
<div className="text-center py-12">
|
||
<div className="text-6xl mb-4">🃏</div>
|
||
<h3
|
||
className="text-xl font-semibold mb-2"
|
||
style={{ color: 'var(--text-primary)' }}
|
||
>
|
||
Empty Deck
|
||
</h3>
|
||
<p className="mb-4" style={{ color: 'var(--text-secondary)' }}>
|
||
Start building your deck by searching for cards
|
||
</p>
|
||
{!sidebarOpen && (
|
||
<Button variant="primary" onClick={onOpenSidebar}>
|
||
Open Card Browser
|
||
</Button>
|
||
)}
|
||
</div>
|
||
) : (
|
||
deckCards
|
||
.sort((a, b) => a.name.localeCompare(b.name))
|
||
.map((card) => (
|
||
<div
|
||
key={`${card.card_id}-${card.id}`}
|
||
className="flex items-center justify-between p-4 rounded-xl transition-colors nav-item-hover"
|
||
style={{ backgroundColor: 'var(--bg-primary)' }}
|
||
>
|
||
<div className="flex items-center space-x-4">
|
||
{card.image_url && (
|
||
<img
|
||
src={card.image_url}
|
||
alt={card.name}
|
||
className="w-14 h-20 object-cover rounded shadow-md"
|
||
/>
|
||
)}
|
||
<div>
|
||
<h4
|
||
className="font-semibold text-lg"
|
||
style={{ color: 'var(--text-primary)' }}
|
||
>
|
||
{card.name}
|
||
</h4>
|
||
<p
|
||
className="text-sm"
|
||
style={{ color: 'var(--text-secondary)' }}
|
||
>
|
||
{card.set_name}
|
||
</p>
|
||
<div className="flex items-center space-x-3 mt-1">
|
||
{card.mana_cost && (
|
||
<div
|
||
className="px-2 py-1 rounded"
|
||
style={{ backgroundColor: 'var(--bg-secondary)' }}
|
||
>
|
||
<ManaCost
|
||
cost={card.mana_cost}
|
||
size="sm"
|
||
useSVG={manaSymbolSettings.useSVG}
|
||
/>
|
||
</div>
|
||
)}
|
||
{card.rarity && (
|
||
<span
|
||
className="text-xs px-2 py-1 rounded capitalize"
|
||
style={rarityBadgeStyle(card.rarity)}
|
||
>
|
||
{card.rarity}
|
||
</span>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center space-x-3">
|
||
<span
|
||
className="font-bold text-lg"
|
||
style={{ color: 'var(--text-primary)' }}
|
||
>
|
||
{card.quantity}x
|
||
</span>
|
||
<div className="flex items-center space-x-1">
|
||
<button
|
||
onClick={() => onRemoveCard(card.card_id, 1)}
|
||
className="w-8 h-8 rounded-full transition-colors flex items-center justify-center font-bold text-white"
|
||
style={{ backgroundColor: '#dc2626' }}
|
||
aria-label="Remove one"
|
||
>
|
||
−
|
||
</button>
|
||
<button
|
||
onClick={() => onAddCard(card, 1)}
|
||
className="w-8 h-8 rounded-full transition-transform hover:scale-105 active:scale-95 flex items-center justify-center font-bold text-white"
|
||
style={{
|
||
background:
|
||
'linear-gradient(135deg, var(--accent-ember) 0%, var(--accent-flame) 100%)',
|
||
boxShadow: 'var(--rim-light-inner), var(--ember-rim-subtle)',
|
||
}}
|
||
aria-label="Add one"
|
||
>
|
||
+
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))
|
||
)}
|
||
</div>
|
||
);
|
||
}
|