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>
171 lines
7.1 KiB
SCSS
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);
|
|
}
|