Files
mtgc/src/styles.css
garrettmills eefe1dc954 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

987 lines
20 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ============================================================
mtgc — design system
Terracotta accent (#C8553D) on a layered, Discord-inspired dark UI.
============================================================ */
:root {
/* Layered surfaces (darkest → lightest) */
--bg-rail: #16171a;
--bg-app: #1a1b1f;
--bg-topbar: #1e1f24;
--bg-content: #212228;
--bg-panel: #282a31;
--bg-input: #2f313a;
--bg-hover: #363841;
--bg-elev: #3a3c46;
--border: #34363e;
--border-soft: rgba(255, 255, 255, 0.055);
--text: #dbdee4;
--text-strong: #f5f6f8;
--muted: #9a9ea8;
--faint: #71757f;
/* Terracotta accent */
--accent: #c8553d;
--accent-hover: #d86e56;
--accent-active: #a8412d;
--accent-subtle: rgba(200, 85, 61, 0.15);
--accent-glow: rgba(200, 85, 61, 0.35);
/* Complementary / status */
--info: #46a6bd; /* teal complement */
--gold: #e0b24a;
--success: #4bb073;
--warn: #e0a34a;
--danger: #d8503f;
--radius: 8px;
--radius-lg: 12px;
--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);
font-size: 14px;
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 {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--bg-input);
color: var(--text);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 7px 13px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
font-family: inherit;
transition: background 0.12s, border-color 0.12s, transform 0.05s, filter 0.12s;
}
button svg {
flex-shrink: 0;
}
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);
}
button.big {
padding: 11px 22px;
font-size: 15px;
border-radius: 10px;
}
input,
select {
background: var(--bg-input);
color: var(--text);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 8px 11px;
font-size: 13px;
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);
}
/* ============================================================
App shell: nav rail + main
============================================================ */
.app {
display: flex;
height: 100vh;
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;
background: linear-gradient(150deg, var(--accent-hover), var(--accent-active));
color: #fff;
font-weight: 800;
font-size: 18px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: var(--shadow-sm);
}
.brand-text {
font-weight: 700;
font-size: 15px;
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);
font-size: 14px;
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;
}
.nav-foot {
margin-top: auto;
display: flex;
align-items: center;
gap: 8px;
padding: 12px 10px 6px;
font-size: 12px;
color: var(--muted);
border-top: 1px solid var(--border-soft);
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
}
.status-dot.on {
background: var(--success);
box-shadow: 0 0 6px var(--success);
}
.status-dot.off {
background: var(--faint);
}
.main {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
background: var(--bg-content);
}
.topbar {
height: 52px;
flex-shrink: 0;
display: flex;
align-items: center;
gap: 10px;
padding: 0 18px;
background: var(--bg-topbar);
border-bottom: 1px solid var(--border-soft);
}
.path {
flex: 1 1 auto;
min-width: 0;
font-size: 12px;
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;
}
/* 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);
font-size: 12px;
}
.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;
font-size: 11px;
color: var(--muted);
}
.ingest-bar-cancel:hover {
color: var(--danger);
border-color: var(--danger);
}
.ingest-bar-dismiss {
flex-shrink: 0;
background: none;
border: none;
color: var(--faint);
cursor: pointer;
padding: 2px 6px;
font-size: 13px;
}
.ingest-bar-dismiss:hover {
color: var(--text);
}
.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;
}
.ingest-segment.active .ingest-segment-fill {
background: linear-gradient(90deg, var(--accent-hover), var(--accent));
}
.ingest-segment-label {
display: block;
margin-top: 3px;
font-size: 10px;
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);
}
.content {
flex: 1;
overflow: auto;
padding: 22px 26px;
}
/* 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 {
width: 72px;
height: 72px;
border-radius: 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: 34px;
color: #fff;
background: linear-gradient(150deg, var(--accent-hover), var(--accent-active));
box-shadow: 0 10px 30px var(--accent-glow);
}
.empty-state h2 {
margin: 4px 0 0;
}
/* ============================================================
Typography helpers
============================================================ */
h2 {
margin: 2px 0 14px;
font-size: 19px;
font-weight: 700;
color: var(--text-strong);
letter-spacing: -0.2px;
}
h3 {
margin: 18px 0 10px;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--faint);
border-bottom: 1px solid var(--border-soft);
padding-bottom: 8px;
}
h4 {
margin: 14px 0 7px;
font-size: 13px;
font-weight: 600;
color: var(--accent-hover);
}
.muted {
color: var(--muted);
}
.hint {
color: var(--muted);
font-size: 13px;
line-height: 1.65;
}
.row {
display: flex;
gap: 8px;
align-items: center;
}
.badge {
display: inline-flex;
align-items: center;
background: var(--accent-subtle);
color: var(--accent-hover);
border-radius: 999px;
padding: 1px 9px;
font-size: 11px;
font-weight: 700;
}
/* ============================================================
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);
font-size: 15px;
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;
font-size: 13px;
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);
}
/* ============================================================
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);
}
.card-type {
color: var(--muted);
font-size: 12px;
}
/* ============================================================
Color pips (WUBRG)
============================================================ */
.pips {
display: inline-flex;
gap: 3px;
}
.pip {
width: 18px;
height: 18px;
border-radius: 50%;
font-size: 10px;
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);
}
.pip.W { background: #f4efdd; color: #4a3f2a; }
.pip.U { background: #3a8fd0; color: #fff; }
.pip.B { background: #38343d; color: #cbc3d4; }
.pip.R { background: var(--accent); color: #fff; }
.pip.G { background: #479b63; color: #fff; }
.pip.C { background: #9aa0ab; color: #23242a; }
.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);
}
/* ============================================================
Tables
============================================================ */
table {
width: 100%;
border-collapse: collapse;
}
th {
text-align: left;
padding: 8px 12px;
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--faint);
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);
}
.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;
font-size: 11px;
color: var(--text);
margin-right: 4px;
}
/* ============================================================
Filters panel
============================================================ */
.filters {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
gap: 14px;
background: var(--bg-panel);
border: 1px solid var(--border-soft);
border-radius: var(--radius-lg);
padding: 16px;
margin-bottom: 16px;
}
.field {
display: flex;
flex-direction: column;
gap: 6px;
}
.field > label {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--faint);
}
.location-input {
padding: 4px 8px;
font-size: 12px;
max-width: 160px;
}
/* ============================================================
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;
font-size: 18px;
}
.hover-card {
position: fixed;
z-index: 200;
border-radius: 14px;
box-shadow: var(--shadow-lg);
border: 1px solid rgba(255, 255, 255, 0.08);
pointer-events: none;
}
.preview-modal-backdrop {
position: fixed;
inset: 0;
z-index: 300;
display: flex;
align-items: center;
justify-content: center;
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;
}
}
.preview-modal {
position: relative;
cursor: default;
}
.preview-modal-img {
display: block;
max-width: min(560px, 84vw);
max-height: 84vh;
border-radius: 18px;
box-shadow: var(--shadow-lg);
border: 1px solid rgba(255, 255, 255, 0.08);
}
.preview-modal-close {
position: absolute;
top: -14px;
right: -14px;
width: 32px;
height: 32px;
padding: 0;
border-radius: 999px;
background: var(--bg-elev);
color: var(--text);
border: 1px solid var(--border);
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: var(--shadow-md);
}
.preview-modal-close:hover {
background: var(--danger);
border-color: var(--danger);
color: #fff;
}
.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);
font-size: 12px;
font-weight: 600;
backdrop-filter: blur(4px);
}
.preview-modal-flip:hover {
background: var(--accent);
border-color: var(--accent);
}
.tile-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
gap: 16px;
}
.tile {
display: flex;
flex-direction: column;
gap: 7px;
}
.tile-img-wrap {
position: relative;
}
.tile-img {
width: 100%;
display: block;
border-radius: 12px;
box-shadow: var(--shadow-sm);
cursor: zoom-in;
transition: transform 0.12s, box-shadow 0.12s;
}
.tile:hover .tile-img {
transform: translateY(-3px);
box-shadow: var(--shadow-md);
}
.tile-noimg {
aspect-ratio: 488 / 680;
border: 1px solid var(--border);
border-radius: 12px;
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;
}
.tile-badge {
background: rgba(0, 0, 0, 0.78);
color: #fff;
border-radius: 8px;
padding: 2px 9px;
font-size: 12px;
font-weight: 700;
backdrop-filter: blur(2px);
}
.tile-badge.warn {
background: var(--warn);
color: #241700;
}
.tile-remove {
background: rgba(0, 0, 0, 0.78);
border: none;
color: #fff;
border-radius: 50%;
width: 24px;
height: 24px;
padding: 0;
line-height: 1;
font-size: 13px;
}
.tile-remove:hover {
background: var(--danger);
}
.tile-footer {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
}
/* ============================================================
Toolbar controls: group-by + view toggle
============================================================ */
.group-by {
display: inline-flex;
align-items: center;
gap: 7px;
font-size: 12px;
color: var(--muted);
}
.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;
font-size: 12px;
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;
}
/* ============================================================
Deck metadata (format + commander)
============================================================ */
.deck-meta {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 20px;
background: var(--bg-panel);
border: 1px solid var(--border-soft);
border-radius: var(--radius-lg);
padding: 14px 16px;
}
.commander-box {
display: flex;
flex-direction: column;
gap: 8px;
min-width: 280px;
flex: 1;
}