Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
/* ============================================================
|
|
|
|
|
|
mtgc — design system
|
|
|
|
|
|
Terracotta accent (#C8553D) on a layered, Discord-inspired dark UI.
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
:root {
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
/* Layered surfaces (darkest → lightest). A warm charcoal ramp (low-saturation
|
|
|
|
|
|
~28° hue) rather than cool blue-grey — reads as "collection in warm light"
|
|
|
|
|
|
and harmonizes with the terracotta accent. Elevation model:
|
|
|
|
|
|
rail/app (chrome) → content (page) → panel (surface) → input (inset). */
|
|
|
|
|
|
--bg-rail: #191714;
|
|
|
|
|
|
--bg-app: #1d1a16;
|
|
|
|
|
|
--bg-topbar: #221e19;
|
|
|
|
|
|
--bg-content: #27221d;
|
|
|
|
|
|
--bg-panel: #322c25;
|
|
|
|
|
|
--bg-input: #3b342d;
|
|
|
|
|
|
--bg-hover: #443c34;
|
|
|
|
|
|
--bg-elev: #4c433a;
|
|
|
|
|
|
|
|
|
|
|
|
--border: #3d372f;
|
|
|
|
|
|
--border-soft: rgba(255, 248, 240, 0.06);
|
|
|
|
|
|
/* Tactile 1px top inner-highlight for raised surfaces (see .panel). */
|
|
|
|
|
|
--panel-highlight: inset 0 1px 0 rgba(255, 246, 236, 0.045);
|
|
|
|
|
|
|
|
|
|
|
|
--text: #e3ddd3;
|
|
|
|
|
|
--text-strong: #f8f4ec;
|
|
|
|
|
|
--muted: #a69d90;
|
|
|
|
|
|
--faint: #7d7568;
|
|
|
|
|
|
|
|
|
|
|
|
/* Terracotta accent (hue unchanged — the app's signature) */
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
--accent: #c8553d;
|
|
|
|
|
|
--accent-hover: #d86e56;
|
|
|
|
|
|
--accent-active: #a8412d;
|
|
|
|
|
|
--accent-subtle: rgba(200, 85, 61, 0.15);
|
|
|
|
|
|
--accent-glow: rgba(200, 85, 61, 0.35);
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
/* Quiet accent for decorative/label use, so full terracotta stays reserved
|
|
|
|
|
|
for primary actions + active state (accent hierarchy). */
|
|
|
|
|
|
--accent-quiet: #c69a86;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
|
|
|
|
|
|
/* Complementary / status */
|
|
|
|
|
|
--info: #46a6bd; /* teal complement */
|
|
|
|
|
|
--gold: #e0b24a;
|
|
|
|
|
|
--success: #4bb073;
|
|
|
|
|
|
--warn: #e0a34a;
|
|
|
|
|
|
--danger: #d8503f;
|
|
|
|
|
|
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
/* WUBRG mana colors, shared by pips + the library color-distribution bar */
|
|
|
|
|
|
--mtg-w: #f4efdd;
|
|
|
|
|
|
--mtg-u: #3a8fd0;
|
|
|
|
|
|
--mtg-b: #38343d;
|
|
|
|
|
|
--mtg-r: var(--accent);
|
|
|
|
|
|
--mtg-g: #479b63;
|
|
|
|
|
|
--mtg-c: #9aa0ab;
|
|
|
|
|
|
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
/* Spacing scale — every gap/margin/padding resolves to one of these. */
|
|
|
|
|
|
--space-1: 4px;
|
|
|
|
|
|
--space-2: 8px;
|
|
|
|
|
|
--space-3: 12px;
|
|
|
|
|
|
--space-4: 16px;
|
|
|
|
|
|
--space-5: 20px;
|
|
|
|
|
|
--space-6: 24px;
|
|
|
|
|
|
--space-7: 32px;
|
|
|
|
|
|
--space-8: 48px;
|
|
|
|
|
|
|
|
|
|
|
|
/* Radius scale */
|
|
|
|
|
|
--radius-sm: 6px;
|
|
|
|
|
|
--radius: 9px;
|
|
|
|
|
|
--radius-lg: 14px;
|
|
|
|
|
|
--radius-card: 12px; /* card-art corners (matches Scryfall image framing) */
|
|
|
|
|
|
--radius-pill: 999px;
|
|
|
|
|
|
|
|
|
|
|
|
/* Type scale */
|
|
|
|
|
|
--fs-micro: 10px; /* dense data labels (under stat numbers, chart axes) */
|
|
|
|
|
|
--fs-caption: 12px;
|
|
|
|
|
|
--fs-body: 14px;
|
|
|
|
|
|
--fs-label: 11px; /* canonical uppercase micro-label, see .section-label */
|
|
|
|
|
|
--fs-title: 16px;
|
|
|
|
|
|
--fs-display: 21px;
|
|
|
|
|
|
--tracking-label: 0.06em;
|
|
|
|
|
|
|
|
|
|
|
|
/* Motion */
|
|
|
|
|
|
--dur-fast: 0.12s;
|
|
|
|
|
|
--dur: 0.2s;
|
|
|
|
|
|
|
|
|
|
|
|
/* Interactive control height — bumped on coarse pointers (see bottom). */
|
|
|
|
|
|
--control-h: 34px;
|
2026-08-02 16:10:25 -05:00
|
|
|
|
/* Height of .bottom-nav's own content — excludes the safe-area inset added
|
|
|
|
|
|
on top of it, so consumers computing clearance must add that separately. */
|
|
|
|
|
|
--bottom-nav-h: 58px;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
|
|
|
|
|
|
--shadow-md: 0 6px 20px rgba(0, 0, 0, 0.4);
|
|
|
|
|
|
--shadow-lg: 0 14px 44px rgba(0, 0, 0, 0.55);
|
|
|
|
|
|
|
|
|
|
|
|
color-scheme: dark;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
* {
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
html,
|
|
|
|
|
|
body,
|
|
|
|
|
|
#root {
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
body {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
font-family: "Inter", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif;
|
|
|
|
|
|
background: var(--bg-app);
|
|
|
|
|
|
color: var(--text);
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 15px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
-webkit-font-smoothing: antialiased;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
::selection {
|
|
|
|
|
|
background: var(--accent-subtle);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Thin, unobtrusive scrollbars */
|
|
|
|
|
|
*::-webkit-scrollbar {
|
|
|
|
|
|
width: 11px;
|
|
|
|
|
|
height: 11px;
|
|
|
|
|
|
}
|
|
|
|
|
|
*::-webkit-scrollbar-thumb {
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.35);
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
border: 3px solid transparent;
|
|
|
|
|
|
background-clip: padding-box;
|
|
|
|
|
|
}
|
|
|
|
|
|
*::-webkit-scrollbar-thumb:hover {
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.5);
|
|
|
|
|
|
background-clip: padding-box;
|
|
|
|
|
|
}
|
|
|
|
|
|
*::-webkit-scrollbar-track {
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* -------- Controls -------- */
|
|
|
|
|
|
|
|
|
|
|
|
button {
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 6px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
background: var(--bg-input);
|
|
|
|
|
|
color: var(--text);
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
padding: 7px 13px;
|
|
|
|
|
|
cursor: pointer;
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 14px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
font-family: inherit;
|
|
|
|
|
|
transition: background 0.12s, border-color 0.12s, transform 0.05s, filter 0.12s;
|
|
|
|
|
|
}
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
button svg {
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
button:hover {
|
|
|
|
|
|
background: var(--bg-hover);
|
|
|
|
|
|
border-color: var(--bg-elev);
|
|
|
|
|
|
}
|
|
|
|
|
|
button:active {
|
|
|
|
|
|
transform: translateY(1px);
|
|
|
|
|
|
}
|
|
|
|
|
|
button:disabled {
|
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
|
cursor: default;
|
|
|
|
|
|
transform: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
button.primary {
|
|
|
|
|
|
background: linear-gradient(180deg, var(--accent-hover), var(--accent));
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
box-shadow: var(--shadow-sm);
|
|
|
|
|
|
}
|
|
|
|
|
|
button.primary:hover {
|
|
|
|
|
|
filter: brightness(1.07);
|
|
|
|
|
|
background: linear-gradient(180deg, var(--accent-hover), var(--accent));
|
|
|
|
|
|
}
|
|
|
|
|
|
button.primary:active {
|
|
|
|
|
|
background: var(--accent-active);
|
|
|
|
|
|
}
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
button.danger {
|
|
|
|
|
|
background: var(--danger);
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
box-shadow: var(--shadow-sm);
|
|
|
|
|
|
}
|
|
|
|
|
|
button.danger:hover {
|
|
|
|
|
|
filter: brightness(1.1);
|
|
|
|
|
|
}
|
|
|
|
|
|
button.danger:active {
|
|
|
|
|
|
filter: brightness(0.92);
|
|
|
|
|
|
}
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
button.big {
|
|
|
|
|
|
padding: 11px 22px;
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 16px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
input,
|
|
|
|
|
|
select {
|
|
|
|
|
|
background: var(--bg-input);
|
|
|
|
|
|
color: var(--text);
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
padding: 8px 11px;
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 14px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
font-family: inherit;
|
|
|
|
|
|
transition: border-color 0.12s, box-shadow 0.12s;
|
|
|
|
|
|
}
|
|
|
|
|
|
input::placeholder {
|
|
|
|
|
|
color: var(--faint);
|
|
|
|
|
|
}
|
|
|
|
|
|
input:focus,
|
|
|
|
|
|
select:focus {
|
|
|
|
|
|
outline: none;
|
|
|
|
|
|
border-color: var(--accent);
|
|
|
|
|
|
box-shadow: 0 0 0 3px var(--accent-subtle);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
/* Native <select> chrome (height, arrow, popup panel) ignores color/border
|
|
|
|
|
|
overrides unless its OS-drawn appearance is dropped first. */
|
|
|
|
|
|
select {
|
|
|
|
|
|
appearance: none;
|
|
|
|
|
|
-webkit-appearance: none;
|
|
|
|
|
|
-moz-appearance: none;
|
|
|
|
|
|
padding-right: 30px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239a9ea8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
|
|
|
|
|
|
background-repeat: no-repeat;
|
|
|
|
|
|
background-position: right 9px center;
|
|
|
|
|
|
background-size: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
select:hover {
|
|
|
|
|
|
border-color: var(--bg-elev);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* The dropdown's own option list stays OS-native (no cross-browser hook to
|
|
|
|
|
|
restyle it), but at least match its swatch to the app's dark theme. */
|
|
|
|
|
|
select option {
|
|
|
|
|
|
background: var(--bg-panel);
|
|
|
|
|
|
color: var(--text);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-08 08:20:09 -05:00
|
|
|
|
input[type="checkbox"] {
|
|
|
|
|
|
width: 15px;
|
|
|
|
|
|
height: 15px;
|
|
|
|
|
|
accent-color: var(--accent);
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
/* ============================================================
|
|
|
|
|
|
Primitives: shared surface / label / page-header building blocks.
|
|
|
|
|
|
These replace treatments that were previously hand-copied (with drift)
|
|
|
|
|
|
across the views — edit here to change all of them at once.
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
/* Canonical raised surface. Replaces the ~6 hand-copied panel treatments
|
|
|
|
|
|
(deck-meta, commander-hero, deck-stats, filters, legality-tile base,
|
|
|
|
|
|
preview-info). The top inner-highlight gives a subtle tactile edge. */
|
|
|
|
|
|
.panel {
|
|
|
|
|
|
background: var(--bg-panel);
|
|
|
|
|
|
border: 1px solid var(--border-soft);
|
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
|
padding: var(--space-4);
|
|
|
|
|
|
box-shadow: var(--panel-highlight);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Recessed variant for wells nested inside a panel. */
|
|
|
|
|
|
.panel-inset {
|
|
|
|
|
|
background: var(--bg-app);
|
|
|
|
|
|
border: 1px solid var(--border-soft);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
padding: var(--space-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Canonical uppercase section marker (panel/section headers). Uppercase +
|
|
|
|
|
|
tracking reads as structural; `--muted` (not faint) keeps it legible. */
|
|
|
|
|
|
.section-label {
|
|
|
|
|
|
font-size: var(--fs-label);
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
letter-spacing: var(--tracking-label);
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Standard page header: title on the left, control cluster on the right,
|
|
|
|
|
|
wrapping onto two lines on a narrow viewport. Replaces the ad-hoc
|
|
|
|
|
|
`<div class="row" style="justify-content:space-between">` each view used. */
|
|
|
|
|
|
.page-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: var(--space-3);
|
|
|
|
|
|
margin-bottom: var(--space-4);
|
|
|
|
|
|
}
|
|
|
|
|
|
.page-header-title {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-3);
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* The right-hand control cluster (group-by + sort + view toggle + page
|
|
|
|
|
|
actions). Also usable standalone. */
|
|
|
|
|
|
.page-header-actions,
|
|
|
|
|
|
.toolbar {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: var(--space-4);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
/* ============================================================
|
|
|
|
|
|
App shell: nav rail + main
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
.app {
|
|
|
|
|
|
display: flex;
|
Add Android support via Tauri
The Rust core was already portable (caller-provided db paths, rustls rather
than openssl) and the shell already had the mobile entry point, so this is
mostly toolchain, provisioning, and platform-gating.
Toolchain:
- scripts/android-setup.sh installs what Android Studio doesn't: SDK
cmdline-tools, a pinned stable NDK, and the Rust Android targets. The NDK is
pinned rather than "latest" because sdkmanager's newest is often a beta
(30.0.15729638 is r30-beta2) -- not something to compile SQLite with. Prefers
Android Studio's bundled JDK 21, since Gradle rejects newer JDKs.
- Track src-tauri/gen/android (excluding build output): AndroidManifest.xml is
hand-edited and Android permissions have no tauri.conf.json equivalent.
- vite: serve on TAURI_DEV_HOST so a device/emulator can reach the dev server.
Card data: downloading streams ~740MB to build a ~73MB db, which is rough on a
phone. Add import_reference_db to adopt a desktop-built file instead, validated
by db::inspect_reference_db first so picking the wrong file is a clean error
rather than a broken install. Installs via copy-then-rename so an interrupted
copy can't leave a partial db.
Ingest hardening (helps desktop too):
- Give ureq connect/read timeouts. Cancellation is only polled per parsed
record, so a stalled socket previously hung forever with no way to cancel.
- Drop the remove_file before rename; POSIX rename already replaces atomically,
and the delete-first window could leave no reference.db at all if the process
was killed -- much likelier on Android.
UI: hide Open/New Library on mobile (scoped storage has no arbitrary-path
equivalent, and SQLite can't open a content:// URI), reported via a cfg!(mobile)
flag on Status rather than user-agent sniffing. Use 100dvh so the soft keyboard
doesn't break the shell.
Verified: 70/70 core tests, desktop build clean, and the aarch64 APK builds
(bundled SQLite C and ring both cross-compile under NDK 28). Not yet run on a
device.
2026-08-02 13:16:34 -05:00
|
|
|
|
/* `dvh` tracks the *dynamic* viewport, so an on-screen keyboard (or a mobile
|
|
|
|
|
|
browser's collapsing chrome) shrinks the shell instead of pushing content
|
|
|
|
|
|
out of an over-tall, unscrollable `100vh` box. Falls back for older
|
|
|
|
|
|
engines that don't know dvh. */
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
height: 100vh;
|
Add Android support via Tauri
The Rust core was already portable (caller-provided db paths, rustls rather
than openssl) and the shell already had the mobile entry point, so this is
mostly toolchain, provisioning, and platform-gating.
Toolchain:
- scripts/android-setup.sh installs what Android Studio doesn't: SDK
cmdline-tools, a pinned stable NDK, and the Rust Android targets. The NDK is
pinned rather than "latest" because sdkmanager's newest is often a beta
(30.0.15729638 is r30-beta2) -- not something to compile SQLite with. Prefers
Android Studio's bundled JDK 21, since Gradle rejects newer JDKs.
- Track src-tauri/gen/android (excluding build output): AndroidManifest.xml is
hand-edited and Android permissions have no tauri.conf.json equivalent.
- vite: serve on TAURI_DEV_HOST so a device/emulator can reach the dev server.
Card data: downloading streams ~740MB to build a ~73MB db, which is rough on a
phone. Add import_reference_db to adopt a desktop-built file instead, validated
by db::inspect_reference_db first so picking the wrong file is a clean error
rather than a broken install. Installs via copy-then-rename so an interrupted
copy can't leave a partial db.
Ingest hardening (helps desktop too):
- Give ureq connect/read timeouts. Cancellation is only polled per parsed
record, so a stalled socket previously hung forever with no way to cancel.
- Drop the remove_file before rename; POSIX rename already replaces atomically,
and the delete-first window could leave no reference.db at all if the process
was killed -- much likelier on Android.
UI: hide Open/New Library on mobile (scoped storage has no arbitrary-path
equivalent, and SQLite can't open a content:// URI), reported via a cfg!(mobile)
flag on Status rather than user-agent sniffing. Use 100dvh so the soft keyboard
doesn't break the shell.
Verified: 70/70 core tests, desktop build clean, and the aarch64 APK builds
(bundled SQLite C and ring both cross-compile under NDK 28). Not yet run on a
device.
2026-08-02 13:16:34 -05:00
|
|
|
|
height: 100dvh;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nav-rail {
|
|
|
|
|
|
width: 220px;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
background: var(--bg-rail);
|
|
|
|
|
|
border-right: 1px solid var(--border-soft);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
padding: 14px 10px 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.brand {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 11px;
|
|
|
|
|
|
padding: 4px 8px 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.brand-mark {
|
|
|
|
|
|
width: 34px;
|
|
|
|
|
|
height: 34px;
|
|
|
|
|
|
border-radius: 10px;
|
Rebrand to mtgc: pirate-ship logo, lowercase app name, mtgc binary
- New logomark: an original pirate-ship silhouette (hull, twin billowing
sails, swallow-tail pennant) in the terracotta brand gradient on a dark
square — a nod to the MTG card "Pirate Ship", not a reproduction of its
art. Source at src-tauri/icons/logo-source.svg; regenerate the full
platform icon set (desktop/Windows Store/iOS/Android) with
`cargo tauri icon src-tauri/icons/logo-source.svg`.
- Apply it in-app via a new inline ShipLogo component (App.tsx), replacing
the old "M" sidebar mark and "✦" empty-state mark with dark rounded
squares containing the ship glyph.
- Rename the app to lowercase "mtgc" everywhere: window title and
productName (tauri.conf.json), <title> (index.html), sidebar brand text,
empty-state heading.
- Rename the Tauri package/binary mtgc-app -> mtgc (and its lib crate
mtgc_app_lib -> mtgc_lib), so `cargo tauri build` now produces
target/release/mtgc; updated main.rs and every README reference.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:53:36 -05:00
|
|
|
|
background: linear-gradient(150deg, var(--bg-elev), var(--bg-rail));
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
box-shadow: var(--shadow-sm);
|
|
|
|
|
|
}
|
|
|
|
|
|
.brand-text {
|
|
|
|
|
|
font-weight: 700;
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 16px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
letter-spacing: 0.2px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.nav-items {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 3px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.nav-item {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
text-align: left;
|
|
|
|
|
|
padding: 9px 12px;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
color: var(--muted);
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 15px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
font-weight: 550;
|
|
|
|
|
|
}
|
|
|
|
|
|
.nav-item:hover {
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.04);
|
|
|
|
|
|
color: var(--text);
|
|
|
|
|
|
}
|
|
|
|
|
|
.nav-item.active {
|
|
|
|
|
|
background: var(--accent-subtle);
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
}
|
|
|
|
|
|
.nav-item.active::before {
|
|
|
|
|
|
content: "";
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: -10px;
|
|
|
|
|
|
top: 9px;
|
|
|
|
|
|
bottom: 9px;
|
|
|
|
|
|
width: 4px;
|
|
|
|
|
|
border-radius: 0 4px 4px 0;
|
|
|
|
|
|
background: var(--accent);
|
|
|
|
|
|
}
|
|
|
|
|
|
.nav-icon {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
color: inherit;
|
|
|
|
|
|
}
|
2026-08-02 16:10:25 -05:00
|
|
|
|
|
|
|
|
|
|
/* Android-style bottom tab bar — replaces .nav-rail on narrow viewports (see
|
|
|
|
|
|
the ≤720px block below), rather than the rail collapsing to an icon strip.
|
|
|
|
|
|
Hidden by default; enabled only in that breakpoint. Fixed to the viewport
|
|
|
|
|
|
(not `.app`) since `.app` uses `overflow: hidden` and z-indexes elsewhere
|
|
|
|
|
|
(dialogs 400+, the click-preview modal 450) are the reason this sits at
|
|
|
|
|
|
200 — above ordinary content, below anything that should cover the whole
|
|
|
|
|
|
screen. */
|
|
|
|
|
|
.bottom-nav {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
z-index: 200;
|
|
|
|
|
|
height: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom));
|
|
|
|
|
|
padding-bottom: env(safe-area-inset-bottom);
|
|
|
|
|
|
background: var(--bg-rail);
|
|
|
|
|
|
border-top: 1px solid var(--border-soft);
|
|
|
|
|
|
}
|
|
|
|
|
|
.bottom-nav-item {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
gap: 3px;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
padding: 0 2px;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: 0;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
.bottom-nav-item.active {
|
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
|
}
|
|
|
|
|
|
.bottom-nav-item.active::before {
|
|
|
|
|
|
content: "";
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
left: 50%;
|
|
|
|
|
|
transform: translateX(-50%);
|
|
|
|
|
|
width: 28px;
|
|
|
|
|
|
height: 3px;
|
|
|
|
|
|
border-radius: 0 0 3px 3px;
|
|
|
|
|
|
background: var(--accent);
|
|
|
|
|
|
}
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
.nav-foot {
|
|
|
|
|
|
margin-top: auto;
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
padding: 14px 10px 10px;
|
|
|
|
|
|
border-top: 1px solid var(--border-soft);
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
.nav-foot::before {
|
|
|
|
|
|
/* A faint accent hairline atop the divider, echoing the active-tab bar. */
|
|
|
|
|
|
content: "";
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: -1px;
|
|
|
|
|
|
left: 10px;
|
|
|
|
|
|
right: 10px;
|
|
|
|
|
|
height: 1px;
|
|
|
|
|
|
background: linear-gradient(90deg, transparent, var(--accent-glow), transparent);
|
|
|
|
|
|
}
|
|
|
|
|
|
.nav-foot-simple {
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 13px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
.status-dot {
|
|
|
|
|
|
width: 8px;
|
|
|
|
|
|
height: 8px;
|
|
|
|
|
|
border-radius: 50%;
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
flex-shrink: 0;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
}
|
|
|
|
|
|
.status-dot.on {
|
|
|
|
|
|
background: var(--success);
|
|
|
|
|
|
box-shadow: 0 0 6px var(--success);
|
|
|
|
|
|
}
|
|
|
|
|
|
.status-dot.off {
|
|
|
|
|
|
background: var(--faint);
|
|
|
|
|
|
}
|
Replace folder sync with an HTTP sync server, keeping the app offline-first
Folder sync left too much to the user's sync tool: platform file access
(the Android SAF plugin), choosing the document, merging conflict copies —
and its merge base could fall out of step with the document (absorbing
conflict copies, "forget", picking a new Android folder), reading every
difference as a deletion.
The new mtgc-server crate stores one versioned library per user and accepts
a write only when it names the version it replaces. It never merges: devices
still do that with merge3, through a new sync cycle over a `Remote` trait in
mtgc-core. The base advances only on knowledge — to what was read, or to a
write the server confirmed — and is stored with the server and account it
belongs to, so a different account starts from an empty ancestor instead of
deleting. The working copy is only rewritten under the app-state lock, and
sync commands now run off the main thread.
Sign-in happens in the browser on the server's pages, via the owner's OIDC
provider and/or local username/password accounts (Argon2id, managed with
`mtgc-server user ...`), and returns a per-device revocable token to the app
by PKCE-protected code: a loopback port on desktop, the mtgc:// scheme on
Android.
The Info tab gains sign-in, the first-sync combine/replace choice, the
account's devices, and JSON export/import; the top bar shows sync state.
Includes a Dockerfile, Compose/Caddy example, and scripts to build and push
${DOCKER_REGISTRY}/mtgc/sync-server.
2026-10-08 22:32:08 -05:00
|
|
|
|
.status-dot.warn {
|
|
|
|
|
|
background: var(--warn);
|
|
|
|
|
|
}
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
/* Sidebar "your library" summary panel. */
|
|
|
|
|
|
.lib-stats-head {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.lib-stats-title {
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
font-size: var(--fs-label);
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
text-transform: uppercase;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
letter-spacing: var(--tracking-label);
|
|
|
|
|
|
color: var(--muted);
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
}
|
|
|
|
|
|
.lib-stats-loading {
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 12px;
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
color: var(--faint);
|
|
|
|
|
|
padding-bottom: 10px;
|
|
|
|
|
|
}
|
2026-07-08 23:54:47 -05:00
|
|
|
|
/* Generic numeric-stat tile row + segmented color bar — used by both the
|
|
|
|
|
|
sidebar "Your library" panel and the per-deck analysis panel. */
|
|
|
|
|
|
.stat-tiles-row {
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
|
}
|
2026-07-08 23:54:47 -05:00
|
|
|
|
.stat-tile {
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 2px;
|
|
|
|
|
|
padding: 8px 2px;
|
|
|
|
|
|
background: var(--bg-panel);
|
|
|
|
|
|
border: 1px solid var(--border-soft);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
transition: border-color 0.15s, transform 0.15s;
|
|
|
|
|
|
}
|
2026-07-08 23:54:47 -05:00
|
|
|
|
.stat-tile:hover {
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
border-color: var(--accent-glow);
|
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
|
}
|
2026-07-08 23:54:47 -05:00
|
|
|
|
.stat-tile-value {
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 17px;
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
font-weight: 800;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
text-shadow: 0 0 14px var(--accent-glow);
|
|
|
|
|
|
}
|
2026-07-08 23:54:47 -05:00
|
|
|
|
.stat-tile-label {
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
font-size: var(--fs-micro);
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
letter-spacing: 0.05em;
|
|
|
|
|
|
color: var(--faint);
|
|
|
|
|
|
}
|
2026-07-08 23:54:47 -05:00
|
|
|
|
.color-bar {
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
display: flex;
|
|
|
|
|
|
height: 8px;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.35);
|
|
|
|
|
|
margin-bottom: 9px;
|
|
|
|
|
|
}
|
2026-07-08 23:54:47 -05:00
|
|
|
|
.color-bar-seg {
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
min-width: 3px;
|
|
|
|
|
|
transition: flex-grow 0.4s ease;
|
|
|
|
|
|
}
|
2026-07-08 23:54:47 -05:00
|
|
|
|
.color-bar-seg.W { background: var(--mtg-w); }
|
|
|
|
|
|
.color-bar-seg.U { background: var(--mtg-u); }
|
|
|
|
|
|
.color-bar-seg.B { background: var(--mtg-b); }
|
|
|
|
|
|
.color-bar-seg.R { background: var(--mtg-r); }
|
|
|
|
|
|
.color-bar-seg.G { background: var(--mtg-g); }
|
|
|
|
|
|
.color-bar-seg.C { background: var(--mtg-c); }
|
|
|
|
|
|
|
|
|
|
|
|
/* Mana-value curve histogram (per-deck analysis panel). */
|
|
|
|
|
|
.mana-curve {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: flex-end;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-height: 84px;
|
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.mana-curve-col {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
gap: 3px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.mana-curve-count {
|
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
|
color: var(--faint);
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
}
|
|
|
|
|
|
.mana-curve-bar {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
min-height: 2px;
|
|
|
|
|
|
border-radius: 3px 3px 0 0;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
transition: height 0.25s ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
.mana-curve-seg {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
min-height: 1px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.mana-curve-seg.W { background: var(--mtg-w); }
|
|
|
|
|
|
.mana-curve-seg.U { background: var(--mtg-u); }
|
|
|
|
|
|
.mana-curve-seg.B { background: var(--mtg-b); }
|
|
|
|
|
|
.mana-curve-seg.R { background: var(--mtg-r); }
|
|
|
|
|
|
.mana-curve-seg.G { background: var(--mtg-g); }
|
|
|
|
|
|
.mana-curve-seg.C { background: var(--mtg-c); }
|
|
|
|
|
|
.mana-curve-label {
|
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
|
color: var(--faint);
|
|
|
|
|
|
}
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
.lib-stats-sub {
|
2026-07-07 21:35:09 -05:00
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 2px;
|
|
|
|
|
|
font-size: 11px;
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
color: var(--faint);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
.main {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
background: var(--bg-content);
|
|
|
|
|
|
}
|
|
|
|
|
|
.topbar {
|
2026-08-02 16:10:25 -05:00
|
|
|
|
/* min-height (not height): on Android the window draws edge-to-edge behind
|
|
|
|
|
|
the status bar, so the safe-area inset has to ADD to the bar's height
|
|
|
|
|
|
rather than eat into it — a fixed height would squash the buttons under
|
|
|
|
|
|
the status bar instead of below it. Inert (0) anywhere without a system
|
|
|
|
|
|
bar to avoid, e.g. desktop. */
|
|
|
|
|
|
min-height: 52px;
|
|
|
|
|
|
padding-top: env(safe-area-inset-top);
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 10px;
|
2026-08-02 16:10:25 -05:00
|
|
|
|
padding-left: 18px;
|
|
|
|
|
|
padding-right: 18px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
background: var(--bg-topbar);
|
|
|
|
|
|
border-bottom: 1px solid var(--border-soft);
|
|
|
|
|
|
}
|
|
|
|
|
|
.path {
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
flex: 1 1 auto;
|
|
|
|
|
|
min-width: 0;
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 13px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
|
|
|
|
color: var(--faint);
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.25);
|
|
|
|
|
|
padding: 4px 10px;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.spacer {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
}
|
2026-07-07 20:22:31 -05:00
|
|
|
|
|
|
|
|
|
|
/* Ingest (card-data download/index) progress: one multi-segment bar spanning
|
|
|
|
|
|
all stages, below the topbar. */
|
|
|
|
|
|
.ingest-bar {
|
|
|
|
|
|
padding: 8px 18px 10px;
|
|
|
|
|
|
background: var(--bg-elev);
|
|
|
|
|
|
border-bottom: 1px solid var(--border-soft);
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 13px;
|
2026-07-07 20:22:31 -05:00
|
|
|
|
}
|
Printing tracking for owned copies and deck art, and an outdated-card-data notice
Owned copies can now be pinned to a printing + finish (nonfoil / foil /
etched), activating the long-dormant library_card_copies table as groups
of identical copies. library_cards.quantity_owned stays authoritative:
groups are a subset of it, and the rest are "unspecified". Tracking more
copies than are unspecified grows ownership to fit; lowering ownership
trims the most recently added groups first. Migration 0006 adds the
group quantity, a unique (card, printing, finish) index, and
deck_cards.preferred_scryfall_id.
A deck slot can show a chosen printing's art, both faces from that
printing, falling back to the default when the id no longer resolves.
It survives quantity/board changes and is kept by duplicate_deck.
UI: the click preview's info tile gets a Printings section when opened
from the Library or a deck (copy groups with steppers, "Add a
printing…", and "Shown in this deck" with Change…/Default), backed by a
printing picker grid. The picker takes Escape in the capture phase so
it closes without closing the preview under it.
Sync: CardEntry.printings (keyed "<scryfall_id>/<finish>") and
DeckCardEntry.printing. Cards now merge field by field with groups per
key, so a quantity edit and a printing added on another device both
survive; merged groups are clamped deterministically. A document is
written as version 2 only once it holds printing data, so older builds
and servers refuse it instead of dropping the data and syncing the loss.
Decklists: "(SET) NUM", optionally "*F*"/"*E*", round-trips through deck
and library export/import. Library export writes a line per group plus
one for unspecified copies.
reference.db printings gain lang and released_at for picker labels and
ordering. When the installed card data lacks any column this build's
reference schema has (found by diffing against the schema applied
in-memory, so no list to maintain), a notice under the top bar offers
Update card data (and Import card data… on phones); "Not now" is
remembered until something more goes missing.
2026-10-16 21:06:17 -05:00
|
|
|
|
/* Card data from an older version: same strip as the ingest bar, tinted as
|
|
|
|
|
|
a warning. Wraps its buttons under the message on narrow screens. */
|
|
|
|
|
|
.outdated-bar {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px 16px;
|
|
|
|
|
|
padding: 8px 18px;
|
|
|
|
|
|
background: rgba(224, 163, 74, 0.12);
|
|
|
|
|
|
border-bottom: 1px solid rgba(224, 163, 74, 0.35);
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.outdated-bar-message {
|
|
|
|
|
|
flex: 1 1 260px;
|
|
|
|
|
|
color: var(--text);
|
|
|
|
|
|
}
|
|
|
|
|
|
.outdated-bar-actions {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
}
|
2026-07-07 20:22:31 -05:00
|
|
|
|
.ingest-bar-head {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
margin-bottom: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.ingest-bar-message {
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
}
|
|
|
|
|
|
.ingest-bar-cancel {
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
padding: 3px 10px;
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 12px;
|
2026-07-07 20:22:31 -05:00
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
.ingest-bar-cancel:hover {
|
|
|
|
|
|
color: var(--danger);
|
|
|
|
|
|
border-color: var(--danger);
|
|
|
|
|
|
}
|
|
|
|
|
|
.ingest-segments {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.ingest-segment {
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.ingest-segment-track {
|
|
|
|
|
|
height: 6px;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.3);
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
.ingest-segment-fill {
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
background: var(--border);
|
|
|
|
|
|
transition: width 0.2s ease;
|
|
|
|
|
|
}
|
ManaBox import, with progress and a format explainer
Library → Import cards… now reads a ManaBox collection export, either the
.csv or the .txt. The CSV is matched by Scryfall ID, falling back to name,
set and collector number, and its Foil column picks the copy group's finish.
Condition, language and prices are ignored; proxies count as owned.
A decklist line that names a printing now resolves to that printing's card
when several cards share the name (a card and its token, or an art-series
entry), and a bare name prefers the real card.
Imports run in one transaction (nestable via SAVEPOINT), which took the
942-row sample from 22s to 0.2s, and off the main thread, with a progress
dialog for slow ones. An explainer of the supported formats now comes
before the file picker.
2026-10-16 21:57:52 -05:00
|
|
|
|
.progress-dialog-bar {
|
|
|
|
|
|
margin: var(--space-3) 0;
|
|
|
|
|
|
}
|
2026-07-07 20:22:31 -05:00
|
|
|
|
.ingest-segment.active .ingest-segment-fill {
|
|
|
|
|
|
background: linear-gradient(90deg, var(--accent-hover), var(--accent));
|
|
|
|
|
|
}
|
|
|
|
|
|
.ingest-segment-label {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
margin-top: 3px;
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 11px;
|
2026-07-07 20:22:31 -05:00
|
|
|
|
color: var(--faint);
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
}
|
|
|
|
|
|
.ingest-segment.active .ingest-segment-label {
|
|
|
|
|
|
color: var(--accent-hover);
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ingest-bar-done .ingest-segment-fill {
|
|
|
|
|
|
background: var(--success);
|
|
|
|
|
|
}
|
|
|
|
|
|
.ingest-bar-done .ingest-bar-message {
|
|
|
|
|
|
color: var(--success);
|
|
|
|
|
|
}
|
|
|
|
|
|
.ingest-bar-error .ingest-segment-fill {
|
|
|
|
|
|
background: var(--danger);
|
|
|
|
|
|
}
|
|
|
|
|
|
.ingest-bar-error .ingest-bar-message {
|
|
|
|
|
|
color: var(--danger);
|
|
|
|
|
|
}
|
|
|
|
|
|
.ingest-bar-cancelled .ingest-segment-fill {
|
|
|
|
|
|
background: var(--warn);
|
|
|
|
|
|
}
|
|
|
|
|
|
.ingest-bar-cancelled .ingest-bar-message {
|
|
|
|
|
|
color: var(--warn);
|
|
|
|
|
|
}
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
.content {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
overflow: auto;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
padding: var(--space-5) var(--space-6);
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Empty / welcome state */
|
|
|
|
|
|
.empty-state {
|
|
|
|
|
|
max-width: 480px;
|
|
|
|
|
|
margin: 9vh auto 0;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.empty-mark {
|
Rebrand to mtgc: pirate-ship logo, lowercase app name, mtgc binary
- New logomark: an original pirate-ship silhouette (hull, twin billowing
sails, swallow-tail pennant) in the terracotta brand gradient on a dark
square — a nod to the MTG card "Pirate Ship", not a reproduction of its
art. Source at src-tauri/icons/logo-source.svg; regenerate the full
platform icon set (desktop/Windows Store/iOS/Android) with
`cargo tauri icon src-tauri/icons/logo-source.svg`.
- Apply it in-app via a new inline ShipLogo component (App.tsx), replacing
the old "M" sidebar mark and "✦" empty-state mark with dark rounded
squares containing the ship glyph.
- Rename the app to lowercase "mtgc" everywhere: window title and
productName (tauri.conf.json), <title> (index.html), sidebar brand text,
empty-state heading.
- Rename the Tauri package/binary mtgc-app -> mtgc (and its lib crate
mtgc_app_lib -> mtgc_lib), so `cargo tauri build` now produces
target/release/mtgc; updated main.rs and every README reference.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:53:36 -05:00
|
|
|
|
width: 88px;
|
|
|
|
|
|
height: 88px;
|
|
|
|
|
|
border-radius: 22px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
Rebrand to mtgc: pirate-ship logo, lowercase app name, mtgc binary
- New logomark: an original pirate-ship silhouette (hull, twin billowing
sails, swallow-tail pennant) in the terracotta brand gradient on a dark
square — a nod to the MTG card "Pirate Ship", not a reproduction of its
art. Source at src-tauri/icons/logo-source.svg; regenerate the full
platform icon set (desktop/Windows Store/iOS/Android) with
`cargo tauri icon src-tauri/icons/logo-source.svg`.
- Apply it in-app via a new inline ShipLogo component (App.tsx), replacing
the old "M" sidebar mark and "✦" empty-state mark with dark rounded
squares containing the ship glyph.
- Rename the app to lowercase "mtgc" everywhere: window title and
productName (tauri.conf.json), <title> (index.html), sidebar brand text,
empty-state heading.
- Rename the Tauri package/binary mtgc-app -> mtgc (and its lib crate
mtgc_app_lib -> mtgc_lib), so `cargo tauri build` now produces
target/release/mtgc; updated main.rs and every README reference.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:53:36 -05:00
|
|
|
|
background: linear-gradient(150deg, var(--bg-elev), var(--bg-rail));
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
box-shadow: 0 10px 30px var(--accent-glow);
|
|
|
|
|
|
}
|
|
|
|
|
|
.empty-state h2 {
|
|
|
|
|
|
margin: 4px 0 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-02 16:10:25 -05:00
|
|
|
|
/* Mobile's Info tab — card-database management, which lives in the topbar
|
|
|
|
|
|
instead on desktop (see INFO_TAB in App.tsx). */
|
2026-10-23 23:27:58 -05:00
|
|
|
|
/* Info page: a column of panels on narrow screens, two columns on wide ones.
|
|
|
|
|
|
Below the breakpoint the two `.info-col` wrappers dissolve (display:
|
|
|
|
|
|
contents) so `order` can interleave their panels into the reading order
|
|
|
|
|
|
the single column always had: card data, libraries, sync, summary, about. */
|
|
|
|
|
|
.info-grid {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: var(--space-4);
|
|
|
|
|
|
}
|
|
|
|
|
|
.info-col {
|
|
|
|
|
|
display: contents;
|
|
|
|
|
|
}
|
|
|
|
|
|
.info-section {
|
2026-08-02 16:10:25 -05:00
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: var(--space-3);
|
2026-10-23 23:27:58 -05:00
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.info-db { order: 1; }
|
|
|
|
|
|
.info-libraries { order: 2; }
|
|
|
|
|
|
.info-sync { order: 3; }
|
|
|
|
|
|
.info-summary { order: 4; }
|
|
|
|
|
|
.info-about { order: 5; }
|
|
|
|
|
|
@media (min-width: 900px) {
|
|
|
|
|
|
.info-grid {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
|
|
|
|
align-items: start;
|
|
|
|
|
|
max-width: 1200px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.info-col {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: var(--space-4);
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.info-about-row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
gap: var(--space-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
.info-version {
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
font-weight: 600;
|
2026-08-02 16:10:25 -05:00
|
|
|
|
}
|
Replace folder sync with an HTTP sync server, keeping the app offline-first
Folder sync left too much to the user's sync tool: platform file access
(the Android SAF plugin), choosing the document, merging conflict copies —
and its merge base could fall out of step with the document (absorbing
conflict copies, "forget", picking a new Android folder), reading every
difference as a deletion.
The new mtgc-server crate stores one versioned library per user and accepts
a write only when it names the version it replaces. It never merges: devices
still do that with merge3, through a new sync cycle over a `Remote` trait in
mtgc-core. The base advances only on knowledge — to what was read, or to a
write the server confirmed — and is stored with the server and account it
belongs to, so a different account starts from an empty ancestor instead of
deleting. The working copy is only rewritten under the app-state lock, and
sync commands now run off the main thread.
Sign-in happens in the browser on the server's pages, via the owner's OIDC
provider and/or local username/password accounts (Argon2id, managed with
`mtgc-server user ...`), and returns a per-device revocable token to the app
by PKCE-protected code: a loopback port on desktop, the mtgc:// scheme on
Android.
The Info tab gains sign-in, the first-sync combine/replace choice, the
account's devices, and JSON export/import; the top bar shows sync state.
Includes a Dockerfile, Compose/Caddy example, and scripts to build and push
${DOCKER_REGISTRY}/mtgc/sync-server.
2026-10-08 22:32:08 -05:00
|
|
|
|
/* Top-bar sync state; opens the Info tab. Quiet when all is well, so it
|
|
|
|
|
|
only draws the eye when something needs attention. */
|
|
|
|
|
|
.sync-indicator {
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
font-size: var(--fs-caption);
|
|
|
|
|
|
padding: 5px 10px;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.sync-indicator-warn {
|
|
|
|
|
|
border-color: var(--warn);
|
|
|
|
|
|
color: var(--warn);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* First sync with an account that already has a library: a decision only the
|
|
|
|
|
|
user can make, so it's set apart from the surrounding status text. */
|
|
|
|
|
|
.sync-choice {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
padding: var(--space-3);
|
|
|
|
|
|
border: 1px solid var(--warn);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
}
|
|
|
|
|
|
.sync-devices {
|
|
|
|
|
|
list-style: none;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
.sync-devices li {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.sync-device-name {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
}
|
|
|
|
|
|
.sync-devices .link-button {
|
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
|
background: none;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
padding: 2px 4px;
|
|
|
|
|
|
color: var(--danger);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* The signed-in account. */
|
Rework library sync around a JSON document; add card filters
Sync
The previous design put the merge inside SQL: timestamp-stamping triggers,
a tombstone table, per-device SQLite snapshots, a device id in the database,
and row merges written as correlated-subquery UPDATEs. All of that schema
was inherited by desktop, and the merge was very hard to verify.
Replace it with a single JSON document plus a three-way merge in plain Rust.
Storing the last-read remote document as the merge base makes "who changed
what" and "what was deleted" derivable, which removes the tombstone table,
the updated_at columns and all eight triggers. It also makes a single shared
file safe: if a publish is lost the remote is unchanged, so the next merge
re-detects the local edits and republishes rather than reading their absence
as a deletion. That removes the need for per-device files and for device
identity entirely. Schema impact drops to one column, decks.uuid.
Conflicts resolve to "theirs", which is required for convergence rather than
chosen for taste — preferring ours has each device re-asserting its own value
forever. Every such choice is reported to the user.
SQLite becomes invisible: the working copy lives in the app data dir and the
user only ever names a .json file. There is always a document, defaulting to
private storage, so edits are never stranded with nowhere to be saved.
Pointing at a file that doesn't exist yet moves the current library there;
pointing at one that does adopts it. A converter brings a pre-document
library.db across.
Android reaches the folder through the Storage Access Framework rather than
MANAGE_EXTERNAL_STORAGE, so no storage permission is declared at all.
Filters
Track card origin in reference.db: Universes Beyond, digital-only and
Un-set cards, plus numeric power/toughness. Each origin flag is derived as
"every printing", not "any printing" — ordinary cards get reprinted into
those sets, and Lightning Bolt appearing in Marvel Commander must not hide
it. Non-numeric power ("*", "1+*") stays NULL so it drops out of range
filters instead of being compared as zero.
Also wire up filters over data already present: price range, format legality
(restricted counts as legal) and "can be a commander". All of these degrade
silently against an older reference.db rather than failing every query.
The shared criteria live in one component used by both filter panels, as a
compact row of icon toggles.
Card Search gains the Library tab's bulk selection: checkboxes in both views,
select-all, and a bulk add-to-deck with a single undo for the whole batch.
2026-08-07 22:03:49 -05:00
|
|
|
|
.sync-folder {
|
|
|
|
|
|
font-size: var(--fs-body);
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
overflow-wrap: anywhere;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Outcome of the synced-folder self-test. Not a `.hint`: whether sync works is
|
|
|
|
|
|
the point of the section, so it reads at body size rather than as
|
|
|
|
|
|
de-emphasized supporting text. */
|
|
|
|
|
|
.sync-ok,
|
|
|
|
|
|
.sync-bad {
|
|
|
|
|
|
font-size: var(--fs-body);
|
|
|
|
|
|
line-height: 1.45;
|
|
|
|
|
|
overflow-wrap: anywhere;
|
|
|
|
|
|
}
|
|
|
|
|
|
.sync-ok {
|
|
|
|
|
|
color: var(--success);
|
|
|
|
|
|
}
|
|
|
|
|
|
.sync-bad {
|
|
|
|
|
|
color: var(--danger);
|
|
|
|
|
|
}
|
2026-08-02 16:10:25 -05:00
|
|
|
|
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
/* ============================================================
|
|
|
|
|
|
Typography helpers
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
h2 {
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
margin: var(--space-1) 0 var(--space-4);
|
|
|
|
|
|
font-size: var(--fs-display);
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
letter-spacing: -0.2px;
|
|
|
|
|
|
}
|
|
|
|
|
|
h3 {
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
margin: var(--space-5) 0 var(--space-3);
|
|
|
|
|
|
font-size: var(--fs-title);
|
2026-07-20 00:21:22 -05:00
|
|
|
|
font-weight: 800;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
text-transform: uppercase;
|
2026-07-20 00:21:22 -05:00
|
|
|
|
letter-spacing: 0.05em;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
border-bottom: 2px solid var(--border-soft);
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
padding-bottom: var(--space-3);
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
}
|
|
|
|
|
|
h4 {
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
margin: var(--space-4) 0 var(--space-2);
|
|
|
|
|
|
font-size: var(--fs-body);
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
font-weight: 600;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
/* Quiet accent — full terracotta is reserved for primary actions. */
|
|
|
|
|
|
color: var(--accent-quiet);
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
}
|
2026-07-20 00:21:22 -05:00
|
|
|
|
|
|
|
|
|
|
/* "Group by" sub-headers (e.g. per card type/subtype/CMC/color within a
|
|
|
|
|
|
* section) — sticky within .content's scroll area so the current group
|
|
|
|
|
|
* stays visible while scrolling through a long list, instead of scrolling
|
|
|
|
|
|
* out of view along with its cards. Applied via class (not the bare h3/h4
|
|
|
|
|
|
* tag) since h3 is also used for .dialog-title, which must not be sticky. */
|
|
|
|
|
|
.group-header {
|
|
|
|
|
|
position: sticky;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
z-index: 5;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
margin: var(--space-4) 0 var(--space-2);
|
|
|
|
|
|
padding: var(--space-2) 0;
|
2026-07-20 00:21:22 -05:00
|
|
|
|
background: var(--bg-app);
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
font-size: var(--fs-body);
|
2026-07-20 00:21:22 -05:00
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
text-transform: none;
|
|
|
|
|
|
letter-spacing: normal;
|
|
|
|
|
|
color: var(--text-strong);
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
/* Quiet warm underline rather than full terracotta (accent hierarchy). */
|
|
|
|
|
|
border-bottom: 2px solid var(--accent-quiet);
|
2026-07-20 00:21:22 -05:00
|
|
|
|
}
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
.muted {
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
.hint {
|
|
|
|
|
|
color: var(--muted);
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 14px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
line-height: 1.65;
|
|
|
|
|
|
}
|
|
|
|
|
|
.row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
.badge {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
background: var(--accent-subtle);
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
color: var(--accent-quiet);
|
|
|
|
|
|
border-radius: var(--radius-pill);
|
|
|
|
|
|
padding: 1px var(--space-2);
|
|
|
|
|
|
font-size: var(--fs-caption);
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-20 00:21:22 -05:00
|
|
|
|
/* Deck format-legality panel: a prominent, colorful tile, matching
|
|
|
|
|
|
.commander-hero / .deck-stats in weight, above the fold on every deck. */
|
|
|
|
|
|
.legality-tile {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
border: 1px solid transparent;
|
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
|
padding: 14px 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.legality-tile.legal {
|
|
|
|
|
|
background: rgba(75, 176, 115, 0.16);
|
|
|
|
|
|
border-color: rgba(75, 176, 115, 0.4);
|
|
|
|
|
|
}
|
|
|
|
|
|
.legality-tile.illegal {
|
|
|
|
|
|
background: rgba(216, 80, 63, 0.16);
|
|
|
|
|
|
border-color: rgba(216, 80, 63, 0.4);
|
|
|
|
|
|
}
|
|
|
|
|
|
.legality-tile.unchecked {
|
|
|
|
|
|
background: var(--bg-panel);
|
|
|
|
|
|
border-color: var(--border-soft);
|
|
|
|
|
|
}
|
|
|
|
|
|
.legality-tile-head {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.legality-tile-icon {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
width: 40px;
|
|
|
|
|
|
height: 40px;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
}
|
|
|
|
|
|
.legality-tile.legal .legality-tile-icon {
|
|
|
|
|
|
background: var(--success);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.legality-tile.illegal .legality-tile-icon {
|
|
|
|
|
|
background: var(--danger);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.legality-tile.unchecked .legality-tile-icon {
|
|
|
|
|
|
background: var(--muted);
|
|
|
|
|
|
color: var(--bg-panel);
|
|
|
|
|
|
}
|
|
|
|
|
|
.legality-tile-info {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 2px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.legality-tile-status {
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.legality-tile.legal .legality-tile-status {
|
|
|
|
|
|
color: var(--success);
|
|
|
|
|
|
}
|
|
|
|
|
|
.legality-tile.illegal .legality-tile-status {
|
|
|
|
|
|
color: var(--danger);
|
|
|
|
|
|
}
|
|
|
|
|
|
.legality-tile-detail {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
}
|
2026-10-26 21:44:16 +00:00
|
|
|
|
.legality-tile-group {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.legality-tile-group-head {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
margin-left: 54px;
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
letter-spacing: 0.04em;
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
button.legality-tile-select {
|
|
|
|
|
|
padding: 2px 8px;
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
text-transform: none;
|
|
|
|
|
|
letter-spacing: normal;
|
|
|
|
|
|
}
|
2026-07-20 00:21:22 -05:00
|
|
|
|
.legality-tile-reasons {
|
|
|
|
|
|
margin: 0 0 0 54px;
|
|
|
|
|
|
padding-left: 18px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
color: var(--text);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
/* ============================================================
|
|
|
|
|
|
Quantity stepper: [− N +] for owned-copy counts. Used on Add Cards
|
|
|
|
|
|
and both Library views (list + tile) for a consistent look.
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
.qty-stepper {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
height: 26px;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
background: var(--bg-input);
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
transition: opacity 0.25s ease, background 0.15s ease, border-color 0.15s ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
.qty-stepper-btn {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
width: 26px;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
background: none;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: 0;
|
|
|
|
|
|
color: var(--muted);
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 16px;
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: background 0.12s, color 0.12s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.qty-stepper-btn:hover:not(:disabled) {
|
|
|
|
|
|
background: var(--accent-subtle);
|
|
|
|
|
|
color: var(--accent-hover);
|
|
|
|
|
|
}
|
|
|
|
|
|
.qty-stepper-btn:active:not(:disabled) {
|
|
|
|
|
|
background: var(--accent);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
transform: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.qty-stepper-btn:disabled {
|
|
|
|
|
|
opacity: 0.3;
|
|
|
|
|
|
cursor: default;
|
|
|
|
|
|
}
|
|
|
|
|
|
.qty-stepper-value {
|
|
|
|
|
|
min-width: 22px;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
padding: 0 2px;
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 14px;
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
}
|
|
|
|
|
|
.qty-stepper.warn {
|
|
|
|
|
|
border-color: var(--warn);
|
|
|
|
|
|
}
|
|
|
|
|
|
.qty-stepper.warn .qty-stepper-value {
|
|
|
|
|
|
color: var(--warn);
|
|
|
|
|
|
}
|
|
|
|
|
|
.qty-stepper.pending {
|
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Overlay variant: dark translucent pill for use atop card art. */
|
|
|
|
|
|
.qty-stepper-overlay {
|
|
|
|
|
|
height: 24px;
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.78);
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
backdrop-filter: blur(2px);
|
|
|
|
|
|
}
|
|
|
|
|
|
.qty-stepper-overlay .qty-stepper-btn {
|
|
|
|
|
|
width: 22px;
|
|
|
|
|
|
color: rgba(255, 255, 255, 0.85);
|
|
|
|
|
|
}
|
|
|
|
|
|
.qty-stepper-overlay .qty-stepper-btn:hover:not(:disabled) {
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.18);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.qty-stepper-overlay .qty-stepper-btn:active:not(:disabled) {
|
|
|
|
|
|
background: var(--accent);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.qty-stepper-overlay .qty-stepper-value {
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.qty-stepper-overlay.warn {
|
|
|
|
|
|
background: var(--warn);
|
|
|
|
|
|
}
|
|
|
|
|
|
.qty-stepper-overlay.warn .qty-stepper-value,
|
|
|
|
|
|
.qty-stepper-overlay.warn .qty-stepper-btn {
|
|
|
|
|
|
color: #241700;
|
|
|
|
|
|
}
|
|
|
|
|
|
.qty-stepper-overlay.warn .qty-stepper-btn:hover:not(:disabled) {
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.12);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
/* ============================================================
|
|
|
|
|
|
Icon buttons: rename, duplicate, delete, dismiss, close — one
|
|
|
|
|
|
consistent look used everywhere a bare icon-only action appears.
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
.icon-btn {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
width: 28px;
|
|
|
|
|
|
height: 28px;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
background: var(--bg-input);
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
.icon-btn:hover:not(:disabled) {
|
|
|
|
|
|
background: var(--bg-hover);
|
|
|
|
|
|
color: var(--text);
|
|
|
|
|
|
border-color: var(--bg-elev);
|
|
|
|
|
|
}
|
|
|
|
|
|
.icon-btn:active:not(:disabled) {
|
|
|
|
|
|
transform: translateY(1px);
|
|
|
|
|
|
}
|
|
|
|
|
|
.icon-btn:disabled {
|
|
|
|
|
|
opacity: 0.35;
|
|
|
|
|
|
}
|
|
|
|
|
|
.icon-btn.icon-btn-danger:hover:not(:disabled) {
|
|
|
|
|
|
background: rgba(216, 80, 63, 0.15);
|
|
|
|
|
|
color: var(--danger);
|
|
|
|
|
|
border-color: var(--danger);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Overlay variant: dark translucent, for sitting on top of card art. */
|
|
|
|
|
|
.icon-btn-overlay {
|
|
|
|
|
|
width: 26px;
|
|
|
|
|
|
height: 26px;
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.78);
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
color: rgba(255, 255, 255, 0.85);
|
|
|
|
|
|
backdrop-filter: blur(2px);
|
|
|
|
|
|
}
|
|
|
|
|
|
.icon-btn-overlay:hover:not(:disabled) {
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.18);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.icon-btn-overlay.icon-btn-danger:hover:not(:disabled) {
|
|
|
|
|
|
background: var(--danger);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
|
Dialogs: confirm (delete) / prompt (rename, duplicate-as)
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
.dialog-backdrop {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
inset: 0;
|
|
|
|
|
|
z-index: 400;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
background: rgba(10, 10, 12, 0.72);
|
|
|
|
|
|
backdrop-filter: blur(2px);
|
|
|
|
|
|
animation: preview-fade-in 0.12s ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
.dialog {
|
|
|
|
|
|
width: min(380px, 90vw);
|
|
|
|
|
|
background: var(--bg-panel);
|
|
|
|
|
|
border: 1px solid var(--border-soft);
|
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
|
padding: 20px;
|
|
|
|
|
|
box-shadow: var(--shadow-lg);
|
|
|
|
|
|
}
|
2026-07-20 00:21:22 -05:00
|
|
|
|
.dialog-wide {
|
|
|
|
|
|
width: min(900px, 92vw);
|
|
|
|
|
|
max-height: 86vh;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
}
|
2026-08-02 16:10:25 -05:00
|
|
|
|
.dialog-title-row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
|
}
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
.dialog-title {
|
2026-08-02 16:10:25 -05:00
|
|
|
|
margin: 0;
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 16px;
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
text-transform: none;
|
|
|
|
|
|
letter-spacing: normal;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
|
padding-bottom: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.dialog-message {
|
|
|
|
|
|
margin: 0 0 18px;
|
|
|
|
|
|
color: var(--muted);
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 14px;
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
}
|
2026-07-20 00:21:22 -05:00
|
|
|
|
.dialog-list {
|
|
|
|
|
|
margin: 0 0 18px;
|
|
|
|
|
|
padding-left: 20px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
max-height: 320px;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
}
|
ManaBox import, with progress and a format explainer
Library → Import cards… now reads a ManaBox collection export, either the
.csv or the .txt. The CSV is matched by Scryfall ID, falling back to name,
set and collector number, and its Foil column picks the copy group's finish.
Condition, language and prices are ignored; proxies count as owned.
A decklist line that names a printing now resolves to that printing's card
when several cards share the name (a card and its token, or an art-series
entry), and a bare name prefers the real card.
Imports run in one transaction (nestable via SAVEPOINT), which took the
942-row sample from 22s to 0.2s, and off the main thread, with a progress
dialog for slow ones. An explainer of the supported formats now comes
before the file picker.
2026-10-16 21:57:52 -05:00
|
|
|
|
.dialog-list code {
|
|
|
|
|
|
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
.dialog-field {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
margin-bottom: 14px;
|
|
|
|
|
|
}
|
Decks tab: a deck list page, with the builder on its own screen
The deck list is no longer a sidebar pinned beside the builder; on every
screen size the Decks tab opens to a deck list page (DeckList), and picking
a deck opens its builder full-width with an "All decks" back button. The
list shows each deck as an art tile (or a compact row, toggleable) with its
colors, size against its format, owned %, cost and legality, and can be
searched, filtered by format and sorted (recently opened, name, format,
size, cost, owned %). Rename/duplicate/export/delete live in each deck's
menu and beside the builder's title; New deck now asks for a format too,
defaulting to the last one used.
A deck's cover art is the commander, else its priciest nonland, unless a
card is chosen with "Use as cover" in the card preview. The cover is stored
in decks.cover_oracle_id (migration 0007) and synced as DeckDoc.cover; a
document carrying one is version 3, so the sync server must be updated
before clients publish covers. The list's stats come from one
all_deck_contents query, summarised with the builder's own analysis code,
now in deckAnalysis.ts.
Closes #9
Closes #11
Closes #12
2026-10-25 15:04:41 -05:00
|
|
|
|
.dialog-field input,
|
|
|
|
|
|
.dialog-field select {
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
.dialog-error {
|
|
|
|
|
|
margin: -6px 0 14px;
|
|
|
|
|
|
color: var(--danger);
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 13px;
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
}
|
|
|
|
|
|
.dialog-actions {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
/* ============================================================
|
|
|
|
|
|
Lists (search results, deck list)
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
.result-list {
|
|
|
|
|
|
list-style: none;
|
|
|
|
|
|
margin: 8px 0 0;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
border: 1px solid var(--border-soft);
|
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
background: var(--bg-panel);
|
|
|
|
|
|
}
|
|
|
|
|
|
.result-list li {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
padding: 9px 12px;
|
|
|
|
|
|
border-bottom: 1px solid var(--border-soft);
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: background 0.1s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.result-list li:last-child {
|
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.result-list li:hover {
|
|
|
|
|
|
background: var(--bg-hover);
|
|
|
|
|
|
}
|
|
|
|
|
|
.card-name {
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
}
|
2026-07-08 23:54:47 -05:00
|
|
|
|
|
2026-07-21 21:04:36 -05:00
|
|
|
|
/* Suggestions list: a thumbnail + a compact name/badges header + wrapped
|
|
|
|
|
|
* synergy reason chips per row, with stacked actions on the right. Denser
|
|
|
|
|
|
* than the default .result-list row (smaller padding/gaps, chips instead of
|
|
|
|
|
|
* a bulleted list) since a card can carry several reasons at once. */
|
|
|
|
|
|
.result-list.suggestion-list li {
|
|
|
|
|
|
cursor: default;
|
|
|
|
|
|
align-items: stretch;
|
|
|
|
|
|
padding: 8px 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.suggestion-row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
.suggestion-main {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.suggestion-head {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.suggestion-score {
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: var(--accent-hover);
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
}
|
|
|
|
|
|
.suggestion-reasons {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.reason-chip {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 5px;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
padding: 1px 8px 1px 2px;
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
|
background: var(--bg-elev);
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
.reason-chip-category {
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
padding: 1px 7px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
letter-spacing: 0.03em;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.reason-chip-tribal .reason-chip-category {
|
|
|
|
|
|
background: var(--accent);
|
|
|
|
|
|
}
|
|
|
|
|
|
.reason-chip-mechanic .reason-chip-category {
|
|
|
|
|
|
background: var(--mtg-u);
|
|
|
|
|
|
}
|
|
|
|
|
|
.reason-chip-archetype .reason-chip-category {
|
|
|
|
|
|
background: var(--warn);
|
|
|
|
|
|
}
|
|
|
|
|
|
.reason-chip-mana-base .reason-chip-category {
|
|
|
|
|
|
background: var(--success);
|
|
|
|
|
|
}
|
|
|
|
|
|
.suggestion-actions {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.suggestion-actions button {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
padding: 5px 10px;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Toggleable "filter by why" chips above the suggestion list — same color
|
|
|
|
|
|
* coding as .reason-chip-category, but as clickable, stateful buttons. */
|
|
|
|
|
|
.suggestion-filters {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.filter-chip {
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
padding: 3px 12px;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
background: var(--bg-elev);
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
border: 1px solid var(--border-soft);
|
|
|
|
|
|
}
|
|
|
|
|
|
.filter-chip:hover {
|
|
|
|
|
|
background: var(--bg-hover);
|
|
|
|
|
|
}
|
|
|
|
|
|
.filter-chip.active {
|
Spare/rarity filters, owned-only suggestions, and a Game night tab
Library filters:
- "Only spare copies" quick filter (Library): cards with more copies
owned than the decks' main boards use, the opposite of the ⚠
over-assignment warning. Rows show "N spare" while it's on.
- Rarity toggles (C/U/R/M) in the shared criteria panel, so Library,
Card Search and the set browser all get them. Any printing counts,
since the library tracks cards rather than printings; within a set
it's that set's printing. Arena-only printings don't count for a card
that also exists on paper, so a digital re-rarity can't make a paper
common read as rare.
Suggestions:
- "From my collection" toggle (remembered) narrows candidates to owned
cards before scoring and paging, so "Suggest more" keeps finding them.
- Each suggestion reports copies other decks already use, shown as
"N spare" / "all in other decks". suggest_cards now takes a
SuggestOptions struct instead of a bare `unusual` flag.
Game tab: a life counter for 2-6 players with tap/hold ±1/±10, a
running change bubble, poison and per-opponent commander damage (which
also comes off life), out-of-the-game markers, dice and a coin, and a
random first player. A seat's deck puts its commander's art behind the
life total. On phones the board fills the screen with the far row
turned to face across the table, and asks to keep the screen awake.
The game is per-device scratch state in localStorage, not synced.
Plain filter chips now show an accent background when active; before,
"Unusual picks" only changed its text colour.
2026-10-14 22:09:39 -05:00
|
|
|
|
/* Plain on/off chips ("Unusual picks"); the category chips below swap in
|
|
|
|
|
|
their own colour. */
|
|
|
|
|
|
background: var(--accent);
|
2026-07-21 21:04:36 -05:00
|
|
|
|
color: #fff;
|
|
|
|
|
|
border-color: transparent;
|
|
|
|
|
|
}
|
|
|
|
|
|
.filter-chip-tribal.active {
|
|
|
|
|
|
background: var(--accent);
|
|
|
|
|
|
}
|
|
|
|
|
|
.filter-chip-mechanic.active {
|
|
|
|
|
|
background: var(--mtg-u);
|
|
|
|
|
|
}
|
|
|
|
|
|
.filter-chip-archetype.active {
|
|
|
|
|
|
background: var(--warn);
|
|
|
|
|
|
}
|
|
|
|
|
|
.filter-chip-mana-base.active {
|
|
|
|
|
|
background: var(--success);
|
|
|
|
|
|
}
|
|
|
|
|
|
.filter-chip-clear {
|
|
|
|
|
|
background: none;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
|
padding: 3px 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
.card-type {
|
|
|
|
|
|
color: var(--muted);
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 13px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
|
Color pips (WUBRG)
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
.pips {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
gap: 3px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.pip {
|
|
|
|
|
|
width: 18px;
|
|
|
|
|
|
height: 18px;
|
|
|
|
|
|
border-radius: 50%;
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 11px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
font-weight: 800;
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.25);
|
|
|
|
|
|
}
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
.pip.W { background: var(--mtg-w); color: #4a3f2a; }
|
|
|
|
|
|
.pip.U { background: var(--mtg-u); color: #fff; }
|
|
|
|
|
|
.pip.B { background: var(--mtg-b); color: #cbc3d4; }
|
|
|
|
|
|
.pip.R { background: var(--mtg-r); color: #fff; }
|
|
|
|
|
|
.pip.G { background: var(--mtg-g); color: #fff; }
|
|
|
|
|
|
.pip.C { background: var(--mtg-c); color: #23242a; }
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
|
|
|
|
|
|
.color-toggles {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 5px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.color-toggles .pip {
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
opacity: 0.32;
|
|
|
|
|
|
transition: opacity 0.1s, box-shadow 0.1s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.color-toggles .pip.on {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
box-shadow: 0 0 0 2px var(--accent-hover);
|
|
|
|
|
|
}
|
Spare/rarity filters, owned-only suggestions, and a Game night tab
Library filters:
- "Only spare copies" quick filter (Library): cards with more copies
owned than the decks' main boards use, the opposite of the ⚠
over-assignment warning. Rows show "N spare" while it's on.
- Rarity toggles (C/U/R/M) in the shared criteria panel, so Library,
Card Search and the set browser all get them. Any printing counts,
since the library tracks cards rather than printings; within a set
it's that set's printing. Arena-only printings don't count for a card
that also exists on paper, so a digital re-rarity can't make a paper
common read as rare.
Suggestions:
- "From my collection" toggle (remembered) narrows candidates to owned
cards before scoring and paging, so "Suggest more" keeps finding them.
- Each suggestion reports copies other decks already use, shown as
"N spare" / "all in other decks". suggest_cards now takes a
SuggestOptions struct instead of a bare `unusual` flag.
Game tab: a life counter for 2-6 players with tap/hold ±1/±10, a
running change bubble, poison and per-opponent commander damage (which
also comes off life), out-of-the-game markers, dice and a coin, and a
random first player. A seat's deck puts its commander's art behind the
life total. On phones the board fills the screen with the far row
turned to face across the table, and asks to keep the screen awake.
The game is per-device scratch state in localStorage, not synced.
Plain filter chips now show an accent background when active; before,
"Unusual picks" only changed its text colour.
2026-10-14 22:09:39 -05:00
|
|
|
|
/* Rarity toggles borrow the colour pips' on/off treatment, tinted like the
|
|
|
|
|
|
expansion-symbol colours printed on the cards themselves. */
|
|
|
|
|
|
/* Common's black symbol needs a ring to show at all on the dark panel. */
|
|
|
|
|
|
.pip.rarity-pip.common { background: #2b2a2e; color: #e6e1d8; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.4); }
|
|
|
|
|
|
.pip.rarity-pip.uncommon { background: linear-gradient(135deg, #c9d3dc, #7f8c98); color: #1d232a; }
|
|
|
|
|
|
.pip.rarity-pip.rare { background: linear-gradient(135deg, #e6c66e, #a77f2a); color: #2b2210; }
|
|
|
|
|
|
.pip.rarity-pip.mythic { background: linear-gradient(135deg, #f08a3c, #c2401f); color: #fff; }
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
|
Tables
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
table {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
border-collapse: collapse;
|
|
|
|
|
|
}
|
|
|
|
|
|
th {
|
|
|
|
|
|
text-align: left;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
padding: var(--space-2) var(--space-3);
|
|
|
|
|
|
font-size: var(--fs-label);
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
text-transform: uppercase;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
letter-spacing: var(--tracking-label);
|
|
|
|
|
|
color: var(--muted);
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
position: sticky;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
background: var(--bg-content);
|
|
|
|
|
|
border-bottom: 1px solid var(--border);
|
|
|
|
|
|
z-index: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
td {
|
|
|
|
|
|
padding: 8px 12px;
|
|
|
|
|
|
border-bottom: 1px solid var(--border-soft);
|
|
|
|
|
|
vertical-align: middle;
|
|
|
|
|
|
}
|
|
|
|
|
|
tbody tr {
|
|
|
|
|
|
transition: background 0.08s;
|
|
|
|
|
|
}
|
|
|
|
|
|
tbody tr:hover {
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.022);
|
|
|
|
|
|
}
|
2026-08-02 16:10:25 -05:00
|
|
|
|
|
|
|
|
|
|
/* On a phone, the List view's 8-column table has no room to lay out
|
|
|
|
|
|
side-by-side and forced horizontal scrolling of the whole page (not just
|
|
|
|
|
|
the table) — see the .content padding/overflow rules elsewhere, nothing
|
|
|
|
|
|
scoped the scroll to the table itself. Below this breakpoint each row
|
|
|
|
|
|
becomes its own stacked card instead: every cell drops to its own line,
|
|
|
|
|
|
headers (meaningless once stacked) are hidden, and cells that need a label
|
|
|
|
|
|
outside their normal column context get one from `data-label` (set in the
|
|
|
|
|
|
view components — see LibraryBrowser/CardSearch/DeckView's table JSX). */
|
|
|
|
|
|
@media (max-width: 720px) {
|
|
|
|
|
|
table,
|
|
|
|
|
|
tbody,
|
|
|
|
|
|
tr {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
thead {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
table {
|
|
|
|
|
|
border-collapse: separate;
|
|
|
|
|
|
border-spacing: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
tbody tr {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
overflow: hidden; /* contains .cell-thumb's float below */
|
|
|
|
|
|
background: var(--bg-panel);
|
|
|
|
|
|
border: 1px solid var(--border-soft);
|
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
|
padding: var(--space-3);
|
|
|
|
|
|
padding-right: calc(var(--space-3) + 28px); /* clears .cell-check */
|
|
|
|
|
|
margin-bottom: var(--space-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
tbody tr:hover {
|
|
|
|
|
|
background: var(--bg-panel);
|
|
|
|
|
|
}
|
|
|
|
|
|
td {
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Selection checkbox: pinned to the row-card's corner instead of taking a
|
|
|
|
|
|
line of its own. */
|
|
|
|
|
|
.cell-check {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: var(--space-3);
|
|
|
|
|
|
right: var(--space-3);
|
|
|
|
|
|
width: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Thumbnail floats so the name/type text wraps beside it — that's what
|
|
|
|
|
|
buys back the vertical space the old one-cell-per-line layout wasted. */
|
|
|
|
|
|
.cell-thumb {
|
|
|
|
|
|
float: left;
|
|
|
|
|
|
width: 56px;
|
|
|
|
|
|
margin-right: var(--space-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
.cell-thumb .card-thumb,
|
|
|
|
|
|
.cell-thumb .thumb-empty {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
width: 56px;
|
|
|
|
|
|
height: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
td.card-name {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
font-size: var(--fs-body);
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
}
|
|
|
|
|
|
td.card-type {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
font-size: var(--fs-caption);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Color pips and any other small cell (MV, Owned, In deck, Decks /
|
|
|
|
|
|
Location, ...) sit inline as wrapping chips instead of one per line —
|
|
|
|
|
|
as long as they fit within the floated thumbnail's height they tuck in
|
|
|
|
|
|
beside it, otherwise they wrap below it like normal text. */
|
|
|
|
|
|
td:has(.pips),
|
|
|
|
|
|
td[data-label] {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
margin: var(--space-2) var(--space-3) 0 0;
|
|
|
|
|
|
font-size: var(--fs-caption);
|
|
|
|
|
|
}
|
|
|
|
|
|
td[data-label]::before {
|
|
|
|
|
|
content: attr(data-label) ":";
|
|
|
|
|
|
font-size: var(--fs-micro);
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
letter-spacing: 0.05em;
|
|
|
|
|
|
color: var(--faint);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Buttons (Add to deck, maybeboard toggle, ...) always get their own full
|
|
|
|
|
|
row below everything else. */
|
|
|
|
|
|
td:has(button) {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
clear: both;
|
|
|
|
|
|
margin-top: var(--space-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
.over-assigned {
|
|
|
|
|
|
color: var(--warn);
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-chip {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
background: var(--bg-input);
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
padding: 2px 8px;
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 12px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
color: var(--text);
|
|
|
|
|
|
margin-right: 4px;
|
|
|
|
|
|
}
|
2026-10-23 23:10:40 -05:00
|
|
|
|
/* A deck chip that opens its deck (see `DeckChip`): keeps the chip's own
|
|
|
|
|
|
look rather than the global button's, plus a hover cue. */
|
|
|
|
|
|
.deck-chip-link {
|
|
|
|
|
|
line-height: inherit;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-chip.deck-chip-link {
|
|
|
|
|
|
font-weight: inherit;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-chip-link:hover {
|
|
|
|
|
|
border-color: var(--accent);
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
}
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
|
2026-07-08 08:20:09 -05:00
|
|
|
|
/* ============================================================
|
|
|
|
|
|
Bulk-selection action bar (Library tab)
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
.bulk-bar {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 16px;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
background: var(--accent-subtle);
|
|
|
|
|
|
border: 1px solid var(--accent-glow);
|
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
|
padding: 10px 16px;
|
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.bulk-bar-count {
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
color: var(--accent-hover);
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.bulk-bar-actions {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.bulk-bar-delta {
|
|
|
|
|
|
width: 48px;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
padding: 8px 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-21 21:04:36 -05:00
|
|
|
|
/* Floating "Undo" snackbar (e.g. after a bulk add-to-deck). Fixed so it stays
|
|
|
|
|
|
reachable regardless of scroll position; sits below dialogs (z-index 400+)
|
|
|
|
|
|
since it's only ever shown against the current view, not over a modal. */
|
|
|
|
|
|
.undo-bar {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
left: 50%;
|
|
|
|
|
|
bottom: 28px;
|
|
|
|
|
|
transform: translateX(-50%);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 14px;
|
|
|
|
|
|
background: var(--bg-panel);
|
|
|
|
|
|
border: 1px solid var(--border-soft);
|
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
|
padding: 10px 16px;
|
|
|
|
|
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
|
|
|
|
|
|
z-index: 300;
|
|
|
|
|
|
}
|
|
|
|
|
|
.undo-bar-close {
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
padding: 2px 6px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
.undo-bar-close:hover {
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
/* ============================================================
|
|
|
|
|
|
Filters panel
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
/* The workspace panel unifies the filter grid and the results toolbar into
|
|
|
|
|
|
one surface (Library + Card Search), so the display controls read as part
|
|
|
|
|
|
of the filtering apparatus instead of floating loosely above it. */
|
|
|
|
|
|
.workspace {
|
|
|
|
|
|
margin-bottom: var(--space-4);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Just the responsive field grid; surface comes from the wrapping `.panel`. */
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
.filters {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
gap: var(--space-4);
|
|
|
|
|
|
}
|
2026-08-02 16:10:25 -05:00
|
|
|
|
/* Wraps every field after "Name contains" so it can be hidden as a group on
|
|
|
|
|
|
narrow screens — a 9-10 field grid is a wall to scroll past on a phone
|
|
|
|
|
|
before any results appear. `display: contents` on the wrapper (rather than
|
|
|
|
|
|
e.g. a nested grid) keeps every field a direct child of `.filters` at every
|
|
|
|
|
|
width, so it doesn't disturb the shared grid's column sizing/auto-flow;
|
|
|
|
|
|
only the max-width:720px rule below actually hides anything. */
|
|
|
|
|
|
.filters-more {
|
|
|
|
|
|
display: contents;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Irrelevant above the breakpoint (every field is already visible there). */
|
|
|
|
|
|
.filters-toggle {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
@media (max-width: 720px) {
|
|
|
|
|
|
.filters-toggle {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
margin-top: var(--space-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
.filters-more:not(.expanded) {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
/* The results toolbar strip at the bottom of the workspace panel: selection +
|
|
|
|
|
|
count on the left, group/sort/view on the right, divided from the filters. */
|
|
|
|
|
|
.workspace-bar {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: var(--space-3);
|
|
|
|
|
|
margin-top: var(--space-4);
|
|
|
|
|
|
padding-top: var(--space-4);
|
|
|
|
|
|
border-top: 1px solid var(--border-soft);
|
|
|
|
|
|
}
|
|
|
|
|
|
.workspace-bar-check {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
font-size: var(--fs-caption);
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
.workspace-bar-count {
|
|
|
|
|
|
font-size: var(--fs-caption);
|
|
|
|
|
|
color: var(--faint);
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
}
|
|
|
|
|
|
.field {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
}
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
/* Form field labels: sentence-case and higher-contrast rather than tiny faint
|
|
|
|
|
|
all-caps — clearer to scan above each input than a structural section marker. */
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
.field > label {
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
font-size: var(--fs-caption);
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
font-weight: 600;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
color: var(--muted);
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
}
|
Rework library sync around a JSON document; add card filters
Sync
The previous design put the merge inside SQL: timestamp-stamping triggers,
a tombstone table, per-device SQLite snapshots, a device id in the database,
and row merges written as correlated-subquery UPDATEs. All of that schema
was inherited by desktop, and the merge was very hard to verify.
Replace it with a single JSON document plus a three-way merge in plain Rust.
Storing the last-read remote document as the merge base makes "who changed
what" and "what was deleted" derivable, which removes the tombstone table,
the updated_at columns and all eight triggers. It also makes a single shared
file safe: if a publish is lost the remote is unchanged, so the next merge
re-detects the local edits and republishes rather than reading their absence
as a deletion. That removes the need for per-device files and for device
identity entirely. Schema impact drops to one column, decks.uuid.
Conflicts resolve to "theirs", which is required for convergence rather than
chosen for taste — preferring ours has each device re-asserting its own value
forever. Every such choice is reported to the user.
SQLite becomes invisible: the working copy lives in the app data dir and the
user only ever names a .json file. There is always a document, defaulting to
private storage, so edits are never stranded with nowhere to be saved.
Pointing at a file that doesn't exist yet moves the current library there;
pointing at one that does adopts it. A converter brings a pre-document
library.db across.
Android reaches the folder through the Storage Access Framework rather than
MANAGE_EXTERNAL_STORAGE, so no storage permission is declared at all.
Filters
Track card origin in reference.db: Universes Beyond, digital-only and
Un-set cards, plus numeric power/toughness. Each origin flag is derived as
"every printing", not "any printing" — ordinary cards get reprinted into
those sets, and Lightning Bolt appearing in Marvel Commander must not hide
it. Non-numeric power ("*", "1+*") stays NULL so it drops out of range
filters instead of being compared as zero.
Also wire up filters over data already present: price range, format legality
(restricted counts as legal) and "can be a commander". All of these degrade
silently against an older reference.db rather than failing every query.
The shared criteria live in one component used by both filter panels, as a
compact row of icon toggles.
Card Search gains the Library tab's bulk selection: checkboxes in both views,
select-all, and a bulk add-to-deck with a single undo for the whole batch.
2026-08-07 22:03:49 -05:00
|
|
|
|
/* Compact icon filter toggles, following .color-toggles' visual grammar: dim
|
|
|
|
|
|
when off, lit with an accent ring when on. Replaces a stack of labelled
|
|
|
|
|
|
checkboxes that ate most of the filter panel's height — the explanations
|
|
|
|
|
|
moved into tooltips. */
|
|
|
|
|
|
.icon-toggles {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 5px;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.icon-toggle {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
width: 28px;
|
|
|
|
|
|
height: 28px;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
|
background: var(--bg-elev);
|
|
|
|
|
|
color: var(--text);
|
|
|
|
|
|
opacity: 0.4;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: opacity 0.1s, box-shadow 0.1s, color 0.1s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.icon-toggle:hover {
|
|
|
|
|
|
opacity: 0.75;
|
|
|
|
|
|
}
|
|
|
|
|
|
.icon-toggle.on {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
box-shadow: 0 0 0 2px var(--accent-hover);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Removing cards should not look like selecting them. */
|
|
|
|
|
|
.icon-toggle.exclude.on {
|
|
|
|
|
|
color: var(--danger);
|
|
|
|
|
|
box-shadow: 0 0 0 2px var(--danger);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Diagonal bar across an active exclusion, so "hidden" reads without the
|
|
|
|
|
|
tooltip. */
|
|
|
|
|
|
.icon-toggle-slash {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
inset: 0;
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
background: linear-gradient(
|
|
|
|
|
|
to bottom right,
|
|
|
|
|
|
transparent calc(50% - 1px),
|
|
|
|
|
|
currentColor calc(50% - 1px),
|
|
|
|
|
|
currentColor calc(50% + 1px),
|
|
|
|
|
|
transparent calc(50% + 1px)
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Separates the exclusions from the selecting filter beside them. */
|
|
|
|
|
|
.icon-toggles-sep {
|
|
|
|
|
|
width: 1px;
|
|
|
|
|
|
align-self: stretch;
|
|
|
|
|
|
margin: 2px 3px;
|
|
|
|
|
|
background: var(--border-soft);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
.location-input {
|
|
|
|
|
|
padding: 4px 8px;
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 13px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
max-width: 160px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-08 23:54:47 -05:00
|
|
|
|
/* ============================================================
|
|
|
|
|
|
TagInput: chip-based multi-value filter with autocomplete
|
|
|
|
|
|
(type/subtype/keyword filters in the Library tab)
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
.tag-input {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
.tag-chips {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 5px;
|
|
|
|
|
|
min-height: 34px;
|
|
|
|
|
|
padding: 5px 8px;
|
|
|
|
|
|
background: var(--bg-input);
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
transition: border-color 0.12s, box-shadow 0.12s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.tag-chips:focus-within {
|
|
|
|
|
|
border-color: var(--accent);
|
|
|
|
|
|
box-shadow: 0 0 0 3px var(--accent-subtle);
|
|
|
|
|
|
}
|
|
|
|
|
|
.tag-chip {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 2px;
|
|
|
|
|
|
background: var(--accent-subtle);
|
|
|
|
|
|
color: var(--accent-hover);
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
padding: 2px 4px 2px 10px;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.tag-chip-remove {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
width: 17px;
|
|
|
|
|
|
height: 17px;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
background: none;
|
|
|
|
|
|
color: inherit;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
.tag-chip-remove:hover {
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.25);
|
|
|
|
|
|
}
|
|
|
|
|
|
.tag-chip-input {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-width: 70px;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
background: none;
|
|
|
|
|
|
padding: 3px 2px;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.tag-chip-input:focus {
|
|
|
|
|
|
outline: none;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
box-shadow: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.tag-suggestions {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
z-index: 50;
|
|
|
|
|
|
top: calc(100% + 4px);
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
max-height: 220px;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
/* ============================================================
|
|
|
|
|
|
Card images: thumbnails, hover preview, tiles
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
.card-thumb {
|
|
|
|
|
|
width: 40px;
|
|
|
|
|
|
height: 56px;
|
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
background: var(--bg-elev);
|
|
|
|
|
|
cursor: zoom-in;
|
|
|
|
|
|
box-shadow: var(--shadow-sm);
|
|
|
|
|
|
transition: transform 0.1s, box-shadow 0.1s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.card-thumb:hover {
|
|
|
|
|
|
transform: scale(1.05);
|
|
|
|
|
|
box-shadow: 0 0 0 2px var(--accent-hover), var(--shadow-md);
|
|
|
|
|
|
}
|
|
|
|
|
|
.thumb-empty {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
width: 40px;
|
|
|
|
|
|
height: 56px;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
color: var(--faint);
|
|
|
|
|
|
border: 1px dashed var(--border);
|
|
|
|
|
|
border-radius: 5px;
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 19px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
}
|
|
|
|
|
|
.hover-card {
|
|
|
|
|
|
position: fixed;
|
2026-07-21 21:04:36 -05:00
|
|
|
|
/* Above .dialog-backdrop (400) — cards inside a dialog (e.g. the
|
|
|
|
|
|
suggestions modal) can be hovered for a preview without it appearing
|
|
|
|
|
|
behind the dialog it's in. */
|
|
|
|
|
|
z-index: 450;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
border-radius: 14px;
|
2026-07-22 20:55:17 -05:00
|
|
|
|
overflow: hidden;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
box-shadow: var(--shadow-lg);
|
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
}
|
2026-07-22 20:55:17 -05:00
|
|
|
|
.hover-card-img {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
|
}
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
.preview-modal-backdrop {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
inset: 0;
|
2026-07-20 00:21:22 -05:00
|
|
|
|
/* Above .dialog-backdrop (400) — a card can now be clicked to zoom from
|
|
|
|
|
|
inside a dialog (e.g. the sample hand modal), and the zoomed preview
|
|
|
|
|
|
must stay on top of the dialog it was opened from. */
|
|
|
|
|
|
z-index: 450;
|
2026-10-14 20:23:51 -05:00
|
|
|
|
/* Centered via `.preview-modal`'s `margin: auto`, NOT align-items /
|
|
|
|
|
|
justify-content: center — those overflow equally off BOTH edges when the
|
|
|
|
|
|
content is taller than the viewport (card art + info tile on a phone),
|
|
|
|
|
|
and the part pushed above the top edge can't be scrolled to, leaving the
|
|
|
|
|
|
close button under the status bar. Auto margins collapse to 0 instead,
|
|
|
|
|
|
so overflow only ever extends downward into the scrollable area. */
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
display: flex;
|
2026-07-22 23:11:26 -05:00
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
padding: 24px 12px;
|
2026-08-02 16:10:25 -05:00
|
|
|
|
/* The close button pokes up above the (flex-centered) modal content, so on
|
|
|
|
|
|
a tall/near-fullscreen card image it can land right under the Android
|
|
|
|
|
|
status bar — same edge-to-edge issue as .topbar, same fix: pad the safe
|
|
|
|
|
|
area instead of a fixed inset. Degrades to the base 24px on anything
|
|
|
|
|
|
without a system bar to avoid. */
|
|
|
|
|
|
padding-top: calc(24px + env(safe-area-inset-top));
|
|
|
|
|
|
padding-bottom: calc(24px + env(safe-area-inset-bottom));
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
background: rgba(10, 10, 12, 0.72);
|
|
|
|
|
|
backdrop-filter: blur(2px);
|
|
|
|
|
|
cursor: zoom-out;
|
|
|
|
|
|
animation: preview-fade-in 0.12s ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes preview-fade-in {
|
|
|
|
|
|
from {
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
to {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-07-22 23:11:26 -05:00
|
|
|
|
/* The info tile (see `.preview-info`) sits beside the card art whenever
|
|
|
|
|
|
there's room, and wraps onto its own line below when there isn't —
|
|
|
|
|
|
plain flex-wrap rather than a hand-tuned width breakpoint, so neither the
|
|
|
|
|
|
art nor the info tile ever has to shrink to make the other fit. Any
|
|
|
|
|
|
remaining vertical overflow (e.g. a double-faced card's full-size faces
|
|
|
|
|
|
plus a tall info tile on a short window) is handled by
|
|
|
|
|
|
`.preview-modal-backdrop`'s own scrolling, not by clipping content. */
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
.preview-modal {
|
|
|
|
|
|
position: relative;
|
2026-10-14 20:23:51 -05:00
|
|
|
|
margin: auto;
|
2026-10-25 14:23:48 -05:00
|
|
|
|
/* Its empty space (e.g. below a short info tile) closes the modal like the
|
|
|
|
|
|
backdrop does — see ClickPreviewModal — so only the actual content gets
|
|
|
|
|
|
the default cursor. */
|
|
|
|
|
|
cursor: zoom-out;
|
2026-07-22 23:11:26 -05:00
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
justify-content: center;
|
2026-10-14 20:35:50 -05:00
|
|
|
|
gap: 16px; /* mirrored by PREVIEW_GAP in components.tsx */
|
2026-07-22 23:11:26 -05:00
|
|
|
|
max-width: 96vw;
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
}
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
.preview-modal-faces {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
2026-10-25 14:23:48 -05:00
|
|
|
|
.preview-modal > :not(.preview-modal-faces),
|
|
|
|
|
|
.preview-modal-faces > * {
|
|
|
|
|
|
cursor: default;
|
|
|
|
|
|
}
|
2026-10-14 20:29:32 -05:00
|
|
|
|
/* Sized explicitly (Scryfall "normal" images are 488x680) rather than from
|
|
|
|
|
|
the image's intrinsic size, so the box is already its final size before the
|
|
|
|
|
|
image loads — ClickPreviewModal measures the layout on mount to decide on
|
|
|
|
|
|
compact mode. Same result as the old intrinsic sizing capped at 90vw/90vh;
|
|
|
|
|
|
`--preview-img-h` is the height budget, shrunk in compact mode. */
|
|
|
|
|
|
.preview-modal {
|
|
|
|
|
|
--preview-img-h: 90vh;
|
|
|
|
|
|
}
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
.preview-modal-img {
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
display: none;
|
2026-10-14 20:29:32 -05:00
|
|
|
|
width: min(488px, 90vw, calc(var(--preview-img-h) * 488 / 680));
|
|
|
|
|
|
aspect-ratio: 488 / 680;
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
max-width: min(720px, 90vw);
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
border-radius: 18px;
|
|
|
|
|
|
box-shadow: var(--shadow-lg);
|
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
|
|
|
|
}
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
.preview-modal-img.active {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
}
|
2026-07-22 20:55:17 -05:00
|
|
|
|
/* Horizontal cards (Battles/Planes/Phenomena, see `CardRef.horizontal`): the
|
|
|
|
|
|
stored image is portrait-shaped with the content itself rotated. The
|
|
|
|
|
|
wrapper's width/height (landscape) and the inner img's width/height
|
|
|
|
|
|
(the same box transposed, pre-rotation) are computed in JS —
|
|
|
|
|
|
`useRotatedPreviewBox` — and passed in as inline styles; this just
|
|
|
|
|
|
positions/rotates the img to fill the wrapper exactly. */
|
|
|
|
|
|
.preview-modal-img-rotated {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
/* Override `.preview-modal-img`'s portrait-oriented max-width/max-height —
|
|
|
|
|
|
they'd otherwise clamp this wrapper's JS-computed (correctly landscape)
|
|
|
|
|
|
inline width/height down, while the un-clamped rotated img inside stays
|
|
|
|
|
|
at its full computed size and overflows, getting cut off by the
|
|
|
|
|
|
`overflow: hidden` above instead of shrinking to fit. */
|
|
|
|
|
|
max-width: none;
|
|
|
|
|
|
max-height: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.preview-modal-rotate-img {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 50%;
|
|
|
|
|
|
left: 50%;
|
|
|
|
|
|
transform: translate(-50%, -50%) rotate(90deg);
|
|
|
|
|
|
}
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
/* Wide enough to show a double-faced card's front and back side by side —
|
|
|
|
|
|
no flip needed. */
|
|
|
|
|
|
@media (min-width: 860px) {
|
|
|
|
|
|
.preview-modal-dfc .preview-modal-faces {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.preview-modal-dfc .preview-modal-img {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
max-width: min(430px, 43vw);
|
|
|
|
|
|
}
|
2026-07-22 20:55:17 -05:00
|
|
|
|
/* A horizontal front face (e.g. a transformed Battle) shown side-by-side
|
|
|
|
|
|
with its back face: keep using the JS-computed inline width/height
|
|
|
|
|
|
(see `.preview-modal-img-rotated` above) rather than the narrower DFC
|
|
|
|
|
|
column width, which would clamp the wrapper and crop the rotated image
|
|
|
|
|
|
the same way the base max-width/max-height would. */
|
|
|
|
|
|
.preview-modal-dfc .preview-modal-img-rotated {
|
|
|
|
|
|
max-width: none;
|
|
|
|
|
|
}
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
.preview-modal-dfc .preview-modal-flip {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-10-14 20:29:32 -05:00
|
|
|
|
/* Compact mode (see ClickPreviewModal): the full layout would be taller than
|
2026-10-14 20:35:50 -05:00
|
|
|
|
the viewport, so it shows one pane at a time — a single face, or the info
|
|
|
|
|
|
tile — above a toolbar (pane toggle + close) that sits at the bottom for
|
|
|
|
|
|
thumb reach. The modal's width/height and `--preview-img-h` are set inline
|
|
|
|
|
|
from JS: a fixed box sized to the largest pane, so the toolbar stays put
|
|
|
|
|
|
when switching panes. Each pane is pushed down onto the toolbar
|
|
|
|
|
|
(`margin-top: auto`) and centered horizontally; an info tile taller than
|
|
|
|
|
|
the art budget scrolls within itself. */
|
2026-10-14 20:29:32 -05:00
|
|
|
|
.preview-modal-compact {
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
flex-wrap: nowrap;
|
|
|
|
|
|
align-items: stretch;
|
|
|
|
|
|
}
|
2026-10-14 20:35:50 -05:00
|
|
|
|
.preview-modal-compact .preview-modal-faces,
|
|
|
|
|
|
.preview-modal-compact .preview-info {
|
|
|
|
|
|
margin-top: auto;
|
|
|
|
|
|
align-self: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
.preview-modal-compact .preview-modal-faces {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.preview-modal-compact .preview-info {
|
|
|
|
|
|
flex: 0 1 auto;
|
|
|
|
|
|
min-height: 0;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Height mirrored by PREVIEW_BAR_H in components.tsx. */
|
2026-10-14 20:29:32 -05:00
|
|
|
|
.preview-modal-bar {
|
2026-10-14 20:35:50 -05:00
|
|
|
|
flex-shrink: 0;
|
2026-10-14 20:29:32 -05:00
|
|
|
|
height: 36px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.preview-modal-compact .preview-modal-img:not(.active) {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.preview-modal-compact .preview-modal-img:not(.preview-modal-img-rotated) {
|
|
|
|
|
|
max-width: min(720px, 90vw);
|
|
|
|
|
|
}
|
|
|
|
|
|
.preview-modal-compact .preview-modal-close,
|
2026-10-24 15:20:27 -05:00
|
|
|
|
.preview-modal-compact .preview-modal-share,
|
2026-10-14 20:29:32 -05:00
|
|
|
|
.preview-modal-compact .preview-modal-flip,
|
|
|
|
|
|
.preview-modal-compact:not(.preview-modal-show-info) .preview-info,
|
|
|
|
|
|
.preview-modal-show-info .preview-modal-faces {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
.preview-modal-close {
|
|
|
|
|
|
position: absolute;
|
2026-10-24 15:20:27 -05:00
|
|
|
|
top: -16px;
|
|
|
|
|
|
right: -16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* The preview's corner buttons are its main controls, so a little larger
|
|
|
|
|
|
than the overlay buttons on tiles. */
|
|
|
|
|
|
.preview-modal-close .icon-btn,
|
|
|
|
|
|
.preview-modal-share .icon-btn {
|
|
|
|
|
|
width: 32px;
|
|
|
|
|
|
height: 32px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.preview-modal-close .icon-btn svg,
|
|
|
|
|
|
.preview-modal-share .icon-btn svg {
|
|
|
|
|
|
width: 18px;
|
|
|
|
|
|
height: 18px;
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
}
|
|
|
|
|
|
.preview-modal-close .icon-btn {
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
box-shadow: var(--shadow-md);
|
|
|
|
|
|
}
|
Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
a ConfirmDialog for delete and PromptDialog (with friendly name-
collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
every ad hoc close/remove button in the app with it: deck actions,
remove-card-from-deck (list and tile), clear-commander, the card-
preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
overridden, leaving native OS chrome (height, arrow, popup) in place.
Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
on wide viewports instead of requiring Flip, and size the preview
images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
polling get_library_stats while card data is loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
|
|
|
|
.preview-modal-close .icon-btn:hover {
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
background: var(--danger);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
2026-10-24 15:20:27 -05:00
|
|
|
|
/* Beside the close button, to its left: past its 32px, plus a 12px gap. */
|
|
|
|
|
|
.preview-modal-share {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: -16px;
|
|
|
|
|
|
right: 28px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.preview-modal-share .icon-btn {
|
|
|
|
|
|
box-shadow: var(--shadow-md);
|
|
|
|
|
|
}
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
.preview-modal-flip {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
bottom: 14px;
|
|
|
|
|
|
left: 50%;
|
|
|
|
|
|
transform: translateX(-50%);
|
|
|
|
|
|
padding: 7px 16px;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
background: rgba(20, 20, 24, 0.75);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 13px;
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
backdrop-filter: blur(4px);
|
|
|
|
|
|
}
|
|
|
|
|
|
.preview-modal-flip:hover {
|
|
|
|
|
|
background: var(--accent);
|
|
|
|
|
|
border-color: var(--accent);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-22 23:11:26 -05:00
|
|
|
|
.preview-info {
|
|
|
|
|
|
width: min(320px, 90vw);
|
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
gap: var(--space-3);
|
2026-07-22 23:11:26 -05:00
|
|
|
|
background: var(--bg-panel);
|
|
|
|
|
|
border: 1px solid var(--border-soft);
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
|
padding: var(--space-4);
|
|
|
|
|
|
box-shadow: var(--shadow-lg), var(--panel-highlight);
|
2026-07-22 23:11:26 -05:00
|
|
|
|
color: var(--text);
|
|
|
|
|
|
}
|
|
|
|
|
|
.preview-info-name {
|
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
line-height: 1.25;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
}
|
|
|
|
|
|
.preview-info-type {
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
.preview-info-row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.preview-info-mv {
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
.preview-info-price {
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: var(--gold);
|
|
|
|
|
|
}
|
|
|
|
|
|
.preview-info-section {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
padding-top: 10px;
|
|
|
|
|
|
border-top: 1px solid var(--border-soft);
|
|
|
|
|
|
}
|
|
|
|
|
|
.preview-info-label {
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
font-size: var(--fs-label);
|
2026-07-22 23:11:26 -05:00
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
text-transform: uppercase;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
letter-spacing: var(--tracking-label);
|
|
|
|
|
|
color: var(--muted);
|
2026-07-22 23:11:26 -05:00
|
|
|
|
}
|
|
|
|
|
|
.preview-info-deck-chips {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.preview-info-legal-grid {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.legal-pill {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
padding: 2px 8px;
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
border: 1px solid transparent;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.legal-pill.legal-legal {
|
|
|
|
|
|
background: rgba(75, 176, 115, 0.16);
|
|
|
|
|
|
border-color: rgba(75, 176, 115, 0.4);
|
|
|
|
|
|
color: var(--success);
|
|
|
|
|
|
}
|
|
|
|
|
|
.legal-pill.legal-restricted {
|
|
|
|
|
|
background: rgba(224, 163, 74, 0.16);
|
|
|
|
|
|
border-color: rgba(224, 163, 74, 0.4);
|
|
|
|
|
|
color: var(--warn);
|
|
|
|
|
|
}
|
|
|
|
|
|
.legal-pill.legal-banned {
|
|
|
|
|
|
background: rgba(216, 80, 63, 0.16);
|
|
|
|
|
|
border-color: rgba(216, 80, 63, 0.4);
|
|
|
|
|
|
color: var(--danger);
|
|
|
|
|
|
text-decoration: line-through;
|
|
|
|
|
|
}
|
|
|
|
|
|
.legal-pill.legal-not_legal {
|
|
|
|
|
|
border-color: var(--border-soft);
|
|
|
|
|
|
color: var(--faint);
|
|
|
|
|
|
opacity: 0.55;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
.tile-grid {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
|
|
|
|
|
|
gap: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.tile {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 7px;
|
|
|
|
|
|
}
|
2026-08-22 19:14:27 -05:00
|
|
|
|
/* A rotated (landscape) card at a single portrait column's width renders
|
|
|
|
|
|
~2x smaller in area than a normal tile (a 85:61 box is short as well as
|
|
|
|
|
|
narrow) — hard to read. Spanning 2 columns gives it a comparable, legible
|
|
|
|
|
|
footprint instead. */
|
|
|
|
|
|
.tile-rotated {
|
|
|
|
|
|
grid-column: span 2;
|
|
|
|
|
|
}
|
2026-10-16 21:14:43 -05:00
|
|
|
|
/* …except when the per-row control is down to one column, where a span of 2
|
|
|
|
|
|
would add an implicit second column and overflow. */
|
|
|
|
|
|
.tile-grid-single .tile-rotated {
|
|
|
|
|
|
grid-column: auto;
|
|
|
|
|
|
}
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
.tile-img-wrap {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
.tile-img {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
display: block;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
border-radius: var(--radius-card);
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
box-shadow: var(--shadow-sm);
|
Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
Discord-styling rewrite dropped the --panel-2 CSS variable it relied
on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
larger centered modal (list and tile views), with a flip button for
double-faced cards' back face. Hover preview is unchanged and clears
when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
variant for tile corners) and use it consistently for owned-copy
counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
dims the row for a 5s grace period (still fully interactive) before
it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
truncation), add icons to Open/New Library and Download/Update card
data, and drop the trailing "…" from the library buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
|
|
|
|
cursor: zoom-in;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
transition: transform var(--dur-fast), box-shadow var(--dur-fast);
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
}
|
|
|
|
|
|
.tile:hover .tile-img {
|
|
|
|
|
|
transform: translateY(-3px);
|
|
|
|
|
|
box-shadow: var(--shadow-md);
|
|
|
|
|
|
}
|
2026-08-22 19:14:27 -05:00
|
|
|
|
/* Horizontal view (see `CardTile`'s `rotated` prop): swaps the tile to a
|
|
|
|
|
|
landscape footprint via `aspect-ratio` — the width still comes from the
|
|
|
|
|
|
grid column, so no viewport math is needed here, unlike the click-preview
|
|
|
|
|
|
modal's rotated case. `container-type: size` lets the actual (still
|
|
|
|
|
|
portrait, pre-rotation) `<img>` inside reference that computed box via
|
|
|
|
|
|
`cqw`/`cqh` to size itself as the exact transpose, so rotating it 90° fills
|
|
|
|
|
|
the wrapper exactly. */
|
|
|
|
|
|
.tile-img-wrap-rotated {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
aspect-ratio: 85 / 61;
|
|
|
|
|
|
container-type: size;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
border-radius: var(--radius-card);
|
|
|
|
|
|
box-shadow: var(--shadow-sm);
|
|
|
|
|
|
transition: transform var(--dur-fast), box-shadow var(--dur-fast);
|
|
|
|
|
|
}
|
|
|
|
|
|
.tile:hover .tile-img-wrap-rotated {
|
|
|
|
|
|
transform: translateY(-3px);
|
|
|
|
|
|
box-shadow: var(--shadow-md);
|
|
|
|
|
|
}
|
|
|
|
|
|
.tile-img-rotated {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 50%;
|
|
|
|
|
|
left: 50%;
|
|
|
|
|
|
width: 100cqh;
|
|
|
|
|
|
height: 100cqw;
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
cursor: zoom-in;
|
|
|
|
|
|
transform: translate(-50%, -50%) rotate(90deg);
|
|
|
|
|
|
}
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
.tile-noimg {
|
|
|
|
|
|
aspect-ratio: 488 / 680;
|
|
|
|
|
|
border: 1px solid var(--border);
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
border-radius: var(--radius-card);
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
background: var(--bg-panel);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
padding: 14px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
.tile-corner {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 8px;
|
|
|
|
|
|
right: 8px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 6px;
|
2026-07-07 21:35:09 -05:00
|
|
|
|
/* The card art's own mana cost prints in this corner — only cover it while
|
|
|
|
|
|
the tile is actually being interacted with. */
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transition: opacity 0.12s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.tile:hover .tile-corner,
|
|
|
|
|
|
.tile:focus-within .tile-corner {
|
|
|
|
|
|
opacity: 1;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
}
|
2026-07-08 08:20:09 -05:00
|
|
|
|
.tile-corner-left {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 8px;
|
|
|
|
|
|
left: 8px;
|
|
|
|
|
|
/* Same reasoning as .tile-corner: don't cover the card name unless the
|
|
|
|
|
|
tile is being interacted with — except once selected, where the
|
|
|
|
|
|
checkbox should stay visible so the selection is visible at a glance. */
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transition: opacity 0.12s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.tile:hover .tile-corner-left,
|
|
|
|
|
|
.tile:focus-within .tile-corner-left,
|
|
|
|
|
|
.tile-selected .tile-corner-left {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
}
|
2026-07-14 21:32:25 -05:00
|
|
|
|
.tile-corner-bottom-right {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
bottom: 8px;
|
|
|
|
|
|
right: 8px;
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transition: opacity 0.12s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.tile:hover .tile-corner-bottom-right,
|
|
|
|
|
|
.tile:focus-within .tile-corner-bottom-right {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
}
|
2026-07-20 00:21:22 -05:00
|
|
|
|
.tile-corner-bottom-left {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
bottom: 8px;
|
|
|
|
|
|
left: 8px;
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transition: opacity 0.12s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.tile:hover .tile-corner-bottom-left,
|
|
|
|
|
|
.tile:focus-within .tile-corner-bottom-left {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
}
|
2026-07-08 08:20:09 -05:00
|
|
|
|
.tile-selected .tile-img {
|
|
|
|
|
|
box-shadow: 0 0 0 3px var(--accent), var(--shadow-md);
|
|
|
|
|
|
}
|
|
|
|
|
|
.tile-select {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
width: 26px;
|
|
|
|
|
|
height: 26px;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.78);
|
|
|
|
|
|
backdrop-filter: blur(2px);
|
|
|
|
|
|
}
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
.tile-badge {
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.78);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
padding: 2px 9px;
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 13px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
backdrop-filter: blur(2px);
|
|
|
|
|
|
}
|
|
|
|
|
|
.tile-badge.warn {
|
|
|
|
|
|
background: var(--warn);
|
|
|
|
|
|
color: #241700;
|
|
|
|
|
|
}
|
|
|
|
|
|
.tile-footer {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
2026-10-16 21:14:43 -05:00
|
|
|
|
/* Inputs keep an intrinsic ~160px width that would overflow tiles narrowed
|
|
|
|
|
|
by the per-row control; let them shrink with the tile instead. */
|
|
|
|
|
|
.tile-footer input {
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
|
Toolbar controls: group-by + view toggle
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
.group-by {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 7px;
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 13px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
}
|
2026-07-22 20:55:17 -05:00
|
|
|
|
.sort-dir-btn {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
width: 30px;
|
|
|
|
|
|
height: 30px;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
|
}
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
.view-toggle {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
background: var(--bg-input);
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
.view-toggle button {
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: 0;
|
|
|
|
|
|
background: transparent;
|
2026-07-07 21:35:09 -05:00
|
|
|
|
font-size: 13px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
padding: 5px 12px;
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
.view-toggle button:hover {
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.04);
|
|
|
|
|
|
}
|
|
|
|
|
|
.view-toggle button.active {
|
|
|
|
|
|
background: var(--accent);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
2026-10-16 21:14:43 -05:00
|
|
|
|
.view-toggle button:disabled {
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
opacity: 0.4;
|
|
|
|
|
|
cursor: default;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Tiles-per-row stepper (see `TileColumnsControl`): same segmented look as
|
|
|
|
|
|
the List/Tiles toggle, with a fixed-width count between − and +. */
|
|
|
|
|
|
.tile-columns button {
|
|
|
|
|
|
padding: 5px 9px;
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
}
|
|
|
|
|
|
.tile-columns .tile-columns-count {
|
|
|
|
|
|
min-width: 2.2em;
|
|
|
|
|
|
padding-left: 4px;
|
|
|
|
|
|
padding-right: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.tile-columns .tile-columns-count:disabled {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
}
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
Decks tab: a deck list page, with the builder on its own screen
The deck list is no longer a sidebar pinned beside the builder; on every
screen size the Decks tab opens to a deck list page (DeckList), and picking
a deck opens its builder full-width with an "All decks" back button. The
list shows each deck as an art tile (or a compact row, toggleable) with its
colors, size against its format, owned %, cost and legality, and can be
searched, filtered by format and sorted (recently opened, name, format,
size, cost, owned %). Rename/duplicate/export/delete live in each deck's
menu and beside the builder's title; New deck now asks for a format too,
defaulting to the last one used.
A deck's cover art is the commander, else its priciest nonland, unless a
card is chosen with "Use as cover" in the card preview. The cover is stored
in decks.cover_oracle_id (migration 0007) and synced as DeckDoc.cover; a
document carrying one is version 3, so the sync server must be updated
before clients publish covers. The list's stats come from one
all_deck_contents query, summarised with the builder's own analysis code,
now in deckAnalysis.ts.
Closes #9
Closes #11
Closes #12
2026-10-25 15:04:41 -05:00
|
|
|
|
Deck list (the Decks tab's front page)
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
Decks tab: a deck list page, with the builder on its own screen
The deck list is no longer a sidebar pinned beside the builder; on every
screen size the Decks tab opens to a deck list page (DeckList), and picking
a deck opens its builder full-width with an "All decks" back button. The
list shows each deck as an art tile (or a compact row, toggleable) with its
colors, size against its format, owned %, cost and legality, and can be
searched, filtered by format and sorted (recently opened, name, format,
size, cost, owned %). Rename/duplicate/export/delete live in each deck's
menu and beside the builder's title; New deck now asks for a format too,
defaulting to the last one used.
A deck's cover art is the commander, else its priciest nonland, unless a
card is chosen with "Use as cover" in the card preview. The cover is stored
in decks.cover_oracle_id (migration 0007) and synced as DeckDoc.cover; a
document carrying one is version 3, so the sync server must be updated
before clients publish covers. The list's stats come from one
all_deck_contents query, summarised with the builder's own analysis code,
now in deckAnalysis.ts.
Closes #9
Closes #11
Closes #12
2026-10-25 15:04:41 -05:00
|
|
|
|
.deck-gallery-toolbar {
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
display: flex;
|
Decks tab: a deck list page, with the builder on its own screen
The deck list is no longer a sidebar pinned beside the builder; on every
screen size the Decks tab opens to a deck list page (DeckList), and picking
a deck opens its builder full-width with an "All decks" back button. The
list shows each deck as an art tile (or a compact row, toggleable) with its
colors, size against its format, owned %, cost and legality, and can be
searched, filtered by format and sorted (recently opened, name, format,
size, cost, owned %). Rename/duplicate/export/delete live in each deck's
menu and beside the builder's title; New deck now asks for a format too,
defaulting to the last one used.
A deck's cover art is the commander, else its priciest nonland, unless a
card is chosen with "Use as cover" in the card preview. The cover is stored
in decks.cover_oracle_id (migration 0007) and synced as DeckDoc.cover; a
document carrying one is version 3, so the sync server must be updated
before clients publish covers. The list's stats come from one
all_deck_contents query, summarised with the builder's own analysis code,
now in deckAnalysis.ts.
Closes #9
Closes #11
Closes #12
2026-10-25 15:04:41 -05:00
|
|
|
|
align-items: center;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: var(--space-3);
|
|
|
|
|
|
margin-bottom: var(--space-5);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-gallery-toolbar h2 {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-gallery-search {
|
|
|
|
|
|
width: 260px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-gallery-controls,
|
|
|
|
|
|
.deck-gallery-create {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.deck-gallery-empty {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
padding: var(--space-8) var(--space-5);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-gallery-empty h3 {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-gallery-empty .hint {
|
|
|
|
|
|
margin: 0 0 var(--space-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-gallery-empty-icon svg {
|
|
|
|
|
|
width: 36px;
|
|
|
|
|
|
height: 36px;
|
|
|
|
|
|
color: var(--accent-quiet);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Tiles: an art banner with the deck's name on it, stats below. */
|
|
|
|
|
|
.deck-gallery-grid {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
|
|
|
|
|
gap: var(--space-4);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
background: var(--bg-panel);
|
|
|
|
|
|
border: 1px solid var(--border-soft);
|
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
|
box-shadow: var(--panel-highlight);
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: transform 0.15s, border-color 0.15s, box-shadow 0.15s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile:hover {
|
|
|
|
|
|
transform: translateY(-2px);
|
|
|
|
|
|
border-color: var(--accent-glow);
|
|
|
|
|
|
box-shadow: var(--panel-highlight), var(--shadow-md);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile:focus-visible,
|
|
|
|
|
|
.deck-row:focus-visible {
|
|
|
|
|
|
outline: 2px solid var(--accent);
|
|
|
|
|
|
outline-offset: 2px;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* The hover lift makes each tile its own stacking context, so the one whose
|
|
|
|
|
|
menu is open has to sit above its neighbours or they'd cover the menu. */
|
|
|
|
|
|
.deck-tile.menu-open,
|
|
|
|
|
|
.deck-row.menu-open {
|
|
|
|
|
|
z-index: 5;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile-cover {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
aspect-ratio: 16 / 9;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile-art {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
inset: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile-art img,
|
|
|
|
|
|
.deck-row-art img {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile-art img {
|
|
|
|
|
|
transition: transform 0.4s ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile:hover .deck-tile-art img {
|
|
|
|
|
|
transform: scale(1.04);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile-art,
|
|
|
|
|
|
.deck-row-art {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
background: repeating-linear-gradient(-45deg, var(--bg-input) 0 10px, var(--bg-hover) 10px 20px);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-art-empty {
|
|
|
|
|
|
font-size: 44px;
|
|
|
|
|
|
font-weight: 800;
|
|
|
|
|
|
color: var(--faint);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Darkens the top (for the badge + menu) and, more, the bottom (for the
|
|
|
|
|
|
name), so both stay legible over any art. */
|
|
|
|
|
|
.deck-tile-scrim {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
inset: 0;
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
background: linear-gradient(
|
|
|
|
|
|
to bottom,
|
|
|
|
|
|
rgba(12, 10, 8, 0.45) 0%,
|
|
|
|
|
|
rgba(12, 10, 8, 0) 28%,
|
|
|
|
|
|
rgba(12, 10, 8, 0) 45%,
|
|
|
|
|
|
rgba(12, 10, 8, 0.92) 100%
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile-heading {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: var(--space-3);
|
|
|
|
|
|
right: var(--space-3);
|
|
|
|
|
|
bottom: var(--space-2);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: flex-end;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile-name {
|
|
|
|
|
|
font-size: 17px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
line-height: 1.25;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
|
|
|
|
|
|
overflow-wrap: anywhere;
|
|
|
|
|
|
display: -webkit-box;
|
|
|
|
|
|
-webkit-line-clamp: 2;
|
|
|
|
|
|
-webkit-box-orient: vertical;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile-heading .pips {
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
margin-bottom: 2px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile-cover .deck-legality {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: var(--space-2);
|
|
|
|
|
|
left: var(--space-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile-body {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
padding: var(--space-3) var(--space-3) var(--space-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile-sub {
|
|
|
|
|
|
font-size: var(--fs-caption);
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile-meta {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
font-size: var(--fs-caption);
|
|
|
|
|
|
min-height: 22px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile-meta .deck-chip,
|
|
|
|
|
|
.deck-row-stats .deck-chip {
|
|
|
|
|
|
margin-right: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile-foot {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
font-size: var(--fs-caption);
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile-foot span:last-child,
|
|
|
|
|
|
.deck-row-cost {
|
|
|
|
|
|
color: var(--text);
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.deck-size {
|
|
|
|
|
|
font-size: var(--fs-caption);
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: var(--text);
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-size-target {
|
|
|
|
|
|
color: var(--faint);
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-size.done {
|
|
|
|
|
|
color: var(--success);
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
}
|
Decks tab: a deck list page, with the builder on its own screen
The deck list is no longer a sidebar pinned beside the builder; on every
screen size the Decks tab opens to a deck list page (DeckList), and picking
a deck opens its builder full-width with an "All decks" back button. The
list shows each deck as an art tile (or a compact row, toggleable) with its
colors, size against its format, owned %, cost and legality, and can be
searched, filtered by format and sorted (recently opened, name, format,
size, cost, owned %). Rename/duplicate/export/delete live in each deck's
menu and beside the builder's title; New deck now asks for a format too,
defaulting to the last one used.
A deck's cover art is the commander, else its priciest nonland, unless a
card is chosen with "Use as cover" in the card preview. The cover is stored
in decks.cover_oracle_id (migration 0007) and synced as DeckDoc.cover; a
document carrying one is version 3, so the sync server must be updated
before clients publish covers. The list's stats come from one
all_deck_contents query, summarised with the builder's own analysis code,
now in deckAnalysis.ts.
Closes #9
Closes #11
Closes #12
2026-10-25 15:04:41 -05:00
|
|
|
|
.deck-size.warn {
|
|
|
|
|
|
color: var(--warn);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.deck-owned-bar {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
height: 4px;
|
|
|
|
|
|
border-radius: var(--radius-pill);
|
|
|
|
|
|
background: var(--bg-input);
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-owned-bar > span {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
background: var(--success);
|
|
|
|
|
|
border-radius: inherit;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.deck-legality {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-1);
|
|
|
|
|
|
font-size: var(--fs-label);
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile .deck-legality {
|
|
|
|
|
|
padding: 2px var(--space-2);
|
|
|
|
|
|
border-radius: var(--radius-pill);
|
|
|
|
|
|
background: rgba(12, 10, 8, 0.72);
|
|
|
|
|
|
backdrop-filter: blur(2px);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-legality.legal {
|
|
|
|
|
|
color: var(--success);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-legality.illegal {
|
|
|
|
|
|
color: var(--warn);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* The ⋯ menu: over the tile's top-right corner, or at a row's end. */
|
|
|
|
|
|
.deck-menu {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile > .deck-menu {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: var(--space-2);
|
|
|
|
|
|
right: var(--space-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-menu-list {
|
|
|
|
|
|
left: auto;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
min-width: 170px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-menu-list > button.danger-text {
|
|
|
|
|
|
color: var(--danger);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Where there's a hover, the tile's menu waits for it; on touch it's always
|
|
|
|
|
|
showing, since there's nothing to reveal it. */
|
|
|
|
|
|
@media (hover: hover) {
|
|
|
|
|
|
.deck-tile > .deck-menu {
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transition: opacity 0.12s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-tile:hover > .deck-menu,
|
|
|
|
|
|
.deck-tile:focus-within > .deck-menu,
|
|
|
|
|
|
.deck-tile.menu-open > .deck-menu {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Rows: the compact alternative, more decks per screen. */
|
|
|
|
|
|
.deck-gallery-rows {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-row {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-3);
|
|
|
|
|
|
padding: var(--space-2) var(--space-3) var(--space-2) var(--space-2);
|
|
|
|
|
|
background: var(--bg-panel);
|
|
|
|
|
|
border: 1px solid var(--border-soft);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
box-shadow: var(--panel-highlight);
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: border-color 0.12s, background 0.12s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-row:hover {
|
|
|
|
|
|
border-color: var(--accent-glow);
|
|
|
|
|
|
background: var(--bg-hover);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-row-art {
|
|
|
|
|
|
width: 72px;
|
|
|
|
|
|
height: 52px;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
flex-shrink: 0;
|
Decks tab: a deck list page, with the builder on its own screen
The deck list is no longer a sidebar pinned beside the builder; on every
screen size the Decks tab opens to a deck list page (DeckList), and picking
a deck opens its builder full-width with an "All decks" back button. The
list shows each deck as an art tile (or a compact row, toggleable) with its
colors, size against its format, owned %, cost and legality, and can be
searched, filtered by format and sorted (recently opened, name, format,
size, cost, owned %). Rename/duplicate/export/delete live in each deck's
menu and beside the builder's title; New deck now asks for a format too,
defaulting to the last one used.
A deck's cover art is the commander, else its priciest nonland, unless a
card is chosen with "Use as cover" in the card preview. The cover is stored
in decks.cover_oracle_id (migration 0007) and synced as DeckDoc.cover; a
document carrying one is version 3, so the sync server must be updated
before clients publish covers. The list's stats come from one
all_deck_contents query, summarised with the builder's own analysis code,
now in deckAnalysis.ts.
Closes #9
Closes #11
Closes #12
2026-10-25 15:04:41 -05:00
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-row-art .deck-art-empty {
|
|
|
|
|
|
font-size: 22px;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
}
|
Decks tab: a deck list page, with the builder on its own screen
The deck list is no longer a sidebar pinned beside the builder; on every
screen size the Decks tab opens to a deck list page (DeckList), and picking
a deck opens its builder full-width with an "All decks" back button. The
list shows each deck as an art tile (or a compact row, toggleable) with its
colors, size against its format, owned %, cost and legality, and can be
searched, filtered by format and sorted (recently opened, name, format,
size, cost, owned %). Rename/duplicate/export/delete live in each deck's
menu and beside the builder's title; New deck now asks for a format too,
defaulting to the last one used.
A deck's cover art is the commander, else its priciest nonland, unless a
card is chosen with "Use as cover" in the card preview. The cover is stored
in decks.cover_oracle_id (migration 0007) and synced as DeckDoc.cover; a
document carrying one is version 3, so the sync server must be updated
before clients publish covers. The list's stats come from one
all_deck_contents query, summarised with the builder's own analysis code,
now in deckAnalysis.ts.
Closes #9
Closes #11
Closes #12
2026-10-25 15:04:41 -05:00
|
|
|
|
.deck-row-main {
|
2026-08-02 16:10:25 -05:00
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-width: 0;
|
Decks tab: a deck list page, with the builder on its own screen
The deck list is no longer a sidebar pinned beside the builder; on every
screen size the Decks tab opens to a deck list page (DeckList), and picking
a deck opens its builder full-width with an "All decks" back button. The
list shows each deck as an art tile (or a compact row, toggleable) with its
colors, size against its format, owned %, cost and legality, and can be
searched, filtered by format and sorted (recently opened, name, format,
size, cost, owned %). Rename/duplicate/export/delete live in each deck's
menu and beside the builder's title; New deck now asks for a format too,
defaulting to the last one used.
A deck's cover art is the commander, else its priciest nonland, unless a
card is chosen with "Use as cover" in the card preview. The cover is stored
in decks.cover_oracle_id (migration 0007) and synced as DeckDoc.cover; a
document carrying one is version 3, so the sync server must be updated
before clients publish covers. The list's stats come from one
all_deck_contents query, summarised with the builder's own analysis code,
now in deckAnalysis.ts.
Closes #9
Closes #11
Closes #12
2026-10-25 15:04:41 -05:00
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 2px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-row-name {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-row-name .card-name {
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-row-name .pips {
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-row-sub {
|
|
|
|
|
|
font-size: var(--fs-caption);
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-row-stats {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-4);
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
font-size: var(--fs-caption);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-row-owned {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-row-owned .deck-owned-bar {
|
|
|
|
|
|
width: 56px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-row-owned > span:last-child {
|
|
|
|
|
|
text-align: right;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Fixed widths, so each stat lines up in a column down the list. */
|
|
|
|
|
|
.deck-row-stats .deck-size {
|
|
|
|
|
|
min-width: 4.5em;
|
|
|
|
|
|
text-align: right;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-row-owned > span:last-child {
|
|
|
|
|
|
min-width: 4ch;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-row-cost {
|
|
|
|
|
|
min-width: 4.5em;
|
|
|
|
|
|
text-align: right;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-row .deck-legality {
|
|
|
|
|
|
min-width: 1em;
|
|
|
|
|
|
font-size: var(--fs-body);
|
2026-08-02 16:10:25 -05:00
|
|
|
|
}
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
|
Decks tab: a deck list page, with the builder on its own screen
The deck list is no longer a sidebar pinned beside the builder; on every
screen size the Decks tab opens to a deck list page (DeckList), and picking
a deck opens its builder full-width with an "All decks" back button. The
list shows each deck as an art tile (or a compact row, toggleable) with its
colors, size against its format, owned %, cost and legality, and can be
searched, filtered by format and sorted (recently opened, name, format,
size, cost, owned %). Rename/duplicate/export/delete live in each deck's
menu and beside the builder's title; New deck now asks for a format too,
defaulting to the last one used.
A deck's cover art is the commander, else its priciest nonland, unless a
card is chosen with "Use as cover" in the card preview. The cover is stored
in decks.cover_oracle_id (migration 0007) and synced as DeckDoc.cover; a
document carrying one is version 3, so the sync server must be updated
before clients publish covers. The list's stats come from one
all_deck_contents query, summarised with the builder's own analysis code,
now in deckAnalysis.ts.
Closes #9
Closes #11
Closes #12
2026-10-25 15:04:41 -05:00
|
|
|
|
/* The builder's way back to the deck list. */
|
|
|
|
|
|
.deck-builder-back {
|
|
|
|
|
|
background: none;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
margin-bottom: var(--space-2);
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-builder-back:hover {
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
background: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-builder-title {
|
|
|
|
|
|
gap: var(--space-1);
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-builder-title h2 {
|
|
|
|
|
|
margin-right: var(--space-2);
|
|
|
|
|
|
overflow-wrap: anywhere;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
|
Deck metadata (format + commander)
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
/* Surface from `.panel`; layout only here. */
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
.deck-meta {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
align-items: flex-start;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
gap: var(--space-5);
|
2026-07-20 00:21:22 -05:00
|
|
|
|
}
|
|
|
|
|
|
/* Separates the deck-level info panels (commander, format, analysis,
|
|
|
|
|
|
legality) from the card-management controls + lists below. */
|
|
|
|
|
|
.deck-section-divider {
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-top: 1px solid var(--border-soft);
|
|
|
|
|
|
margin: 4px 0 18px;
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
}
|
2026-07-21 21:04:36 -05:00
|
|
|
|
|
|
|
|
|
|
/* The four top-of-page info panels (commander, meta, analysis, legality).
|
|
|
|
|
|
Single column matches the existing top-to-bottom reading order on
|
|
|
|
|
|
narrow/portrait viewports; on wide viewports the compact panels move
|
|
|
|
|
|
into a side column so the analysis panel (the one panel that benefits
|
|
|
|
|
|
from extra width) gets the rest of the row instead of everything
|
|
|
|
|
|
stretching out with wasted horizontal space. grid-template-areas (not
|
|
|
|
|
|
flex wrapping) is what lets the two-column arrangement reorder visually
|
|
|
|
|
|
without touching DOM order. */
|
|
|
|
|
|
.deck-top-grid {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
|
grid-template-areas:
|
|
|
|
|
|
"commander"
|
|
|
|
|
|
"meta"
|
|
|
|
|
|
"stats"
|
|
|
|
|
|
"legality";
|
|
|
|
|
|
gap: 16px;
|
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-top-commander {
|
|
|
|
|
|
grid-area: commander;
|
2026-10-23 22:29:56 -05:00
|
|
|
|
/* One hero per commander: a partner pair stacks. */
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 16px;
|
2026-07-21 21:04:36 -05:00
|
|
|
|
}
|
|
|
|
|
|
.deck-top-meta {
|
|
|
|
|
|
grid-area: meta;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-top-stats {
|
|
|
|
|
|
grid-area: stats;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-top-legality {
|
|
|
|
|
|
grid-area: legality;
|
|
|
|
|
|
}
|
|
|
|
|
|
@media (min-width: 1100px) {
|
|
|
|
|
|
.deck-top-grid {
|
|
|
|
|
|
grid-template-columns: minmax(360px, 560px) 1fr;
|
|
|
|
|
|
grid-template-areas:
|
|
|
|
|
|
"commander stats"
|
|
|
|
|
|
"meta stats"
|
2026-10-23 23:18:27 -05:00
|
|
|
|
"legality stats"
|
|
|
|
|
|
". stats";
|
|
|
|
|
|
/* The last row takes whatever height the analysis panel adds, so the
|
|
|
|
|
|
left-hand panels stack together instead of spreading out to match. */
|
|
|
|
|
|
grid-template-rows: auto auto auto 1fr;
|
2026-07-21 21:04:36 -05:00
|
|
|
|
align-items: start;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
tile/list view toggle, hover card previews, deck format + commander support,
per-card location field, grouping by type/subtype/CMC/color, localStorage
persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00
|
|
|
|
.commander-box {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
min-width: 280px;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
}
|
2026-07-08 23:54:47 -05:00
|
|
|
|
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
/* Prominent commander card display, above the fold on a Commander/Brawl deck.
|
|
|
|
|
|
Surface from `.panel`; layout only here. */
|
2026-07-08 23:54:47 -05:00
|
|
|
|
.commander-hero {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
gap: var(--space-4);
|
2026-07-08 23:54:47 -05:00
|
|
|
|
}
|
|
|
|
|
|
.commander-hero-img {
|
|
|
|
|
|
width: 84px;
|
|
|
|
|
|
height: 117px;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
cursor: zoom-in;
|
|
|
|
|
|
}
|
|
|
|
|
|
.commander-hero-noimg {
|
|
|
|
|
|
width: 84px;
|
|
|
|
|
|
height: 117px;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
border: 1px dashed var(--border);
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
padding: 8px;
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
.commander-hero-info {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 5px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.commander-hero-label {
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
font-size: var(--fs-micro);
|
2026-07-08 23:54:47 -05:00
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
text-transform: uppercase;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
letter-spacing: var(--tracking-label);
|
|
|
|
|
|
/* Quiet accent keeps a hint of brand on the hero without competing with
|
|
|
|
|
|
primary-action terracotta elsewhere. */
|
|
|
|
|
|
color: var(--accent-quiet);
|
2026-07-08 23:54:47 -05:00
|
|
|
|
}
|
|
|
|
|
|
.commander-hero-name {
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
font-size: var(--fs-display);
|
2026-07-08 23:54:47 -05:00
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
}
|
|
|
|
|
|
.commander-hero-type {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
/* Per-deck analysis panel. Surface from `.panel`. */
|
2026-07-08 23:54:47 -05:00
|
|
|
|
.deck-stats-head {
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
margin-bottom: var(--space-3);
|
2026-07-08 23:54:47 -05:00
|
|
|
|
}
|
|
|
|
|
|
.deck-stats-body {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 24px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-stats-numbers {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 8px;
|
2026-10-23 23:18:27 -05:00
|
|
|
|
flex: 1 1 240px;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* The analysis tiles span the panel above the bars and curve. They size to
|
|
|
|
|
|
their value (a "$1,234" cost is wider than a card count) and share out any
|
|
|
|
|
|
spare width, wrapping on narrow screens rather than squeezing a value past
|
|
|
|
|
|
its tile's edges. */
|
|
|
|
|
|
.deck-stats .stat-tiles-row {
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-stats .stat-tile {
|
|
|
|
|
|
flex: 1 0 auto;
|
|
|
|
|
|
padding: 8px 10px;
|
2026-07-08 23:54:47 -05:00
|
|
|
|
}
|
|
|
|
|
|
.deck-stats-curve {
|
2026-10-23 23:18:27 -05:00
|
|
|
|
/* Shares the row with the color bars, or takes the full width once they
|
|
|
|
|
|
wrap onto separate lines. */
|
|
|
|
|
|
flex: 1 1 240px;
|
|
|
|
|
|
min-width: 0;
|
2026-07-08 23:54:47 -05:00
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-stats-curve-label {
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
font-size: var(--fs-micro);
|
2026-07-08 23:54:47 -05:00
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
letter-spacing: 0.05em;
|
|
|
|
|
|
color: var(--faint);
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
margin-bottom: var(--space-1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
|
Responsive groundwork
|
|
|
|
|
|
Desktop (the primary target) is unchanged above; these breakpoints keep
|
|
|
|
|
|
the layout usable as the window narrows and lay the foundation for a
|
Decks tab: a deck list page, with the builder on its own screen
The deck list is no longer a sidebar pinned beside the builder; on every
screen size the Decks tab opens to a deck list page (DeckList), and picking
a deck opens its builder full-width with an "All decks" back button. The
list shows each deck as an art tile (or a compact row, toggleable) with its
colors, size against its format, owned %, cost and legality, and can be
searched, filtered by format and sorted (recently opened, name, format,
size, cost, owned %). Rename/duplicate/export/delete live in each deck's
menu and beside the builder's title; New deck now asks for a format too,
defaulting to the last one used.
A deck's cover art is the commander, else its priciest nonland, unless a
card is chosen with "Use as cover" in the card preview. The cover is stored
in decks.cover_oracle_id (migration 0007) and synced as DeckDoc.cover; a
document carrying one is version 3, so the sync server must be updated
before clients publish covers. The list's stats come from one
all_deck_contents query, summarised with the builder's own analysis code,
now in deckAnalysis.ts.
Closes #9
Closes #11
Closes #12
2026-10-25 15:04:41 -05:00
|
|
|
|
future mobile port. Breakpoint: 720 (compact).
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
/* Compact: tighten page gutters and collapse the nav rail to an icon strip
|
2026-08-02 16:10:25 -05:00
|
|
|
|
the left rail's job moves to .bottom-nav below instead, the customary
|
|
|
|
|
|
placement on Android). */
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
@media (max-width: 720px) {
|
|
|
|
|
|
.content {
|
|
|
|
|
|
padding: var(--space-4);
|
2026-08-02 16:10:25 -05:00
|
|
|
|
/* Clear the fixed bottom nav, or its last row of content is hidden
|
|
|
|
|
|
behind it — the safe-area inset is already baked into the nav's own
|
|
|
|
|
|
height, so it isn't added again here. */
|
|
|
|
|
|
padding-bottom: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom) + var(--space-4));
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
}
|
|
|
|
|
|
.nav-rail {
|
2026-08-02 16:10:25 -05:00
|
|
|
|
display: none;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
}
|
2026-08-02 16:10:25 -05:00
|
|
|
|
.bottom-nav {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
}
|
|
|
|
|
|
.topbar {
|
|
|
|
|
|
padding-left: var(--space-3);
|
|
|
|
|
|
padding-right: var(--space-3);
|
|
|
|
|
|
/* padding-top (the safe-area inset) is untouched here — this must not
|
|
|
|
|
|
reintroduce the shorthand `padding: 0 ...` that was zeroing it out. */
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
}
|
2026-08-02 16:10:25 -05:00
|
|
|
|
/* The library path is the first thing to go when horizontal space is tight —
|
|
|
|
|
|
the library actions stay reachable. */
|
|
|
|
|
|
.path {
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
2026-08-02 16:10:25 -05:00
|
|
|
|
/* Deck view's header (name + Suggest/Export/Sample buttons) and several
|
|
|
|
|
|
panels below assume desktop width via `min-width` (280/220px) and a
|
|
|
|
|
|
non-wrapping `.row` — those don't fit a phone and forced the whole page
|
|
|
|
|
|
to scroll horizontally. Let everything wrap and drop the min-widths
|
|
|
|
|
|
instead. */
|
|
|
|
|
|
.row {
|
|
|
|
|
|
flex-wrap: wrap;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
}
|
2026-08-02 16:10:25 -05:00
|
|
|
|
.commander-box,
|
|
|
|
|
|
.deck-stats-numbers,
|
|
|
|
|
|
.deck-stats-curve {
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
Decks tab: a deck list page, with the builder on its own screen
The deck list is no longer a sidebar pinned beside the builder; on every
screen size the Decks tab opens to a deck list page (DeckList), and picking
a deck opens its builder full-width with an "All decks" back button. The
list shows each deck as an art tile (or a compact row, toggleable) with its
colors, size against its format, owned %, cost and legality, and can be
searched, filtered by format and sorted (recently opened, name, format,
size, cost, owned %). Rename/duplicate/export/delete live in each deck's
menu and beside the builder's title; New deck now asks for a format too,
defaulting to the last one used.
A deck's cover art is the commander, else its priciest nonland, unless a
card is chosen with "Use as cover" in the card preview. The cover is stored
in decks.cover_oracle_id (migration 0007) and synced as DeckDoc.cover; a
document carrying one is version 3, so the sync server must be updated
before clients publish covers. The list's stats come from one
all_deck_contents query, summarised with the builder's own analysis code,
now in deckAnalysis.ts.
Closes #9
Closes #11
Closes #12
2026-10-25 15:04:41 -05:00
|
|
|
|
/* Deck list toolbar: title and create buttons on top, then search, then
|
|
|
|
|
|
the filter/sort/view controls sharing a line. */
|
|
|
|
|
|
.deck-gallery-toolbar .spacer {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-gallery-toolbar h2 {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-gallery-create {
|
|
|
|
|
|
order: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-gallery-search {
|
|
|
|
|
|
order: 2;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-gallery-controls {
|
|
|
|
|
|
order: 3;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-gallery-controls select {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Rows put their stats on a second line, under the name. */
|
|
|
|
|
|
.deck-row {
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
row-gap: var(--space-1);
|
|
|
|
|
|
}
|
|
|
|
|
|
.deck-row-stats {
|
|
|
|
|
|
order: 3;
|
|
|
|
|
|
flex-basis: 100%;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: var(--space-3);
|
|
|
|
|
|
padding-left: calc(72px + var(--space-3));
|
|
|
|
|
|
}
|
2026-08-02 16:10:25 -05:00
|
|
|
|
/* `.deck-meta` puts the Format selector and the commander search box side
|
|
|
|
|
|
by side as flex-wrap row items — with `.commander-box`'s min-width now
|
|
|
|
|
|
0, it no longer wraps onto its own line, it just gets squeezed into
|
|
|
|
|
|
whatever sliver is left next to "Format", crushing the search results.
|
|
|
|
|
|
Stack them instead. */
|
|
|
|
|
|
.deck-meta {
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: stretch;
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
}
|
2026-08-02 16:10:25 -05:00
|
|
|
|
/* The Suggested Cards dialog: `.suggestion-actions`' two nowrap buttons
|
|
|
|
|
|
are flex-shrink:0, so they claimed their full fixed width no matter how
|
|
|
|
|
|
little room was left, crushing `.suggestion-main` down to almost
|
|
|
|
|
|
nothing — the name text then wrapped one word (sometimes one syllable)
|
|
|
|
|
|
per line instead of actually gaining any space. `flex-basis: 100%` on
|
|
|
|
|
|
`.suggestion-actions` forces it onto its own full-width line below the
|
|
|
|
|
|
thumbnail + name/reasons instead of competing with them for the same
|
|
|
|
|
|
line. */
|
|
|
|
|
|
.dialog-wide {
|
|
|
|
|
|
width: 96vw;
|
|
|
|
|
|
padding: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.suggestion-row {
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.suggestion-head {
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
row-gap: 2px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.suggestion-actions {
|
|
|
|
|
|
flex-basis: 100%;
|
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
|
margin-top: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.suggestion-actions button {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Clear the bottom nav the same way .content does, so the undo snackbar
|
|
|
|
|
|
doesn't land underneath it. */
|
|
|
|
|
|
.undo-bar {
|
|
|
|
|
|
bottom: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom) + var(--space-3));
|
|
|
|
|
|
}
|
|
|
|
|
|
/* The ingest bar's segmented layout assumes room for 4 side-by-side
|
|
|
|
|
|
stage labels — on a phone-width screen the tiny-weighted stages
|
|
|
|
|
|
("Index", "Indexing") can't fit theirs and it collides with its
|
|
|
|
|
|
neighbor's. Drop the per-segment labels (the header message above
|
|
|
|
|
|
already names the current stage in more detail) and let that header
|
|
|
|
|
|
message wrap instead of ellipsis-truncating, so status text like a
|
|
|
|
|
|
download's running byte count stays readable. */
|
|
|
|
|
|
.ingest-bar {
|
|
|
|
|
|
padding: var(--space-2) var(--space-3) var(--space-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
.ingest-bar-message {
|
|
|
|
|
|
white-space: normal;
|
|
|
|
|
|
overflow: visible;
|
|
|
|
|
|
text-overflow: clip;
|
|
|
|
|
|
}
|
|
|
|
|
|
.ingest-segment-label {
|
Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.
- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
--accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
select-all, count) into a workspace toolbar inside the filter panel,
so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
rail (stacks / icon-collapse at breakpoints), touch-target sizing.
Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Touch input (future mobile / touchscreen laptops): grow tap targets to a
|
|
|
|
|
|
comfortable size without inflating the compact mouse-driven desktop look. */
|
|
|
|
|
|
@media (pointer: coarse) {
|
|
|
|
|
|
button,
|
|
|
|
|
|
input,
|
|
|
|
|
|
select,
|
|
|
|
|
|
.nav-item {
|
|
|
|
|
|
min-height: var(--control-h);
|
|
|
|
|
|
}
|
|
|
|
|
|
input[type="checkbox"] {
|
|
|
|
|
|
width: 18px;
|
|
|
|
|
|
height: 18px;
|
|
|
|
|
|
}
|
2026-07-08 23:54:47 -05:00
|
|
|
|
}
|
2026-07-20 00:21:22 -05:00
|
|
|
|
|
Add a News tab: set releases and links to Wizards' articles
The app knew nothing about the game outside your own collection — no way to
see that a set drops next week, or that a Secret Lair was announced.
Two sources with very different reliability, kept apart so one failing can't
take the page down.
Releases come from Scryfall's documented /sets endpoint. Clicking a set fetches
its cards live rather than reading reference.db, so a set released or spoiled
since the last card-data download still works; ownership is joined locally by
oracle_id, and the same add-to-library stepper works there as anywhere else.
Articles have no supported source at all: Wizards publishes no RSS or Atom feed
and no public API, and their page is a Nuxt app whose CSS classes are per-build
hashes. So the app links out rather than reproducing anyone's writing, which
keeps the scraped surface to titles and hrefs. parse_articles keys on nothing
cosmetic — anchors pointing at /en/news/<category>/<slug>, taking the anchor's
own text as the title. A restyle can't break that; only a change to their URL
scheme could, and the fixture tests fail loudly when it does rather than the
page quietly going empty. Verified against a live capture: 15 articles, correct
titles and categories.
Two things the fixtures caught. Where an article is linked several times per
card, the first link is a "Learn More" button, so the longest text wins rather
than the first. And a *successful* fetch yielding zero articles is treated as a
failure, since that is the signature of their markup changing.
get_set_cards returns a full CardRef rather than the handful of fields the grid
needs, so the tab renders the same CardTile as the rest of the app and gets
click-to-preview instead of a lookalike that does nothing when clicked.
Both feeds cache to news-cache.json for six hours and fall back to whatever was
last seen, so opening the tab offline shows stale content with its age rather
than an error.
Also promotes the HTTP agent out of ingest.rs into a shared http module, so
timeouts and User-Agent can't drift between the ingester and anything else that
reaches the network. Adds tauri-plugin-opener for opening articles externally.
2026-08-07 23:35:50 -05:00
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
|
News
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
/* Releases and articles side by side on desktop; stacked once there isn't
|
|
|
|
|
|
room, which the ≤900px rule below handles. */
|
|
|
|
|
|
.news-columns {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: 1fr 1fr;
|
|
|
|
|
|
gap: var(--space-5);
|
|
|
|
|
|
align-items: start;
|
|
|
|
|
|
}
|
|
|
|
|
|
@media (max-width: 900px) {
|
|
|
|
|
|
.news-columns {
|
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.news-col {
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.news-col .group-header {
|
|
|
|
|
|
margin-top: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Both feeds are dense, uniform rows rather than the default result-list's
|
|
|
|
|
|
roomier search-result spacing. */
|
|
|
|
|
|
.set-list li,
|
|
|
|
|
|
.article-list li {
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-3);
|
|
|
|
|
|
padding: 8px 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* --- releases ------------------------------------------------------------
|
|
|
|
|
|
A fixed-width date gutter is what makes this scan as a timeline: the dates
|
|
|
|
|
|
form one column and every set name starts at the same x, regardless of how
|
|
|
|
|
|
long the date renders in the user's locale. */
|
|
|
|
|
|
.set-date {
|
|
|
|
|
|
flex: 0 0 52px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: flex-end;
|
|
|
|
|
|
line-height: 1.15;
|
|
|
|
|
|
}
|
|
|
|
|
|
.set-date-day {
|
|
|
|
|
|
font-size: var(--fs-caption);
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.set-date-year {
|
|
|
|
|
|
font-size: var(--fs-micro);
|
|
|
|
|
|
color: var(--faint);
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Not-yet-released sets get the accent, so "what's coming" is findable at a
|
|
|
|
|
|
glance without reading any dates. */
|
|
|
|
|
|
.set-row.upcoming .set-date-day {
|
|
|
|
|
|
color: var(--accent-hover);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Scryfall's set symbols are SVGs with dark ink baked in, so they disappear
|
|
|
|
|
|
against a dark panel — inverted rather than shipping our own icon set. */
|
|
|
|
|
|
.set-icon {
|
|
|
|
|
|
width: 24px;
|
|
|
|
|
|
height: 24px;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
filter: invert(1);
|
|
|
|
|
|
opacity: 0.62;
|
|
|
|
|
|
transition: opacity 0.12s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.set-row:hover .set-icon {
|
|
|
|
|
|
opacity: 0.95;
|
|
|
|
|
|
}
|
|
|
|
|
|
.set-row-main {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 1px;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.set-name {
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.set-row-meta {
|
|
|
|
|
|
font-size: var(--fs-micro);
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
text-transform: capitalize;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* --- articles ------------------------------------------------------------ */
|
|
|
|
|
|
|
|
|
|
|
|
/* Fixed width so every title starts at the same x. Variable-width chips left
|
|
|
|
|
|
the titles ragged, which read as misalignment rather than as structure.
|
|
|
|
|
|
The categories are a small closed set ("announcements" is the longest),
|
|
|
|
|
|
so a fixed slot is safe. */
|
|
|
|
|
|
.article-cat {
|
|
|
|
|
|
flex: 0 0 88px;
|
|
|
|
|
|
font-size: var(--fs-micro);
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
letter-spacing: 0.04em;
|
|
|
|
|
|
color: var(--faint);
|
|
|
|
|
|
text-align: right;
|
|
|
|
|
|
line-height: 1.3;
|
|
|
|
|
|
overflow-wrap: anywhere;
|
|
|
|
|
|
}
|
|
|
|
|
|
.article-list li:hover .article-cat {
|
|
|
|
|
|
color: var(--accent-quiet);
|
|
|
|
|
|
}
|
|
|
|
|
|
.article-title {
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
line-height: 1.35;
|
|
|
|
|
|
overflow-wrap: anywhere;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
2026-08-14 01:02:21 -05:00
|
|
|
|
|
|
|
|
|
|
/* Suggestion count inside a "narrow to" chip — how many suggestions that
|
|
|
|
|
|
specific tribe/keyword/archetype explains, so the most useful narrowing is
|
|
|
|
|
|
obvious before clicking. */
|
|
|
|
|
|
.filter-chip-count {
|
|
|
|
|
|
opacity: 0.55;
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
margin-left: 2px;
|
|
|
|
|
|
}
|
Spare/rarity filters, owned-only suggestions, and a Game night tab
Library filters:
- "Only spare copies" quick filter (Library): cards with more copies
owned than the decks' main boards use, the opposite of the ⚠
over-assignment warning. Rows show "N spare" while it's on.
- Rarity toggles (C/U/R/M) in the shared criteria panel, so Library,
Card Search and the set browser all get them. Any printing counts,
since the library tracks cards rather than printings; within a set
it's that set's printing. Arena-only printings don't count for a card
that also exists on paper, so a digital re-rarity can't make a paper
common read as rare.
Suggestions:
- "From my collection" toggle (remembered) narrows candidates to owned
cards before scoring and paging, so "Suggest more" keeps finding them.
- Each suggestion reports copies other decks already use, shown as
"N spare" / "all in other decks". suggest_cards now takes a
SuggestOptions struct instead of a bare `unusual` flag.
Game tab: a life counter for 2-6 players with tap/hold ±1/±10, a
running change bubble, poison and per-opponent commander damage (which
also comes off life), out-of-the-game markers, dice and a coin, and a
random first player. A seat's deck puts its commander's art behind the
life total. On phones the board fills the screen with the far row
turned to face across the table, and asks to keep the screen awake.
The game is per-device scratch state in localStorage, not synced.
Plain filter chips now show an accent background when active; before,
"Unusual picks" only changed its text colour.
2026-10-14 22:09:39 -05:00
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
|
Game night (life counter)
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
.game-setup {
|
|
|
|
|
|
max-width: 720px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-resume {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
gap: var(--space-3);
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
margin-bottom: var(--space-4);
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-setup-form {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: var(--space-4);
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-seats {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-seat {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-seat-color {
|
|
|
|
|
|
width: 28px;
|
|
|
|
|
|
height: 28px;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
border: 2px solid rgba(255, 255, 255, 0.15);
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-seat-name,
|
|
|
|
|
|
.game-seat-deck {
|
|
|
|
|
|
flex: 1 1 120px;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.game-board {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
min-height: 420px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
user-select: none;
|
|
|
|
|
|
-webkit-user-select: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-row {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-height: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-player {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
|
border: 2px solid var(--player-color);
|
|
|
|
|
|
background-color: var(--bg-panel);
|
|
|
|
|
|
background-color: color-mix(in srgb, var(--player-color) 28%, var(--bg-panel));
|
|
|
|
|
|
background-size: cover;
|
|
|
|
|
|
background-position: center 30%;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
/* Lets the life total size itself to the panel (cq units) — a 6-player
|
|
|
|
|
|
phone layout has panels a third the size of a 2-player one. */
|
|
|
|
|
|
container-type: size;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Darkens commander art enough for the numbers to read over it. */
|
|
|
|
|
|
.game-player::before {
|
|
|
|
|
|
content: "";
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
inset: 0;
|
|
|
|
|
|
background: linear-gradient(180deg, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.55));
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-player.rotated {
|
|
|
|
|
|
transform: rotate(180deg);
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-player.first {
|
|
|
|
|
|
box-shadow: 0 0 0 3px var(--warn), 0 0 24px rgba(224, 163, 74, 0.45);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Dims the panel rather than filtering it, which would also grey out the
|
|
|
|
|
|
"out" reason and the first-player highlight that need to stay legible. */
|
|
|
|
|
|
.game-player.out::before {
|
|
|
|
|
|
background: rgba(12, 11, 10, 0.78);
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-player.out .game-life {
|
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.game-life-half {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
z-index: 1;
|
|
|
|
|
|
width: 50%;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: 0;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
color: rgba(255, 255, 255, 0.35);
|
|
|
|
|
|
font-size: 28px;
|
|
|
|
|
|
font-weight: 300;
|
|
|
|
|
|
touch-action: manipulation;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-life-half:hover {
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.03);
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-life-half:active {
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.08);
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-life-half.minus {
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
|
padding-left: 8%;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-life-half.plus {
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
|
padding-right: 8%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.game-player-head {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 8px;
|
|
|
|
|
|
left: 12px;
|
|
|
|
|
|
right: 12px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
text-shadow: 0 1px 6px rgba(0, 0, 0, 0.7);
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-player-name {
|
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-player-deck {
|
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-life {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
font-size: 64px;
|
|
|
|
|
|
font-size: min(34cqw, 44cqh);
|
|
|
|
|
|
font-weight: 800;
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6);
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-delta {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
left: 100%;
|
|
|
|
|
|
margin-left: 6px;
|
|
|
|
|
|
padding: 1px 7px;
|
|
|
|
|
|
border-radius: var(--radius-pill);
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.5);
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
font-size: max(13px, min(8cqw, 11cqh));
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
text-shadow: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-delta.up {
|
|
|
|
|
|
color: #86e0a4;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-delta.down {
|
|
|
|
|
|
color: #ff9a86;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-out-label {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
margin-top: 6px;
|
|
|
|
|
|
padding: 2px 10px;
|
|
|
|
|
|
border-radius: var(--radius-pill);
|
|
|
|
|
|
background: var(--danger);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
letter-spacing: 0.06em;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-player-foot {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
bottom: 8px;
|
|
|
|
|
|
left: 8px;
|
|
|
|
|
|
right: 8px;
|
|
|
|
|
|
z-index: 2;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
/* Taps between the badges still reach the −/+ halves underneath. */
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-player-foot > button {
|
|
|
|
|
|
pointer-events: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-badge {
|
|
|
|
|
|
padding: 3px 9px;
|
|
|
|
|
|
border-radius: var(--radius-pill);
|
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.14);
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.5);
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-badge-btn:hover {
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.7);
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-badge.first {
|
|
|
|
|
|
border-color: transparent;
|
|
|
|
|
|
background: var(--warn);
|
|
|
|
|
|
color: #2b1d05;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.game-strip {
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-hub {
|
|
|
|
|
|
width: 36px;
|
|
|
|
|
|
height: 36px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
border: 1px solid var(--border-soft);
|
|
|
|
|
|
background: var(--bg-elev);
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-hub:hover {
|
|
|
|
|
|
background: var(--bg-hover);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.game-menu {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-menu button {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.game-counters {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-counter-row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-counter-heading {
|
|
|
|
|
|
margin-top: 6px;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-dot {
|
|
|
|
|
|
display: inline-block;
|
|
|
|
|
|
width: 10px;
|
|
|
|
|
|
height: 10px;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-stepper {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-stepper button {
|
|
|
|
|
|
min-width: 34px;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-stepper-value {
|
|
|
|
|
|
min-width: 34px;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.game-dice-buttons {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(4, 1fr);
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-dice-buttons button {
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-dice-result {
|
|
|
|
|
|
min-height: 96px;
|
|
|
|
|
|
margin: 12px 0 4px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-dice-latest {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
animation: game-dice-pop 0.22s ease-out;
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-dice-value {
|
|
|
|
|
|
font-size: 52px;
|
|
|
|
|
|
font-weight: 800;
|
|
|
|
|
|
line-height: 1.05;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-dice-history {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes game-dice-pop {
|
|
|
|
|
|
from {
|
|
|
|
|
|
transform: scale(0.6);
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
to {
|
|
|
|
|
|
transform: none;
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* On a phone the table takes the whole screen — topbar and bottom nav
|
|
|
|
|
|
included; the hub menu's "Leave the table" (or back) is the way out. */
|
|
|
|
|
|
@media (max-width: 720px) {
|
|
|
|
|
|
.game-board {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
inset: 0;
|
|
|
|
|
|
z-index: 300;
|
|
|
|
|
|
height: auto;
|
|
|
|
|
|
min-height: 0;
|
|
|
|
|
|
padding: calc(env(safe-area-inset-top) + 6px) 6px calc(env(safe-area-inset-bottom) + 6px);
|
|
|
|
|
|
background: var(--bg-app);
|
|
|
|
|
|
}
|
|
|
|
|
|
.game-life-half {
|
|
|
|
|
|
font-size: 22px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
Printing tracking for owned copies and deck art, and an outdated-card-data notice
Owned copies can now be pinned to a printing + finish (nonfoil / foil /
etched), activating the long-dormant library_card_copies table as groups
of identical copies. library_cards.quantity_owned stays authoritative:
groups are a subset of it, and the rest are "unspecified". Tracking more
copies than are unspecified grows ownership to fit; lowering ownership
trims the most recently added groups first. Migration 0006 adds the
group quantity, a unique (card, printing, finish) index, and
deck_cards.preferred_scryfall_id.
A deck slot can show a chosen printing's art, both faces from that
printing, falling back to the default when the id no longer resolves.
It survives quantity/board changes and is kept by duplicate_deck.
UI: the click preview's info tile gets a Printings section when opened
from the Library or a deck (copy groups with steppers, "Add a
printing…", and "Shown in this deck" with Change…/Default), backed by a
printing picker grid. The picker takes Escape in the capture phase so
it closes without closing the preview under it.
Sync: CardEntry.printings (keyed "<scryfall_id>/<finish>") and
DeckCardEntry.printing. Cards now merge field by field with groups per
key, so a quantity edit and a printing added on another device both
survive; merged groups are clamped deterministically. A document is
written as version 2 only once it holds printing data, so older builds
and servers refuse it instead of dropping the data and syncing the loss.
Decklists: "(SET) NUM", optionally "*F*"/"*E*", round-trips through deck
and library export/import. Library export writes a line per group plus
one for unspecified copies.
reference.db printings gain lang and released_at for picker labels and
ordering. When the installed card data lacks any column this build's
reference schema has (found by diffing against the schema applied
in-memory, so no list to maintain), a notice under the top bar offers
Update card data (and Import card data… on phones); "Not now" is
remembered until something more goes missing.
2026-10-16 21:06:17 -05:00
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
|
Printings: the click preview's per-printing controls, and the picker
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
.printing-row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.printing-row-thumb {
|
|
|
|
|
|
width: 28px;
|
|
|
|
|
|
height: 39px;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
|
background: var(--bg-elev);
|
|
|
|
|
|
}
|
|
|
|
|
|
.printing-row-label {
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
overflow-wrap: anywhere;
|
|
|
|
|
|
}
|
|
|
|
|
|
.printing-actions {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.printing-actions button {
|
|
|
|
|
|
padding: 2px 10px;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Above the click preview (450) it opens from. */
|
|
|
|
|
|
.printing-picker-backdrop {
|
|
|
|
|
|
z-index: 500;
|
|
|
|
|
|
}
|
|
|
|
|
|
.printing-grid {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(auto-fill, minmax(min(130px, 40vw), 1fr));
|
|
|
|
|
|
gap: var(--space-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
.printing-option {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.printing-option-art {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
border: 2px solid transparent;
|
|
|
|
|
|
border-radius: var(--radius-card);
|
|
|
|
|
|
background: none;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
.printing-option-art:hover {
|
|
|
|
|
|
border-color: var(--accent-hover);
|
|
|
|
|
|
background: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.printing-option.selected .printing-option-art {
|
|
|
|
|
|
border-color: var(--accent);
|
|
|
|
|
|
}
|
|
|
|
|
|
.printing-option-art img {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
aspect-ratio: 488 / 680;
|
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
|
border-radius: calc(var(--radius-card) - 2px);
|
|
|
|
|
|
}
|
|
|
|
|
|
.printing-option-label {
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
}
|
|
|
|
|
|
.printing-option-meta {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
|
}
|
2026-10-16 21:44:55 -05:00
|
|
|
|
|
|
|
|
|
|
/* Library switcher: the top bar's library label, as a menu button. Keeps
|
|
|
|
|
|
`.path`'s look (and its hiding on narrow screens). */
|
|
|
|
|
|
.library-switcher {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
overflow: visible;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
}
|
|
|
|
|
|
@media (max-width: 720px) {
|
|
|
|
|
|
/* Like `.path`: the Info tab's library list stands in for it on phones. */
|
|
|
|
|
|
.library-switcher {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.library-switcher-button {
|
|
|
|
|
|
flex: 1 1 auto;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
background: none;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
color: inherit;
|
|
|
|
|
|
font: inherit;
|
|
|
|
|
|
padding: 4px 10px;
|
|
|
|
|
|
text-align: left;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
.library-switcher-button:hover {
|
|
|
|
|
|
color: var(--text);
|
|
|
|
|
|
}
|
|
|
|
|
|
.library-switcher-label {
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.library-menu {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: calc(100% + var(--space-1));
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
z-index: 50;
|
|
|
|
|
|
min-width: 280px;
|
|
|
|
|
|
max-width: min(420px, 90vw);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
padding: var(--space-1);
|
|
|
|
|
|
background: var(--bg-elev);
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
box-shadow: var(--shadow-md);
|
|
|
|
|
|
font-family: inherit;
|
|
|
|
|
|
white-space: normal;
|
|
|
|
|
|
}
|
|
|
|
|
|
.library-menu > button {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: baseline;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
background: none;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
|
padding: var(--space-2);
|
|
|
|
|
|
color: var(--text);
|
|
|
|
|
|
text-align: left;
|
|
|
|
|
|
font-family: system-ui, sans-serif;
|
|
|
|
|
|
}
|
|
|
|
|
|
.library-menu > button:hover {
|
|
|
|
|
|
background: var(--bg-hover);
|
|
|
|
|
|
}
|
|
|
|
|
|
.library-menu hr {
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-top: 1px solid var(--border);
|
|
|
|
|
|
margin: var(--space-1) 0;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
.library-menu-check {
|
|
|
|
|
|
width: 1em;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
color: var(--accent-quiet);
|
|
|
|
|
|
}
|
|
|
|
|
|
.library-menu-name {
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: var(--text-strong);
|
|
|
|
|
|
}
|
|
|
|
|
|
.library-menu .hint {
|
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
|
padding-left: var(--space-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
.library-menu-error {
|
|
|
|
|
|
padding: var(--space-2);
|
|
|
|
|
|
font-family: system-ui, sans-serif;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* The Info tab's list of libraries. */
|
|
|
|
|
|
.library-list {
|
|
|
|
|
|
list-style: none;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
.library-list li {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-3);
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
padding: var(--space-2) var(--space-3);
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
}
|
|
|
|
|
|
.library-list li.active {
|
|
|
|
|
|
border-color: var(--accent-quiet);
|
|
|
|
|
|
}
|
|
|
|
|
|
.library-list-main {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 2px;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
flex: 1 1 160px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.library-list-actions {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.library-warn {
|
|
|
|
|
|
color: var(--warn);
|
|
|
|
|
|
}
|
|
|
|
|
|
.library-sync-check {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: var(--space-2);
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|