batchCooking/apps/admin-web/src/styles/_theme.scss
Nicolas c7301ea844 feat(admin): scaffold de l'application d'administration apps/admin-web
Nouvelle app Vite/React independante (workspace apps/*), calquee sur
apps/web : port 5174, sa propre image Docker (nginx statique), son propre
domaine/deploiement.

- AdminApiClient (VITE_ADMIN_API_URL, credentials: include) + ApiError.
- AdminAuthContext / RequireAdmin : restaure la session admin via
  GET /admin/auth/me, garde de route (miroir de AuthContext/RequireAuth).
- LoginPage (/login) : validation cliente via adminLoginSchema partage,
  erreurs traduites via ErrorMessageService.
- AdminLayout : sidebar (Tableau de bord / Monitoring / Corrections) +
  deconnexion, rendu une fois autour du groupe RequireAdmin.
- Pages Dashboard / Monitoring / Corrections en placeholder (remplies aux
  PR 3-5).
- i18n fr (bloc admin.* + sous-ensemble errors.*), tokens _theme.scss
  copies de apps/web (extraction en package partage : suivi separe).
- Dockerfile multi-stage (node build -> nginx:alpine) + nginx.conf (SPA
  fallback). Service admin-web dans docker-compose.yml (port ADMIN_WEB_PORT,
  VITE_ADMIN_API_URL en build arg).
- Cypress : login.feature (KO -> message, OK -> dashboard) + admin-layout
  .cy.ts (redirection /login sans session, nav entre sections, logout).
  Job "Run admin-web E2E tests" ajoute a ci.yml.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-28 23:36:30 +02:00

171 lines
7.1 KiB
SCSS

// NOTE: verbatim copy of apps/web/src/styles/_theme.scss. Extracting these
// tokens into a shared package (consumed by both apps) is tracked separately
// — keep the two files in sync by hand until then.
// =============================================================================
// Design tokens — the single source of truth for colors, spacing, typography
// 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 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 {
// --- 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: #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
// spacing stays visually consistent as the app grows.
--space-xs: 0.25rem; // 4px
--space-sm: 0.5rem; // 8px
--space-md: 1rem; // 16px
--space-lg: 1.5rem; // 24px
--space-xl: 2rem; // 32px
--space-2xl: 3rem; // 48px — inter-section spacing
// --- Typography --------------------------------------------------------
// 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;
--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 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 an explicit "light" choice (see
// apps/web's `ThemeContext`, `SYSTEM` = no `data-theme` attribute at all —
// this block then decides) can override a dark OS setting.
@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 an explicit "dark" choice (`ThemeContext`
// sets `data-theme="dark"` on `<html>`), 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);
}