);
}
diff --git a/apps/web/src/styles/_theme.scss b/apps/web/src/styles/_theme.scss
index 386743b..b062d73 100644
--- a/apps/web/src/styles/_theme.scss
+++ b/apps/web/src/styles/_theme.scss
@@ -3,29 +3,61 @@
// and other reusable values across the whole app.
//
// Exposed as CSS custom properties on :root (not plain SCSS variables) so
-// they're available at *runtime*, not just compile time — this is what
-// would let a future dark-mode toggle (or any theme switch) just redefine
-// these variables instead of rebuilding the stylesheet. Every other .scss
-// file should reference `var(--token-name)`, never a hardcoded color/size.
+// they're available at *runtime*, not just compile time — this is what lets
+// dark mode work below by simply redefining these variables instead of
+// rebuilding the stylesheet. Every other .scss file should reference
+// `var(--token-name)`, never a hardcoded color/size.
+//
+// Palette name: "Mise en Place" — a kitchen-operations identity (batch
+// cooking as logistics: everything labeled and in its place before you
+// start) rather than a food-blog one. See the design proposal for the full
+// rationale: https://claude.ai/code/artifact/1db63af0-cfd1-4f77-9369-71ca6accd06f
//
// Import this partial once, globally (see global.scss) — never re-import it
// from a component-level .scss file, `:root` only needs to be declared once.
// =============================================================================
:root {
- // --- Color palette --------------------------------------------------------
- // Neutral surface: page background vs. the "card" surface content sits on.
- --color-background: #ffffff;
+ // --- Surfaces & ink ---------------------------------------------------
+ // Neutral surface: page background ("porcelaine") vs. the card surface
+ // content sits on, plus a recessed variant for panels/table headers.
+ --color-background: #eef2ed;
--color-surface: #ffffff;
+ --color-surface-alt: #e2e8e0;
// Text.
- --color-text: #1a1a1a;
- --color-text-muted: #555555;
- // Brand/accent — used for primary buttons and links.
- --color-primary: #2f6f4f;
- --color-primary-hover: #24573e;
- // Feedback.
- --color-error: #c0392b;
- --color-border: #888888;
+ --color-text: #1f2a22;
+ --color-text-muted: #57685a;
+ --color-border: #c7d0c4;
+
+ // --- Brand accents, each with one job --------------------------------
+ // Basil — primary actions, links, brand presence.
+ --color-primary: #2e6b4a;
+ --color-primary-hover: #244f38;
+ // Vermillion — secondary accent for urgency/strong calls to action
+ // (e.g. a timer, "start session"). Never reused for allergen alerts
+ // below — those need their own, unambiguous color.
+ --color-accent: #cc4b26;
+ --color-accent-hover: #a83c1c;
+ // Turmeric — category/classification tags.
+ --color-tag: #c98a1b;
+ --color-tag-ink: #3a2c05; // pairs with a solid --color-tag fill only.
+
+ // --- Feedback -----------------------------------------------------------
+ --color-success: #2e6b4a;
+ --color-warning: #c98a1b;
+ --color-error: #b3271e;
+
+ // --- Allergens / intolerances --------------------------------------------
+ // A 3-tier food-safety scale, kept distinct from --color-error so an
+ // allergen warning is never confused with a form validation error:
+ // - critical (declared allergen): its own color, solid/inverted fill
+ // - moderate (intolerance): reuses --color-warning, tinted fill
+ // - trace ("may contain traces of…"): neutral, dashed outline
+ // The severity is carried by the FILL TREATMENT, not the hue alone, so
+ // it stays legible for color-blind users. See the design proposal's
+ // "Alertes & allergènes" section for the full component set.
+ --color-allergen: #a8123f;
+ --color-allergen-ink: #ffe9ef; // pairs with a solid --color-allergen fill only.
// --- Spacing scale ---------------------------------------------------------
// Multiples of a 4px base unit — use these instead of ad hoc px values so
@@ -35,21 +67,101 @@
--space-md: 1rem; // 16px
--space-lg: 1.5rem; // 24px
--space-xl: 2rem; // 32px
+ --space-2xl: 3rem; // 48px — inter-section spacing
// --- Typography --------------------------------------------------------
- --font-family-base: system-ui, sans-serif;
- --font-size-base: 1rem;
- --font-size-sm: 0.875rem;
- --font-size-xs: 0.8rem;
+ // System font stacks only — no remote webfont, so there's zero loading
+ // latency and no flash of unstyled text, which fits an app meant to be
+ // used quickly under time pressure. Three roles: a condensed "label"
+ // face for headings/eyebrows, a humanist face for body copy, and a
+ // monospace for anything that lines up in columns (times, quantities).
+ --font-display: "Bahnschrift", "Arial Narrow", "Segoe UI", sans-serif;
+ --font-body: "Segoe UI", "Helvetica Neue", Arial, sans-serif;
+ --font-mono: "Cascadia Mono", Consolas, "SF Mono", "Liberation Mono", monospace;
- // --- Shape / misc --------------------------------------------------------
- --radius-base: 4px;
+ --font-size-xs: 0.75rem; // 12px — captions, meta
+ --font-size-sm: 0.875rem; // 14px — labels, secondary text
+ --font-size-base: 1rem; // 16px — body
+ --font-size-md: 1.125rem; // 18px — lead paragraph
+ --font-size-lg: 1.375rem; // 22px — H3 / card titles
+ --font-size-xl: 1.75rem; // 28px — H2 / section titles
+ --font-size-2xl: 2.25rem; // 36px — H1 / page titles
+ --font-size-3xl: 3rem; // 48px — display, exceptional use only
+
+ // --- Shape / elevation --------------------------------------------------
+ --radius-base: 4px; // controls (inputs, buttons) — deliberately flat
+ --radius-md: 10px; // cards
+ --radius-lg: 18px; // panels, modals
+ --radius-pill: 999px; // tags, badges
--max-width-form: 22rem;
+
+ --shadow-sm: 0 1px 2px rgba(31, 42, 34, 0.08), 0 1px 1px rgba(31, 42, 34, 0.06);
+ --shadow-md: 0 6px 16px rgba(31, 42, 34, 0.12), 0 2px 6px rgba(31, 42, 34, 0.08);
}
// Lets the browser pick sensible default colors (form controls, scrollbars)
-// for whichever mode (light/dark) the user's OS is in, until this app has
-// its own explicit dark theme wired to the tokens above.
+// for whichever mode the user ends up in.
:root {
color-scheme: light dark;
}
+
+// --- Dark mode ---------------------------------------------------------
+// Follows the OS/browser preference by default. Guarded with
+// `:root:not([data-theme="light"])` so that, if a manual theme switch is
+// ever added, an explicit "light" choice can override a dark OS setting —
+// today nothing sets `data-theme`, so this simply tracks system preference.
+@media (prefers-color-scheme: dark) {
+ :root:not([data-theme="light"]) {
+ --color-background: #14181a;
+ --color-surface: #1c221e;
+ --color-surface-alt: #262e27;
+ --color-text: #edf1ea;
+ --color-text-muted: #a9b5a6;
+ --color-border: #384038;
+
+ --color-primary: #5fae7e;
+ --color-primary-hover: #7cc496;
+ --color-accent: #ea7a48;
+ --color-accent-hover: #f0946c;
+ --color-tag: #e8b84b;
+ --color-tag-ink: #2a2005;
+
+ --color-success: #5fae7e;
+ --color-warning: #e8b84b;
+ --color-error: #e5675a;
+
+ --color-allergen: #e2547b;
+ --color-allergen-ink: #3a0416;
+
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
+ --shadow-md: 0 8px 20px rgba(0, 0, 0, 0.45);
+ }
+}
+
+// Mirrors the block above for a future explicit "dark" choice (e.g. a
+// theme toggle), so it wins over the OS setting in both directions.
+:root[data-theme="dark"] {
+ --color-background: #14181a;
+ --color-surface: #1c221e;
+ --color-surface-alt: #262e27;
+ --color-text: #edf1ea;
+ --color-text-muted: #a9b5a6;
+ --color-border: #384038;
+
+ --color-primary: #5fae7e;
+ --color-primary-hover: #7cc496;
+ --color-accent: #ea7a48;
+ --color-accent-hover: #f0946c;
+ --color-tag: #e8b84b;
+ --color-tag-ink: #2a2005;
+
+ --color-success: #5fae7e;
+ --color-warning: #e8b84b;
+ --color-error: #e5675a;
+
+ --color-allergen: #e2547b;
+ --color-allergen-ink: #3a0416;
+
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
+ --shadow-md: 0 8px 20px rgba(0, 0, 0, 0.45);
+}
diff --git a/apps/web/src/styles/global.scss b/apps/web/src/styles/global.scss
index 94c22ff..efce8ca 100644
--- a/apps/web/src/styles/global.scss
+++ b/apps/web/src/styles/global.scss
@@ -7,11 +7,49 @@
@use "./theme";
+// Include borders/padding in an element's declared width/height everywhere,
+// rather than the browser default of adding them on top.
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+
// Minimal reset: remove the default body margin so pages can control their
// own layout without fighting the browser's default 8px margin.
body {
margin: 0;
- font-family: var(--font-family-base);
+ font-family: var(--font-body);
+ font-size: var(--font-size-base);
+ line-height: 1.55;
color: var(--color-text);
background: var(--color-background);
}
+
+// Headings use the condensed "label" face app-wide — see _theme.scss for
+// the rationale. `text-wrap: balance` avoids a lone short word wrapping
+// onto its own line in multi-line titles.
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ margin: 0;
+ font-family: var(--font-display);
+ font-weight: 700;
+ text-wrap: balance;
+}
+
+// Default to the page-title size; a heading used as a smaller component
+// title (e.g. the auth card's
) overrides this in its own stylesheet.
+h1 {
+ font-size: var(--font-size-2xl);
+}
+
+// A visible, consistent focus ring for keyboard navigation — the browser
+// default varies a lot between elements and browsers.
+:focus-visible {
+ outline: 2px solid var(--color-accent);
+ outline-offset: 2px;
+}