203 lines
7.2 KiB
CSS
203 lines
7.2 KiB
CSS
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
|
|
/* Theme palettes. The Tailwind color tokens (tailwind.config.js) resolve to
|
|
these CSS variables, so setting `data-theme` on <html> re-themes the whole
|
|
app. `:root` is the default (Classic Cream) palette, also used when no/an
|
|
unknown data-theme is set. Each named theme overrides the same ten tokens.
|
|
Keep this list in sync with the registry in src/lib/theme.js. */
|
|
:root,
|
|
[data-theme='classic'] {
|
|
--color-surface: #f7f3ec;
|
|
--color-surface-raised: #ffffff;
|
|
--color-surface-sunken: #ede8de;
|
|
--color-border: #d4c9b4;
|
|
--color-text-primary: #1c1a16;
|
|
--color-text-secondary: #6b6354;
|
|
--color-accent: #4a6741;
|
|
--color-accent-hover: #3a5233;
|
|
--color-accent-warm: #c47d2e;
|
|
--color-danger: #b34040;
|
|
}
|
|
|
|
/* Dashboard stat-tile tokens. Defined once here on :root so EVERY theme
|
|
inherits them (theme blocks below don't redefine them), and they resolve
|
|
through the per-theme color tokens — so by default the eight tiles all read
|
|
as a plain raised surface, exactly like the rest of the app. The vibrant
|
|
`carnival` theme below overrides each tile with its own color. The tile
|
|
foreground vars let the colored variant flip to light text without touching
|
|
the global text tokens. Dashboard.jsx references these as literal
|
|
`bg-[var(--stat-N)]` classes (Tailwind only generates classes it can see as
|
|
complete strings, so the per-tile names are spelled out, not built up). */
|
|
:root {
|
|
/* Text on accent-warm fills (the For Sale badge, "Check" flags). The warm
|
|
oranges are too light for white text in the light themes, so this is the
|
|
theme's dark ink everywhere — on Midnight that's the surface color. */
|
|
--color-on-accent-warm: var(--color-text-primary);
|
|
|
|
--stat-fg: var(--color-text-primary);
|
|
--stat-fg-muted: var(--color-text-secondary);
|
|
--stat-1: var(--color-surface-raised);
|
|
--stat-2: var(--color-surface-raised);
|
|
--stat-3: var(--color-surface-raised);
|
|
--stat-4: var(--color-surface-raised);
|
|
--stat-5: var(--color-surface-raised);
|
|
--stat-6: var(--color-surface-raised);
|
|
--stat-7: var(--color-surface-raised);
|
|
--stat-8: var(--color-surface-raised);
|
|
--stat-1-hover: var(--color-surface-sunken);
|
|
--stat-2-hover: var(--color-surface-sunken);
|
|
--stat-3-hover: var(--color-surface-sunken);
|
|
--stat-4-hover: var(--color-surface-sunken);
|
|
--stat-5-hover: var(--color-surface-sunken);
|
|
--stat-6-hover: var(--color-surface-sunken);
|
|
--stat-7-hover: var(--color-surface-sunken);
|
|
--stat-8-hover: var(--color-surface-sunken);
|
|
}
|
|
|
|
/* Dark theme: accent-hover is *lighter* than accent (the opposite of the light
|
|
themes), since a darker hover would disappear against dark surfaces. */
|
|
[data-theme='midnight'] {
|
|
--color-surface: #1a1d1a;
|
|
--color-surface-raised: #242823;
|
|
--color-surface-sunken: #151814;
|
|
--color-border: #3a3f38;
|
|
--color-text-primary: #ece8e0;
|
|
--color-text-secondary: #a8a294;
|
|
--color-accent: #7fa56e;
|
|
--color-accent-hover: #94b884;
|
|
--color-accent-warm: #d9954a;
|
|
--color-danger: #d46a6a;
|
|
--color-on-accent-warm: var(--color-surface);
|
|
}
|
|
|
|
[data-theme='ocean'] {
|
|
--color-surface: #f0f5f8;
|
|
--color-surface-raised: #ffffff;
|
|
--color-surface-sunken: #e1eaf0;
|
|
--color-border: #c2d3de;
|
|
--color-text-primary: #14222b;
|
|
--color-text-secondary: #516a79;
|
|
--color-accent: #2e6e8e;
|
|
--color-accent-hover: #245870;
|
|
--color-accent-warm: #e0913b;
|
|
--color-danger: #c0504d;
|
|
}
|
|
|
|
[data-theme='berry'] {
|
|
--color-surface: #f8f2f6;
|
|
--color-surface-raised: #ffffff;
|
|
--color-surface-sunken: #efe2ea;
|
|
--color-border: #dcc6d3;
|
|
--color-text-primary: #2a1a26;
|
|
--color-text-secondary: #7a6070;
|
|
--color-accent: #9b3b6e;
|
|
--color-accent-hover: #7e2d58;
|
|
--color-accent-warm: #c9762e;
|
|
--color-danger: #c0392b;
|
|
}
|
|
|
|
[data-theme='sunset'] {
|
|
--color-surface: #fbf3ec;
|
|
--color-surface-raised: #ffffff;
|
|
--color-surface-sunken: #f4e6da;
|
|
--color-border: #e6cdb8;
|
|
--color-text-primary: #2a1c14;
|
|
--color-text-secondary: #7a604d;
|
|
--color-accent: #b84a26;
|
|
--color-accent-hover: #9a3d1c;
|
|
--color-accent-warm: #e0913b;
|
|
--color-danger: #b33a3a;
|
|
}
|
|
|
|
/* Carnival: the deliberately loud theme. A clean light base lets the
|
|
multi-colored dashboard tiles (overridden below) pop. The stat tiles flip to
|
|
white text via --stat-fg. */
|
|
[data-theme='carnival'] {
|
|
--color-surface: #faf7fd;
|
|
--color-surface-raised: #ffffff;
|
|
--color-surface-sunken: #f0eaf7;
|
|
--color-border: #e2d9ec;
|
|
--color-text-primary: #1b1228;
|
|
--color-text-secondary: #6a5c7c;
|
|
--color-accent: #7c3aed;
|
|
--color-accent-hover: #6d28d9;
|
|
--color-accent-warm: #ea580c;
|
|
--color-danger: #e11d48;
|
|
|
|
--stat-fg: #ffffff;
|
|
--stat-fg-muted: rgba(255, 255, 255, 0.82);
|
|
--stat-1: #7c3aed; /* herd — violet */
|
|
--stat-2: #2563eb; /* bucks — blue */
|
|
--stat-3: #db2777; /* does — pink */
|
|
--stat-4: #ea580c; /* kits — orange */
|
|
--stat-5: #059669; /* for sale — green*/
|
|
--stat-6: #d97706; /* on hold — amber */
|
|
--stat-7: #0891b2; /* sold — cyan */
|
|
--stat-8: #475569; /* archived — slate*/
|
|
--stat-1-hover: #6d28d9;
|
|
--stat-2-hover: #1d4ed8;
|
|
--stat-3-hover: #be185d;
|
|
--stat-4-hover: #c2410c;
|
|
--stat-5-hover: #047857;
|
|
--stat-6-hover: #b45309;
|
|
--stat-7-hover: #0e7490;
|
|
--stat-8-hover: #334155;
|
|
}
|
|
|
|
/* Theme-aware form controls. Inputs/textareas/selects across the app don't set
|
|
their own background or text color, so without this they fall back to the
|
|
browser default (white background, near-black text) — fine on the light
|
|
themes (where it matches `surface-raised`) but unreadable on the dark
|
|
Midnight theme, which renders a glaring white box with washed-out placeholder
|
|
text. Tying the controls to the theme tokens keeps them legible everywhere
|
|
without touching each component. */
|
|
@layer base {
|
|
input:not([type='checkbox']):not([type='radio']):not([type='file']):not([type='range']):not([type='color']),
|
|
textarea,
|
|
select {
|
|
background-color: var(--color-surface-raised);
|
|
color: var(--color-text-primary);
|
|
}
|
|
|
|
/* iOS Safari zooms the page when focusing a control whose font-size is under
|
|
16px, which is jarring in the installed PWA. Most controls use text-sm, so
|
|
bump them to 16px on small screens only; desktop keeps the denser size. */
|
|
@media (max-width: 767px) {
|
|
input:not([type='checkbox']):not([type='radio']):not([type='file']):not([type='range']):not([type='color']),
|
|
textarea,
|
|
select {
|
|
font-size: 16px;
|
|
}
|
|
}
|
|
|
|
input::placeholder,
|
|
textarea::placeholder {
|
|
color: var(--color-text-secondary);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
/* `!important` is deliberate: several inputs/buttons already set
|
|
`focus:outline-none` (paired with their own `focus:ring-2`) which would
|
|
otherwise win on specificity over a plain `:focus-visible` rule and leave
|
|
keyboard focus invisible on exactly the elements that most need it. This
|
|
guarantees every focusable element gets a visible accent-colored ring on
|
|
keyboard focus, app-wide, without auditing each component individually. */
|
|
:focus-visible {
|
|
outline: 2px solid theme('colors.accent') !important;
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: 0.01ms !important;
|
|
scroll-behavior: auto !important;
|
|
}
|
|
}
|