From 1769d4576ac88df10e8f3a250c8c74057ca070ec Mon Sep 17 00:00:00 2001 From: Randall Stillwell Date: Thu, 4 Jun 2026 12:55:29 -0500 Subject: [PATCH] =?UTF-8?q?refactor(design):=20site-wide=20sweep=20?= =?UTF-8?q?=E2=80=94=20broken=20Tailwind=20tokens,=20rounded=20corners,=20?= =?UTF-8?q?SearchBar=20primitive=20(#117)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 ` - + } + }} + placeholder="Search cards…" + />
@@ -142,16 +146,31 @@ export default function CollectionPageView(props) { {/* System collection indicator */} {collection.isSystemCollection && (
- + 🔒 SYSTEM
- + -
+
{VOCAB.SYSTEM_COLLECTION_SYNC_HINT} -
@@ -163,11 +182,15 @@ export default function CollectionPageView(props) { {/* TCG Tags */}
- {Object.entries(gameStats).map(([game, count]) => + {Object.entries(gameStats).map(([game, count]) => count > 0 ? ( {game} @@ -202,7 +225,7 @@ export default function CollectionPageView(props) {
-
@@ -248,28 +287,25 @@ export default function CollectionPageView(props) { {/* Search and Filters */}
-
- + { setSearchCards(e.target.value); handleSearchCards(e.target.value); }} - className="w-64 px-4 py-2 border rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent" - style={{ borderColor: 'var(--border)', backgroundColor: 'var(--bg-secondary)' }} + onClear={() => { + setSearchCards(''); + handleSearchCards(''); + }} + placeholder="Search cards…" /> - - -
- + - + - + - + onSearchQueryChange(e.target.value)} - - placeholder="Search for cards..." - - className="flex-1 px-3 py-2 border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-ember bg-bg-primary text-text-primary text-sm" - - /> - - - -
- -
- - - - {/* Settings Panel */} - - {showSettings && ( - -
- - - -
- - )} - - - - {/* Quick Filters */} - - {showFilters && ( - -
- - {/* Color Filters */} - -
- -
- - - - {(filters.colors.length > 0 || filters.cmc || filters.rarity) && ( - - - - )} - -
- -
- - {['W', 'U', 'B', 'R', 'G'].map(color => ( - - - - ))} - -
- -
- - - - {/* CMC and Rarity */} - -
- -
- - - - - -
- -
- - - - - -
- -
- -
- - )} - - - - {/* Card List */} - -
- - {viewMode === 'list' ? ( - - /* List View */ - -
- - {searchLoading ? ( - -
- -
- -

Searching...

- -
- - ) : searchResults.length === 0 ? ( - -
- -
🔍
- -

- - {searchQuery ? 'No cards found' : 'No cards available'} - -

- -
- - ) : ( - - searchResults.map((card) => ( - -
onSelectCard(card)} - - > - -
- - {card.image_url && ( - - {card.name} - - )} - -
- -

{card.name}

- -

{card.set_name}

- -
- - {card.mana_cost && ( - - - - )} - - {card.rarity && ( - - - - {card.rarity[0].toUpperCase()} - - - - )} - -
- -
- -
- -
- - )) - - )} - -
- - ) : ( - - /* Thumbnail View */ - -
- - {searchLoading ? ( - -
- -
- -

Searching...

- -
- - ) : searchResults.length === 0 ? ( - -
- -
🔍
- -

- - {searchQuery ? 'No cards found' : 'No cards available'} - -

- -
- - ) : ( - -
- - {searchResults.map((card) => ( - -
onSelectCard(card)} - - > - - {card.image_url ? ( - -
- - {card.name} - - {/* Hover overlay with card name */} - -
- -
- -

{card.name}

- -

{card.set_name}

- -
- -
- - {/* Rarity indicator */} - - {card.rarity && ( - -
- - )} - -
- - ) : ( - -
- -
- -

{card.name}

- -

{card.set_name}

- -
- -
- - )} - -
- - ))} - -
- - )} - -
- - )} - -
- - - - ) : ( - - /* Card Detail View */ - - <> - - {/* Detail Header */} - -
- - - -

{selectedCard.name}

- -
- - - - {/* Card Detail Content */} - -
- -
- - {/* Card Image */} - - {selectedCard.image_url && ( - -
- - {selectedCard.name} - -
- - )} - - - - {/* Card Info */} - -
- -
- -

{selectedCard.name}

- -

{selectedCard.set_name}

- -
- - - - {selectedCard.mana_cost && ( - -
- - - -
- - - -
- -
- - )} - - - - {selectedCard.card_type && ( - -
- - - -
{selectedCard.card_type}
- -
- - )} - - - - {selectedCard.oracle_text && ( - -
- - - -
{selectedCard.oracle_text}
- -
- - )} - - - -
- - {selectedCard.rarity && ( - -
- - - -
- - {selectedCard.rarity} - -
- -
- - )} - - - - {selectedCard.cmc !== undefined && ( - -
- - - -
{selectedCard.cmc}
- -
- - )} - -
- -
- -
- -
- - - +
+ {!selectedCard ? ( + <> + {/* Header */} +
+

+ Card Browser +

+
+ {/* View Mode Toggle */} +
+ + +
+ + + + +
+
+ + {/* Search */} +
+
+
+ onSearchQueryChange(e.target.value)} + onClear={() => onSearchQueryChange('')} + placeholder="Search for cards…" + /> +
+ +
+
+ + {/* Settings Panel */} + {showSettings && ( +
+ +
+ )} + + {/* Quick Filters */} + {showFilters && ( +
+ {/* Color Filters */} +
+
+ + {hasActiveFilters && ( + )} - - - - {/* Floating Action Button */} - - {selectedCard && ( - -
- - - -
- - )} -
+
+ {['W', 'U', 'B', 'R', 'G'].map((color) => ( + + ))} +
+
+ + {/* CMC and Rarity */} +
+
+ + +
+
+ + +
+
+
+ )} + + {/* Card List */} +
+ {viewMode === 'list' ? ( +
+ {searchLoading ? ( +
+
+

+ Searching... +

+
+ ) : searchResults.length === 0 ? ( +
+
🔍
+

+ {searchQuery ? 'No cards found' : 'No cards available'} +

+
+ ) : ( + searchResults.map((card) => ( +
onSelectCard(card)} + > +
+ {card.image_url && ( + {card.name} + )} +
+

+ {card.name} +

+

+ {card.set_name} +

+
+ {card.mana_cost && ( + + )} + {card.rarity && ( + + {card.rarity[0].toUpperCase()} + + )} +
+
+
+
+ )) + )} +
+ ) : ( +
+ {searchLoading ? ( +
+
+

+ Searching... +

+
+ ) : searchResults.length === 0 ? ( +
+
🔍
+

+ {searchQuery ? 'No cards found' : 'No cards available'} +

+
+ ) : ( +
+ {searchResults.map((card) => ( +
onSelectCard(card)} + > + {card.image_url ? ( +
+ {card.name} +
+
+

+ {card.name} +

+

+ {card.set_name} +

+
+
+ {card.rarity && ( +
+ )} +
+ ) : ( +
+
+

+ {card.name} +

+

+ {card.set_name} +

+
+
+ )} +
+ ))} +
+ )} +
+ )} +
+ + ) : ( + /* Card Detail View */ + <> +
+ +

+ {selectedCard.name} +

+
+ +
+
+ {selectedCard.image_url && ( +
+ {selectedCard.name} +
+ )} + +
+
+

+ {selectedCard.name} +

+

+ {selectedCard.set_name} +

+
+ + {selectedCard.mana_cost && ( +
+ +
+ +
+
+ )} + + {selectedCard.card_type && ( +
+ +
+ {selectedCard.card_type} +
+
+ )} + + {selectedCard.oracle_text && ( +
+ +
+ {selectedCard.oracle_text} +
+
+ )} + +
+ {selectedCard.rarity && ( +
+ +
+ {selectedCard.rarity} +
+
+ )} + + {selectedCard.cmc !== undefined && ( +
+ +
+ {selectedCard.cmc} +
+
+ )} +
+
+
+
+ + )} + + {/* Floating Action Button */} + {selectedCard && ( +
+ +
+ )} +
); } diff --git a/components/DeckBuilderDeckList.js b/components/DeckBuilderDeckList.js index 4b948e0..25ef458 100644 --- a/components/DeckBuilderDeckList.js +++ b/components/DeckBuilderDeckList.js @@ -1,5 +1,27 @@ /* 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, @@ -10,76 +32,112 @@ export default function DeckBuilderDeckList({ onRemoveCard, }) { return ( -
- {deckCards.length === 0 ? ( -
-
🃏
-

Empty Deck

-

Start building your deck by searching for cards

- {!sidebarOpen && ( - + )} +
+ ) : ( + deckCards + .sort((a, b) => a.name.localeCompare(b.name)) + .map((card) => ( +
+
+ {card.image_url && ( + {card.name} + )} +
+

+ {card.name} +

+

+ {card.set_name} +

+
+ {card.mana_cost && ( +
- Open Card Browser - + +
+ )} + {card.rarity && ( + + {card.rarity} + )}
- ) : ( - deckCards - .sort((a, b) => a.name.localeCompare(b.name)) - .map((card) => ( -
-
- {card.image_url && ( - {card.name} - )} -
-

{card.name}

-

{card.set_name}

-
- {card.mana_cost && ( -
- -
- )} - {card.rarity && ( - - {card.rarity} - - )} -
-
-
-
- {card.quantity}x -
- - -
-
-
- )) - )} +
+
+ + {card.quantity}x + +
+ + +
+
+
+ )) + )} +
); } diff --git a/components/DeckBuilderStatsBar.js b/components/DeckBuilderStatsBar.js index 0d67b24..7e36524 100644 --- a/components/DeckBuilderStatsBar.js +++ b/components/DeckBuilderStatsBar.js @@ -4,26 +4,41 @@ */ export default function DeckBuilderStatsBar({ stats }) { return ( -
+
-
{stats.totalCards}/100
-
Cards
+
+ {stats.totalCards}/100 +
+
+ Cards +
-
{stats.avgCmc}
-
Avg CMC
+
+ {stats.avgCmc} +
+
+ Avg CMC +
-
+
{Object.keys(stats.colorCounts).length}
-
Colors
+
+ Colors +
-
+
{Object.keys(stats.typeCounts).length}
-
Types
+
+ Types +
); diff --git a/components/DeckBuilderView.js b/components/DeckBuilderView.js index 17604b6..259b6c6 100644 --- a/components/DeckBuilderView.js +++ b/components/DeckBuilderView.js @@ -2,8 +2,16 @@ import Link from 'next/link'; import DeckBuilderCardBrowser from './DeckBuilderCardBrowser'; import DeckBuilderDeckList from './DeckBuilderDeckList'; import DeckBuilderStatsBar from './DeckBuilderStatsBar'; +import { Button } from './ui'; import { computeDeckStats } from '../lib/deck-builder-stats'; +/* 2026-06-04 design-sweep pass: replaced broken Tailwind token + classes (bg-bg-secondary, text-text-primary, bg-accent-ember, etc. + — none of which are defined in tailwind.config.js and produced + zero CSS) with inline style={{ ... CSS vars ... }} + the Button + primitive + `glass-panel` + `rounded-2xl` so this view actually + renders with the Liquid Glass design system. */ + export default function DeckBuilderView({ addCardToDeck, clearFilters, @@ -37,46 +45,46 @@ export default function DeckBuilderView({
- + ← Back to Decks
-

{deck.name}

-

+

+ {deck.name} +

+

{deck.format} • {stats.totalCards}/100 cards

- - - View Deck + + +
-
+
-

+

Deck Cards ({stats.totalCards})

- + {sidebarOpen ? 'Hide' : 'Show'} Browser +
diff --git a/components/Layout.js b/components/Layout.js index 610d072..47df0e8 100644 --- a/components/Layout.js +++ b/components/Layout.js @@ -99,11 +99,7 @@ function UserProfileDropdown({ user, onMobileMenuClose }) { {profileMenuItems.map((item) => (
+
-

Mana Symbol Style

-

+

+ Mana Symbol Style +

+

{useSVG ? 'Using official Scryfall SVG symbols' : 'Using custom circular symbols'}

); -} \ No newline at end of file +} diff --git a/components/ManaSymbols.js b/components/ManaSymbols.js index 6399c23..f76424e 100644 --- a/components/ManaSymbols.js +++ b/components/ManaSymbols.js @@ -212,7 +212,7 @@ export function AdvancedManaCost({ cost, showAnalysis = false, useSVG = false })
{showAnalysis && ( -
+
CMC: {analysis.cmc}
{analysis.colors.length > 0 && (
diff --git a/components/ShareModal.js b/components/ShareModal.js index 2ec23ff..63e934d 100644 --- a/components/ShareModal.js +++ b/components/ShareModal.js @@ -184,21 +184,33 @@ export default function ShareModal({ {/* Search Results */} {searchResults.length > 0 && ( -
+
{searchResults.map((user) => (
handleInvite(user)} - className="flex items-center p-3 hover:bg-gray-50 cursor-pointer" + className="flex items-center p-3 cursor-pointer nav-item-hover" > -
+
{user.email.charAt(0).toUpperCase()}
-
{user.email}
-
Click to invite as viewer
+
+ {user.email} +
+
+ Click to invite as viewer +
))} @@ -207,19 +219,32 @@ export default function ShareModal({ {/* Email invite option */} {searchQuery && isValidEmail(searchQuery) && !searchResults.some(u => u.email === searchQuery) && ( -
+
handleInvite(searchQuery)} - className="flex items-center p-3 hover:bg-gray-50 cursor-pointer" + className="flex items-center p-3 cursor-pointer nav-item-hover" > -
- +
+
-
Invite {searchQuery}
-
Send email invitation as viewer
+
+ Invite {searchQuery} +
+
+ Send email invitation as viewer +
@@ -310,7 +335,8 @@ export default function ShareModal({
)} @@ -204,38 +242,64 @@ export default function DeckDetail() {
{/* Stats Sidebar */}
-
-

Statistics

- +
+

+ Statistics +

+
- Total Cards: - {stats.totalCards} + Total Cards: + + {stats.totalCards} +
- Avg. CMC: - {stats.avgCmc} + Avg. CMC: + + {stats.avgCmc} +
- Format: - {deck.format} + Format: + + {deck.format} +
{/* Color Distribution */} {Object.keys(stats.colorCounts).length > 0 && (
-

Color Distribution

+

+ Color Distribution +

{Object.entries(stats.colorCounts) - .sort(([,a], [,b]) => b - a) + .sort(([, a], [, b]) => b - a) .map(([color, count]) => (
{color} - {color} + + {color} +
- {count} + + {count} +
))}
@@ -245,15 +309,30 @@ export default function DeckDetail() { {/* Type Distribution */} {Object.keys(stats.typeCounts).length > 0 && (
-

Card Types

+

+ Card Types +

{Object.entries(stats.typeCounts) - .sort(([,a], [,b]) => b - a) + .sort(([, a], [, b]) => b - a) .slice(0, 8) .map(([type, count]) => (
- {type} - {count} + + {type} + + + {count} +
))}
@@ -262,22 +341,25 @@ export default function DeckDetail() {
{/* Group By Controls */} -
-

Group Cards By

+
+

+ Group Cards By +

{[ { value: 'type', label: 'Card Type' }, { value: 'cmc', label: 'Mana Cost' }, { value: 'color', label: 'Color' }, - { value: 'rarity', label: 'Rarity' } + { value: 'rarity', label: 'Rarity' }, ].map(option => ( +
{/* Stats */}
-
-
{decks.length}
-
Total Decks
-
-
-
- {decks.filter(d => d.format === 'Commander').length} +
+
+ {decks.length}
-
Commander
+
Total Decks
-
-
- {decks.filter(d => d.is_public).length} +
+
+ {decks.filter((d) => d.format === 'Commander').length}
-
Public
+
Commander
-
-
+
+
+ {decks.filter((d) => d.is_public).length} +
+
Public
+
+
+
{decks.reduce((sum, deck) => sum + (deck.card_count || 0), 0)}
-
Total Cards
+
Total Cards
@@ -234,65 +245,83 @@ export default function Decks() { {decks.length === 0 ? (
🃏
-

No decks yet

-

Create your first deck to get started

- +
) : (
{decks.map((deck) => ( -
+
{getFormatIcon(deck.format)} - + {deck.format}
-

{deck.name}

- +

+ {deck.name} +

+ {deck.description && ( -

{deck.description}

+

+ {deck.description} +

)} -
+
{deck.card_count || 0} cards - {deck.is_public && Public} + {deck.is_public && ( + Public + )}
- - Edit Deck + + - - View + +
@@ -326,7 +355,7 @@ export default function Decks() { id="create-deck-format" value={newDeck.format} onChange={(e) => setNewDeck({ ...newDeck, format: e.target.value })} - className="w-full px-3 py-2 border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-ember bg-bg-secondary text-text-primary" + className={inputFieldClass} > @@ -346,7 +375,7 @@ export default function Decks() { id="create-deck-description" value={newDeck.description} onChange={(e) => setNewDeck({ ...newDeck, description: e.target.value })} - className="w-full px-3 py-2 border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-ember bg-bg-secondary text-text-primary" + className={inputFieldClass} rows="3" placeholder="Describe your deck strategy..." /> @@ -391,9 +420,7 @@ export default function Decks() { label="Deck Name *" required value={editingDeck.name} - onChange={(e) => - setEditingDeck({ ...editingDeck, name: e.target.value }) - } + onChange={(e) => setEditingDeck({ ...editingDeck, name: e.target.value })} />