tokens/aleris-tokens.css
/* ==========================================================================
Aleris Design Tokens — Canonical Reference
==========================================================================
Source of truth for all Aleris digital products.
Three-layer architecture: Primitives → Semantic → Component.
Figma is the upstream source for primitives. This file consolidates
Figma exports with documented design decisions. Where values differ
from Figma, a FIGMA-UPDATE comment marks what needs syncing back.
Last updated: May 2026
Maintainer: Torfinn Almers, Head of Design, Aleris Group
========================================================================== */
/* Companion stylesheet for Museo Sans @font-face declarations:
baseline/tokens/aleris-fonts.css
Load order: tokens, then fonts. Fonts file references --font-family-primary
defined here. */
/* --------------------------------------------------------------------------
LAYER 1: PRIMITIVES
Raw values. No semantic meaning. Platform-independent.
Naming convention: category.group.scale (100=light/tint, 300=mid, 500=full)
-------------------------------------------------------------------------- */
:root {
/* --- Colors: Brand --- */
--color-petrol-100: #d9e1e2; /* @usage Tinted backgrounds, selected row | @constraint Never as text color */
--color-petrol-300: #7fa9ae; /* @usage Secondary accents, hover tints | @constraint Not for body text — too light */
--color-petrol-500: #004851; /* @usage Primary text, headings, structural elements | @constraint Default text color. Only specify when deviating */
--color-sand-50: #faf8f6; /* @usage Page background for instrumental surfaces | @constraint Tools, admin, dashboards only. Communicative uses sand-100 */
--color-sand-100: #f2ece4; /* @usage Page background for communicative surfaces | @constraint Patient-facing, marketing, booking. Never use white as page bg */
--color-sand-300: #e7ceb5;
--color-sand-500: #d9b48f;
--color-orange-100: #fde8df;
--color-orange-300: #ffbe9f; /* @usage Light accent backgrounds, soft orange tints | @constraint Decorative/background only, not for interactive states */
--color-orange-400: #faaa8d; /* @usage Button hover state for primary buttons | @constraint Only as hover/active variant of orange-500 */
--color-orange-500: #f58c61; /* @usage Primary CTAs, accent, active indicators | @constraint One primary CTA per screen maximum. Never for decoration */
/* --- Colors: Neutral --- */
--color-gray-100: #d7d2cb;
--color-gray-300: #9e9281;
--color-gray-500: #585044;
--color-white: #ffffff; /* @usage Card/surface backgrounds, inverse text bg | @constraint Never as page background. Pages are sand-100 or sand-50 */
/* --- Colors: Feedback ---
FIGMA-UPDATE: Figma has only warning-100 (#c14444). This file adds
error-500 (consolidated from Figma's warning-100) and warning-500
(from documented amber, fits sand family). Sync back to Figma. */
--color-error-500: #c14444; /* @usage Validation errors, destructive actions | @constraint State communication only. Never decoration */
--color-warning-500: #d9b48f; /* @usage Warning states, caution indicators | @constraint State communication only. Same hex as sand-500 — context differentiates */
/* --- Colors: Confirm ---
Completion/confirmation action color. Distinct from goal-achieved
(dashboard indicator) — this is for interactive "mark done" actions.
Evidence: Hälsodeklarationer prototype "Klarmarkera" button.
FIGMA-UPDATE: Not yet in Figma. Add as action color. */
--color-confirm-500: #27ae60; /* @usage Interactive confirm actions (Klarmarkera, Godkänn, Signera) | @constraint Distinct from goal-achieved green. Confirm = interactive, goal-achieved = indicator */
/* --- Colors: Goal Status ---
Traffic light indicators for KPI/goal dashboards only.
Never use in patient-facing interfaces.
MUST always pair with icon or shape — never color alone (accessibility). */
--color-goal-achieved: #2e8540; /* @usage KPI target met indicator | @constraint Dashboard only. Always pair with icon/shape. Never for interactive confirm */
--color-goal-borderline: #ffb81c; /* @usage KPI near-target indicator | @constraint Dashboard only. Always pair with icon/shape */
--color-goal-missed: #d4351c; /* @usage KPI target missed indicator | @constraint Dashboard only. Always pair with icon/shape */
--color-goal-no-data: #007bc7; /* @usage KPI no-data indicator | @constraint Dashboard only. Always pair with icon/shape */
/* --- Colors: Data Visualization ---
Dedicated chart/graph palette. Deliberately distinct from brand palette
to prevent confusion between data points and interactive UI elements.
Ordered by recommended usage sequence (start with 01, add as needed).
Cool series (01-06) and warm series (07-12) can pair by index. */
--color-chart-01-teal: #0f9081;
--color-chart-02-mint: #94cbc4;
--color-chart-03-blue: #577ba3;
--color-chart-04-blue-light: #98c9ef;
--color-chart-05-purple: #a078c2;
--color-chart-06-purple-light: #d8b8ef;
--color-chart-07-terracotta: #d77a61;
--color-chart-08-terracotta-light: #f0c3b2;
--color-chart-09-marine: #6b9495;
--color-chart-10-olive: #bed0c0;
--color-chart-11-warm-grey: #e4ded5;
--color-chart-12-dark-sand: #d9b48f;
/* Note: chart-12 dark-sand is same hex as sand-500 and warning-500.
Context differentiates: sand-500 = brand primitive, warning-500 = UI state,
chart-12 = data category. If chart-12 appears alongside a warning state
in the same view, consider using a different chart color. */
/* --- Spacing ---
Modular scale: 1.5 ratio aligned to 4px grid.
Shares ratio with type scale for cross-dimensional harmony.
FIGMA-UPDATE: Replaces previous spacing scale. Sync to Figma. */
--spacing-0: 0px; /* @usage Zero gap, collapsed state | @constraint Use explicitly — don't omit spacing, set it to 0 */
--spacing-3xs: 4px; /* @usage Minimum gap, icon padding, label-to-field | @constraint Smallest usable gap. Never go below this */
--spacing-2xs: 8px; /* @usage Tight element spacing, inline gaps | @constraint Related inline elements */
--spacing-xs: 12px; /* @usage Related element spacing, input padding-y | @constraint Default vertical rhythm within components */
--spacing-sm: 16px; /* @usage Default component padding, grid margin mobile | @constraint Most common padding value */
--spacing-md: 24px; /* @usage Section spacing within components, card padding | @constraint Primary structural spacing */
--spacing-lg: 36px; /* @usage Between components, above h2 | @constraint Component-level separation */
--spacing-xl: 48px; /* @usage Between sections | @constraint Section-level separation */
--spacing-2xl: 72px; /* @usage Major section breaks | @constraint Communicative surfaces mainly */
--spacing-3xl: 96px; /* @usage Page-level spacing | @constraint Communicative surfaces only. Never on instrumental */
/* --- Border Radius ---
Three-tier system based on functional role:
s (4px) → containers: cards, panels, modals, tables
m (8px) → interactive: buttons, inputs, dropdowns, action cards
full → compact indicators: badges, tags, status dots
l (12px) → reserved, not currently assigned to any component */
--radius-0: 0px; /* @usage Sharp corners, full-bleed edges | @constraint Explicit removal of rounding */
--radius-s: 4px; /* @usage Containers: cards, panels, modals, tables | @constraint Container tier only. Not for buttons or inputs */
--radius-m: 8px; /* @usage Interactive: buttons, inputs, dropdowns | @constraint Interactive element tier. Not for containers */
--radius-l: 12px; /* @usage Reserved — not currently assigned | @constraint Do not use without governance approval */
--radius-full: 100px; /* @usage Compact indicators: badges, tags, avatars | @constraint Indicator tier only. No full-pill buttons exist in Aleris */
/* --- Stroke --- */
--stroke-0: 0px;
--stroke-xs: 1px;
--stroke-m: 2px;
--stroke-xl: 4px;
/* --- Typography: Font Family --- */
--font-family-primary: 'Museo Sans', Arial, sans-serif;
--font-family-fallback: Arial, sans-serif;
--font-family-icons: 'Font Awesome 6 Pro';
/* --- Typography: Font Size ---
Perfect fifth modular scale. Ratio: 1.5. Base: 18px (1rem).
Shares ratio with spacing scale for cross-dimensional harmony.
Half-steps (sm, h4) use geometric means for pragmatic in-between sizes.
FIGMA-UPDATE: Full scale replacement. Sync all sizes to Figma. */
--font-size-xs: 0.78rem; /* 14px — accessibility floor */
--font-size-sm: 0.89rem; /* 16px — √(14×18), labels, UI chrome */
--font-size-base: 1rem; /* 18px — body text. Scale anchor. */
--font-size-md: 1.22rem; /* 22px — √(18×27), h4 half-step */
--font-size-lg: 1.5rem; /* 27px — base × 1.5 */
--font-size-xl: 2.22rem; /* 40px — base × 1.5² */
--font-size-2xl: 3.33rem; /* 60px — base × 1.5³ */
/* --- Typography: Font Weight ---
Mapped to actual Museo Sans font files (woff2).
Available weights: 100, 300, 500, 700, 900 (plus italics).
IMPORTANT: Museo Sans has no 400 weight. Using font-weight: 400
causes browser synthesis — always use these tokens instead.
When falling back to Arial, 500 renders slightly heavier than
Arial's 400 but is acceptable. */
--font-weight-light: 300; /* Museo Sans 300. @usage Large display text on communicative surfaces | @constraint Exceptional use only. Size must be xl (40px) or above */
--font-weight-regular: 500; /* Museo Sans 500 (Medium). @usage Body text, labels, buttons, UI chrome | @constraint The workhorse. Never use 400 — Museo Sans has no 400 weight */
--font-weight-bold: 700; /* Museo Sans 700. @usage Headings, emphasis, CTAs | @constraint The other workhorse. 500 and 700 are the system */
--font-weight-black: 900; /* Museo Sans 900. @usage Almost never | @constraint Exceptional use only. Try 700 at larger size first */
/* --- Typography: Line Height ---
Values chosen to resonate with the type and spacing scales.
body 18px × 1.5 = 27px (= h3 font size)
h3 27px × 1.33 = 36px (= spacing-lg)
h2 40px × 1.2 = 48px (= spacing-xl) */
--line-height-tight: 1.1; /* Display text (60px) */
--line-height-heading: 1.2; /* h1, h2 */
--line-height-subheading: 1.33; /* h3, h4 */
--line-height-body: 1.5; /* Body text — 18 × 1.5 = 27px = h3 size */
/* --- Typography: Letter Spacing --- */
--letter-spacing-normal: 0;
--letter-spacing-tight: -0.01em; /* h2, h3 */
--letter-spacing-tighter: -0.015em; /* h1 */
--letter-spacing-tightest: -0.02em; /* Display */
/* --- Shadows ---
Petrol-tinted for brand coherence. Mapped to Figma's e0–e3 scale.
FIGMA-UPDATE: Figma uses black-based shadows. Update Figma to
petrol-tinted values for brand consistency. */
--shadow-e0: 0px 0px 0px rgba(0, 72, 81, 0);
--shadow-e1: 0px 2px 8px rgba(0, 72, 81, 0.08);
--shadow-e2: 0px 4px 16px rgba(0, 72, 81, 0.12);
--shadow-e3: 0px 6px 24px rgba(0, 72, 81, 0.16);
/* --- Layout Grid ---
12-column grid. Gutters use spacing tokens.
Max-width varies by surface temperature:
communicative = capped for reading comfort,
instrumental = wider or fluid for data density. */
--grid-columns: 12;
--grid-gutter: var(--spacing-md);
--grid-margin-mobile: var(--spacing-sm);
--grid-margin-desktop: var(--spacing-md);
--grid-max-width-communicative: 1200px;
--grid-max-width-instrumental: 1440px;
/* --- Breakpoints ---
Practical values, not derived from the modular scale.
Use container queries where possible; these cover viewport fallbacks. */
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
/* --- Icons ---
Font Awesome 6 Pro (licensed). Available styles: solid, regular, light.
Icon sizes follow a scale independent of typography.
FIGMA-UPDATE: Move icon sizes out of text-size styles in Figma. */
--icon-xs: 12px; /* Inline indicators, badge icons */
--icon-sm: 16px; /* Button icons, form field icons */
--icon-md: 18px; /* Default inline icon (matches body text) */
--icon-lg: 24px; /* Navigation, card header icons */
--icon-xl: 48px; /* Feature icons, empty states */
/* --------------------------------------------------------------------------
LAYER 2: SEMANTIC TOKENS
Map primitives to usage context. Carry meaning.
These are the tokens product teams should reference in code.
-------------------------------------------------------------------------- */
/* --- Brand Role --- */
--brand-primary: var(--color-petrol-500);
--brand-accent: var(--color-orange-500);
--brand-light: var(--color-white);
/* --- Text --- */
--text-primary: var(--color-petrol-500);
--text-secondary: var(--color-gray-500);
--text-tertiary: var(--color-gray-300);
--text-inverse: var(--color-white);
--text-accent: var(--color-orange-500);
--text-link: var(--color-petrol-500);
--text-disabled: var(--color-gray-300);
--text-error: var(--color-error-500);
/* --- Backgrounds / Surfaces ---
Two page surfaces by temperature:
Communicative (patient-facing, marketing, booking): sand-100.
Cards are white on sand — warmth is the brand, cards provide editorial structure.
Instrumental (tools, admin, documentation, dashboards): sand-50.
Near-white with faint sand warmth. Content lives directly on the surface.
Cards used sparingly — only when content genuinely needs visual grouping.
The surface choice drives whether cards are structural (communicative)
or optional (instrumental). */
--surface-page: var(--color-sand-100); /* @usage Communicative page background | @constraint Patient-facing, marketing, booking. Cards mandatory on this surface */
--surface-page-instrumental: var(--color-sand-50); /* @usage Instrumental page background | @constraint Tools, admin, dashboards. Cards optional on this surface */
--surface-card: var(--color-white); /* @usage Card and panel backgrounds | @constraint Always white. Never sand on cards */
--surface-elevated: var(--color-white); /* @usage Dropdowns, popovers, tooltips | @constraint White with elevation shadow */
--surface-overlay: var(--color-white); /* @usage Modal and dialog backgrounds | @constraint White with highest elevation */
/* Surface temperature: neutral/warm/cold variants.
These support the communicative (warm) vs. instrumental (cold/neutral)
surface modes described in the design governance document. */
--surface-subtle-neutral: var(--color-sand-100);
--surface-subtle-warm: var(--color-orange-100);
--surface-subtle-cold: var(--color-petrol-100);
--surface-strong-neutral: var(--color-sand-500);
--surface-strong-warm: var(--color-orange-500);
--surface-strong-cold: var(--color-petrol-500);
/* --- Borders --- */
--border-default: var(--color-gray-100);
--border-strong: var(--color-gray-300);
--border-focus: var(--color-petrol-500);
--border-error: var(--color-error-500);
/* --- Interactive States --- */
--state-hover: var(--color-orange-500);
--state-active: var(--color-sand-500);
--state-focus-ring: var(--color-petrol-500);
--state-disabled-bg: var(--color-gray-100);
--state-disabled-text: var(--color-gray-300);
/* --- Feedback / Status ---
Reserved for state communication only. Never decoration. */
--status-error: var(--color-error-500);
--status-warning: var(--color-warning-500);
--status-confirm: var(--color-confirm-500);
--status-background: var(--color-white);
/* --- Goal Status (semantic) ---
Dashboard KPI indicators. Instrumental surfaces only.
Always pair with icon or shape — never color alone. */
--goal-achieved: var(--color-goal-achieved);
--goal-borderline: var(--color-goal-borderline);
--goal-missed: var(--color-goal-missed);
--goal-no-data: var(--color-goal-no-data);
/* --- Data Visualization (semantic) ---
Use chart-sequence for ordered category assignment.
Use chart-cool-*/chart-warm-* for intentional temperature grouping.
12 colors total — sufficient for most visualizations.
If you need more than 6-8 in one chart, reconsider the visualization. */
/* Sequential assignment — use in this order */
--chart-1: var(--color-chart-01-teal);
--chart-2: var(--color-chart-02-mint);
--chart-3: var(--color-chart-03-blue);
--chart-4: var(--color-chart-04-blue-light);
--chart-5: var(--color-chart-05-purple);
--chart-6: var(--color-chart-06-purple-light);
--chart-7: var(--color-chart-07-terracotta);
--chart-8: var(--color-chart-08-terracotta-light);
--chart-9: var(--color-chart-09-marine);
--chart-10: var(--color-chart-10-olive);
--chart-11: var(--color-chart-11-warm-grey);
--chart-12: var(--color-chart-12-dark-sand);
/* Paired assignment — for comparisons, before/after, two-series charts */
--chart-pair-1-a: var(--color-chart-01-teal);
--chart-pair-1-b: var(--color-chart-07-terracotta);
--chart-pair-2-a: var(--color-chart-03-blue);
--chart-pair-2-b: var(--color-chart-08-terracotta-light);
--chart-pair-3-a: var(--color-chart-05-purple);
--chart-pair-3-b: var(--color-chart-10-olive);
/* --- Typography Compositions ---
Composite tokens for common text roles.
Each bundles: size, weight, line-height, letter-spacing, color.
Perfect fifth (1.5) modular scale from 18px base.
Line-heights resonate with spacing scale:
body 18×1.5 = 27px (h3 size)
h3 27×1.33 = 36px (spacing-lg)
h2 40×1.2 = 48px (spacing-xl) */
/* Heading 1 / Display — communicative hero, landing pages (60px) */
--type-h1-size: var(--font-size-2xl);
--type-h1-weight: var(--font-weight-bold);
--type-h1-line-height: var(--line-height-tight);
--type-h1-letter-spacing: var(--letter-spacing-tightest);
--type-h1-color: var(--text-primary);
/* Heading 2 — page title, major section (40px) */
--type-h2-size: var(--font-size-xl);
--type-h2-weight: var(--font-weight-bold);
--type-h2-line-height: var(--line-height-heading);
--type-h2-letter-spacing: var(--letter-spacing-tighter);
--type-h2-color: var(--text-primary);
/* Heading 3 — section heading (27px) */
--type-h3-size: var(--font-size-lg);
--type-h3-weight: var(--font-weight-bold);
--type-h3-line-height: var(--line-height-subheading);
--type-h3-letter-spacing: var(--letter-spacing-tight);
--type-h3-color: var(--text-primary);
/* Heading 4 — card header, sub-subsection (22px) */
--type-h4-size: var(--font-size-md);
--type-h4-weight: var(--font-weight-bold);
--type-h4-line-height: var(--line-height-subheading);
--type-h4-letter-spacing: var(--letter-spacing-normal);
--type-h4-color: var(--text-primary);
/* Lead — introductory paragraph below a heading.
Bridges heading and body: heading-sized but body-weight, secondary color.
Not a heading — it doesn't structure, it orients.
Use once per page/section, directly after the h1 or h2. */
--type-lead-size: var(--font-size-lg); /* 27px — same as h3 */
--type-lead-weight: var(--font-weight-regular); /* 500 — not bold */
--type-lead-line-height: var(--line-height-subheading); /* 1.33 */
--type-lead-letter-spacing: var(--letter-spacing-normal);
--type-lead-color: var(--text-secondary); /* Lighter than headings */
/* Body text (18px) */
--type-body-size: var(--font-size-base);
--type-body-weight: var(--font-weight-regular);
--type-body-line-height: var(--line-height-body);
--type-body-color: var(--text-primary);
/* Body bold — emphasis within body (18px bold) */
--type-body-bold-weight: var(--font-weight-bold);
/* Label — form labels, UI chrome (16px) */
--type-label-size: var(--font-size-sm);
--type-label-weight: var(--font-weight-regular);
--type-label-line-height: var(--line-height-body);
--type-label-color: var(--text-primary);
/* Small — captions, metadata, timestamps (14px) */
--type-small-size: var(--font-size-xs);
--type-small-weight: var(--font-weight-regular);
--type-small-line-height: var(--line-height-body);
--type-small-color: var(--text-secondary);
/* --- Spacing: Semantic ---
Spacing above headings proportional to heading visual weight:
Above h2 (40px) → spacing-lg (36px)
Above h3 (27px) → spacing-md (24px)
Above h4 (22px) → spacing-sm (16px) */
--spacing-page-padding: var(--spacing-sm);
--spacing-card-padding: var(--spacing-md);
--spacing-section-gap: var(--spacing-md);
--spacing-element-gap: var(--spacing-xs);
--spacing-label-gap: var(--spacing-3xs);
/* --- Elevation --- */
--elevation-flat: var(--shadow-e0);
--elevation-card: var(--shadow-e1);
--elevation-dropdown: var(--shadow-e2);
--elevation-modal: var(--shadow-e3);
/* --------------------------------------------------------------------------
LAYER 3: COMPONENT TOKENS (STUBS)
Specific to individual components. Extend as the system matures.
These are starting points — not a complete component library.
-------------------------------------------------------------------------- */
/* --- Button --- */
--button-primary-bg: var(--brand-accent);
--button-primary-text: var(--color-white);
--button-primary-hover-bg: var(--color-orange-400);
--button-primary-radius: var(--radius-m);
--button-primary-padding-x: var(--spacing-md);
--button-primary-padding-y: var(--spacing-xs);
--button-primary-font-size: var(--font-size-base);
--button-primary-font-weight: var(--font-weight-regular);
--button-primary-shadow: var(--shadow-e1);
/* Variant clusters alias padding / font-size / font-weight to the primary
cluster so every variant has a complete contract. Override an alias if a
specific variant truly needs different sizing — but the default is "use
the primary cluster's spacing and type rules so all four variants match
visually beyond the colour story." */
--button-secondary-bg: var(--brand-primary);
--button-secondary-text: var(--color-white);
--button-secondary-hover-bg: var(--color-petrol-300);
--button-secondary-radius: var(--radius-m);
--button-secondary-padding-x: var(--button-primary-padding-x);
--button-secondary-padding-y: var(--button-primary-padding-y);
--button-secondary-font-size: var(--button-primary-font-size);
--button-secondary-font-weight: var(--button-primary-font-weight);
--button-outline-bg: transparent;
--button-outline-text: var(--brand-primary);
--button-outline-border: var(--brand-primary);
--button-outline-border-rule: var(--stroke-xs) solid var(--button-outline-border);
--button-outline-radius: var(--radius-m);
--button-outline-padding-x: var(--button-primary-padding-x);
--button-outline-padding-y: var(--button-primary-padding-y);
--button-outline-font-size: var(--button-primary-font-size);
--button-outline-font-weight: var(--button-primary-font-weight);
--button-outline-hover-bg: var(--color-petrol-100);
--button-ghost-bg: transparent;
--button-ghost-text: var(--brand-primary);
--button-ghost-radius: var(--radius-m);
--button-ghost-padding-x: var(--button-primary-padding-x);
--button-ghost-padding-y: var(--button-primary-padding-y);
--button-ghost-font-size: var(--button-primary-font-size);
--button-ghost-font-weight: var(--button-primary-font-weight);
--button-ghost-hover-bg: var(--color-petrol-100);
/* Confirm button — completion/sign-off actions only.
"Klarmarkera", "Godkänn", "Signera", "Markera som klar".
Not for generic "yes/ok" in dialogs — use primary for those. */
--button-confirm-bg: var(--color-confirm-500);
--button-confirm-text: var(--color-white);
--button-confirm-hover-bg: #219a52;
--button-confirm-radius: var(--radius-m);
/* Small button variant */
--button-small-font-size: var(--font-size-xs);
--button-small-padding-x: var(--spacing-sm);
--button-small-padding-y: var(--spacing-3xs);
/* --- Tab Navigation ---
Sticky horizontal tabs below page headings.
Used for sub-navigation within a section (not primary nav).
Active tab indicated by underline in accent color, not background change.
Sticks below topbar on scroll (z-index between sticky and sidebar). */
--tab-nav-bg: transparent; /* Inherits page surface */
--tab-nav-border-bottom: var(--border-default); /* Colour only */
--tab-nav-border-bottom-rule: var(--stroke-xs) solid var(--tab-nav-border-bottom); /* Full shorthand */
--tab-nav-padding-x: var(--spacing-sm);
--tab-nav-padding-y: var(--spacing-xs);
--tab-nav-gap: var(--spacing-md); /* Between tab items */
--tab-nav-font-size: var(--font-size-sm);
--tab-nav-font-weight: var(--font-weight-regular);
--tab-nav-color: var(--text-secondary);
--tab-nav-color-active: var(--text-primary);
--tab-nav-color-hover: var(--text-primary);
--tab-nav-indicator-color: var(--color-petrol-500); /* Underline on active tab */
--tab-nav-indicator-width: 2px;
--tab-nav-sticky-z: var(--z-sticky); /* Sticks on scroll */
/* --- Card ---
Cards use border + shadow together on sand backgrounds.
Border provides definition; shadow provides subtle lift.
Evidence: Hälsodeklarationer prototype — "the subtle line
that helps with contrast making them pop subtly." */
--card-bg: var(--surface-card);
--card-radius: var(--radius-s);
--card-padding: var(--spacing-card-padding);
--card-border: var(--border-default); /* Colour only — for fine-grained composition */
--card-border-rule: var(--stroke-xs) solid var(--card-border); /* Full shorthand — `border: var(--card-border-rule)` Just Works */
--card-shadow: var(--elevation-card);
/* --- Input ---
Label above field (Fixed). Mark optional fields, not required.
Error: "what happened + what to do" below the field.
Disabled vs read-only: see governance comments below. */
/* Field box */
--input-bg: var(--color-white);
--input-border: var(--border-default); /* Colour only */
--input-border-rule: var(--stroke-xs) solid var(--input-border); /* Full shorthand */
--input-border-focus: var(--border-focus);
--input-border-error: var(--border-error);
--input-radius: var(--radius-m);
--input-padding-x: var(--spacing-sm);
--input-padding-y: var(--spacing-xs);
--input-font-size: var(--font-size-base);
--input-text: var(--text-primary);
--input-placeholder: var(--text-tertiary);
/* Label — always above the field */
--input-label-size: var(--font-size-sm);
--input-label-weight: var(--font-weight-bold);
--input-label-color: var(--text-primary);
--input-label-gap: var(--spacing-3xs); /* 4px between label and field */
/* Helper text — below field, replaced by error on validation failure */
--input-helper-size: var(--font-size-xs);
--input-helper-color: var(--text-secondary);
/* Error — replaces helper text position on validation failure.
Pattern: "what happened + what to do" (from den nära experten digital voice).
Input gets aria-invalid="true", error msg linked via aria-describedby. */
--input-error-color: var(--color-error-500);
--input-error-size: var(--font-size-xs);
--input-error-border: var(--border-error); /* red ring on the input itself */
/* Optional indicator — "(valfritt)" suffix in label.
Mark optional fields, not required. In clinical forms 80%+ of fields
are required — marking all of them creates noise. */
--input-optional-color: var(--text-secondary);
/* Focus — petrol ring. Consistent with keyboard navigation patterns.
Uses focus-visible (not focus) to avoid showing ring on mouse click. */
--input-focus-ring: var(--state-focus-ring);
--input-focus-ring-offset: 2px;
/* Disabled — use sparingly.
Patient-facing: prefer explain-on-click over disabled state.
Show what's possible, explain prerequisites to unlock it.
Instrumental: disabled acceptable when workflow context is
self-evident to the user (e.g. clinician knows they must
complete the note before signing). */
--input-disabled-bg: var(--color-gray-100);
--input-disabled-text: var(--text-disabled);
--input-disabled-border: var(--color-gray-100);
/* Read-only — looks like content, not a greyed-out input.
For data that can be unlocked to editable (role-based or
state-based), present in an input-shaped container with a
lock icon. Otherwise present as plain content text. */
--input-readonly-bg: var(--surface-page); /* sand — blends with page */
--input-readonly-text: var(--text-primary);
--input-readonly-border: transparent;
/* --- Badge --- */
--badge-radius: var(--radius-full);
--badge-font-size: var(--font-size-xs);
--badge-padding-x: var(--spacing-2xs);
--badge-padding-y: var(--spacing-3xs);
/* --- Table ---
Two table types share the same token foundation:
Read tables (display) and Work tables (interactive/editable).
Density is user-selectable in instrumental surfaces. */
/* Row density — three levels */
--table-row-height-compact: 36px;
--table-row-height-default: 48px;
--table-row-height-comfortable: 64px;
/* Cell spacing */
--table-cell-padding-x: var(--spacing-sm);
--table-cell-padding-y: var(--spacing-2xs);
/* Header */
--table-header-bg: var(--surface-subtle-cold);
--table-header-text: var(--text-primary);
--table-header-weight: var(--font-weight-bold);
--table-header-size: var(--font-size-xs);
--table-header-transform: none;
--table-header-letter-spacing: 0.05em;
/* Body */
--table-body-size: var(--font-size-sm);
--table-body-color: var(--text-primary);
/* Row states */
--table-row-hover-bg: var(--surface-subtle-warm);
--table-row-selected-bg: var(--color-petrol-100);
--table-row-border: var(--border-default); /* Colour only */
--table-row-border-rule: var(--stroke-xs) solid var(--table-row-border); /* Full shorthand */
/* Zebra striping — optional, off by default */
--table-row-stripe-bg: var(--color-sand-100);
/* --- Motion ---
Animation serves feedback and state communication only, not decoration.
Four duration levels, two easing curves. No more.
Surface temperature selects from the same tokens: instrumental stays
at instant/fast, communicative can use moderate/slow.
Only animate transform and opacity (GPU-composited, no layout reflow).
prefers-reduced-motion: reduce disables ALL animation (Fixed, a11y).
See aleris-baseline-animation.md for full guidance. */
/* Duration — four levels */
--duration-instant: 100ms; /* Focus rings, color shifts, checkbox toggles — feels immediate */
--duration-fast: 200ms; /* Buttons, hover, tooltips, badge updates — system reacting */
--duration-moderate: 350ms; /* Modals, accordions, sidebars, card flip — spatial tracking */
--duration-slow: 500ms; /* Page transitions, major layout shifts — used sparingly */
/* Easing — two curves */
--ease-out: cubic-bezier(0.0, 0.0, 0.2, 1); /* Elements arriving: fast start, gentle stop */
--ease-in-out: cubic-bezier(0.4, 0.0, 0.2, 1); /* Elements moving: smooth at both ends */
/* Never use ease-in alone — slow start reads as unresponsive. */
/* Keyframe primitives (reference — apply via CSS @keyframes)
fade-in: opacity 0→1, translateY 4px→0 (ease-out, duration-fast)
scale-in: opacity 0→1, scale 0.97→1 (ease-out, duration-fast)
fade-in suits content entering the viewport (cards, list items, panels).
scale-in suits overlays and modals (dialogs, dropdowns, popovers).
Collapse is slightly faster than expand (300ms vs 350ms) — closing
feels decisive, opening feels gradual. */
/* --- Skeleton / Loading ---
Shimmer over spinners. Products compose primitives (rectangle, circle,
text-line) to match their content layouts.
< 2s: shimmer only. > 2s: shimmer + progress. > 10s: navigate away + notify.
No animation in email — all email layouts are static (Fixed). */
--skeleton-bg: var(--color-gray-100);
--skeleton-shimmer: linear-gradient(90deg, var(--color-gray-100) 0%, var(--color-sand-100) 50%, var(--color-gray-100) 100%);
--skeleton-duration: var(--duration-moderate); /* 350ms per sweep */
--skeleton-easing: var(--ease-in-out);
--skeleton-radius-rect: var(--radius-s); /* Rectangle primitives */
--skeleton-radius-circle: var(--radius-full); /* Avatar/icon placeholders */
/* --- Adaptive Responsive ---
Component swap at breakpoints, not just scaling.
The grid (12-col, container queries) handles layout.
These tokens handle the stacking context. */
/* Z-index hierarchy — canonical stacking order */
--z-base: 0;
--z-dropdown: 10; /* Dropdowns, select menus, popovers */
--z-sticky: 20; /* Sticky table headers, fixed elements within scroll */
--z-sidebar: 30; /* Sidebar navigation (when overlaying on mobile) */
--z-search: 40; /* Command palette / search overlay */
--z-topbar: 50; /* TopBar — always on top of page content */
--z-modal: 100; /* Dialogs, sheets, modal overlays */
--z-toast: 110; /* Toast notifications — above modals */
/* --- Table Density Persistence ---
Density (compact/default/comfortable) is a user preference,
not a per-view design decision. Persist via localStorage
or user profile. Key: 'aleris-table-density'.
Default if no preference stored: 'default' (48px rows). */
--table-density-preference-key: 'aleris-table-density';
/* --- Images ---
Format strategy: AVIF → WebP → JPEG (progressive enhancement via <picture>).
Component sets the aspect ratio; image fills via object-fit: cover.
EXIF metadata stripped by default — GPS data from clinic photos is a privacy risk.
See aleris-baseline-images.md for full guidance. */
/* Aspect ratios — used with CSS aspect-ratio property */
--image-ratio-hero: 16 / 9; /* Heroes, banners, video thumbnails */
--image-ratio-content: 3 / 2; /* Articles, staff portraits, cards — most versatile */
--image-ratio-square: 1 / 1; /* Avatars, thumbnails, small functional images */
--image-ratio-document: 4 / 3; /* Documentation, clinical photography */
/* Border radius on images */
--image-radius-default: var(--radius-m); /* 8px — softened corners */
--image-radius-avatar: var(--radius-full); /* Circular for profile images */
--image-radius-none: 0; /* Full-bleed images */
}