# Baseline — AI Instructions

The Aleris brand is documented in two layers. **Foundation** defines identity and reasoning: colour, typography, voice, imagery, and the two reasoning tools (constant/contextual, emotional modes). **Baseline** is the implementation layer: the token architecture, component rules, and pattern library that apply Foundation across digital products. Read Foundation first; read Baseline for how to execute it.

When Foundation and Baseline conflict, Foundation takes precedence.

Before writing or modifying any UI code in an Aleris product, use only tokens from `aleris-tokens.css`. When uncertain about a decision, flag it as an open question in the code rather than guessing:

```css
/* OPEN: [description of the decision needed] */
```

---

## Token system

All values come from `tokens/aleris-tokens.css` — a three-layer architecture:

- **Primitives** — raw values (colors, spacing, type sizes). Platform-independent.
- **Semantic** — primitives mapped to usage (text colors, surface colors, states). Reference these in product code.
- **Component** — semantic tokens composed into component-level clusters (button, input, card, table, tab-nav).

Machine-readable token data with `usage` and `constraint` fields is in `tokens/baseline-tokens.json`. Consult it for lookup; where the two disagree, `constitutional/tokens-are-canonical.md` § 1 says which one wins and why no document may restate a value from either.

**Never use raw values.** No hex colors, no pixel values for spacing, no raw Tailwind color classes. Everything goes through tokens. This is a rule about product code, and it is not the same as the one above, which governs what a document may say.

**The one documented exception: a page rendered before the token stylesheet can load.** A server-rendered pre-auth surface (login, an error page, a maintenance page) is often served before the built, hashed-filename stylesheet is reachable — it sits behind the gate it is rendering, so it cannot `@import` or link `aleris-tokens.css` and has to inline its colours. The exception exists because a real build's first version of exactly this page used two values that aren't Aleris colours at all (`#d9663d`, `#e6e1da`) — there was nothing here to check against. **The four values a pre-auth page needs, cited from canonical rather than invented for this case:** page background `--surface-page` (sand-100, `#f2ece4`), text `--text-primary` (petrol-500, `#004851`), primary button `--color-orange-600` (`#d14811`), border `--border-default` (gray-100, `#d7d2cb`). Inlining these four is the exception; every other surface still goes through the token file. Pin the values with a test that extracts every hex a pre-auth page uses and asserts each one is one of these four — a build cannot see this rule fail otherwise.

**Serving this file.** The comments in `aleris-tokens.css` are its documentation and they are not optional — every `@usage` and `@constraint` annotation is a rule, and the supersede notes are why a value is what it is. They are also not for browsers. **Strip comments from the stylesheet at build time, keep them in the source a developer opens, and leave one pointer comment in the emitted file saying where the reasoning went.** Stripping is not minifying: remove the comments and the blank runs they leave behind, and nothing else — whitespace crunching, shorthand rewriting and selector merging each change what the browser computes if they are wrong, and the weight here is almost entirely in the prose.

Two reasons, and the second is the one that bites. **Weight:** the file is 50,487 bytes, roughly 34,000 of them comment. Gzipped it is 15.9 KB with the prose and 3.3 KB without, so about four fifths of the compressed download is English sentences — prose has little redundancy to squeeze while a token file is almost nothing but redundancy. On a page that inlines this file that is the largest single saving available, and one consumer measured 50.0 KB of comment in 82.4 KB of inlined stylesheet, on a patient-facing surface, on every load. **Build safety:** a comment body can contain a sequence that closes it early. Browsers recover silently and strict minifiers do not — this has already broken one consumer's production build, in dev-invisible fashion, and the fix at the time was to reword the comment. A build that strips comments before the minifier sees them is not exposed to it at all.

`patientguide/frame/lib/css.js` is a working implementation, including the guard that refuses to emit a stylesheet when an unterminated comment opener survives the strip — that failure mode silently swallows every declaration up to the next closer, which is a page with rules missing and no error anywhere.

---

## Hard rules

They apply to every surface, every product.

1. **Page background: sand, with everything layered on it.** Communicative = `--surface-page` (sand-100, #f2ece4). Instrumental = `--surface-page-instrumental` (sand-50, #faf8f6). The page's own background is always one of these two; a white or coloured section is a layer on top of it, even when it fills the screen. A section takes one of four colours: `--surface-section-white`, `--surface-section-cold` (petrol-100), `--surface-section-warm` (orange-300), or `--surface-section-strong` (petrol-500) for the one strong section on a page. orange-100 is not a section colour – it measures 1.01:1 against sand-100 and the two read as one surface.
2. **Cards and surfaces: white, not sand.** `--surface-card` is white. Sand on cards inverts the hierarchy. Cards stay white on any section; a white card on a white section is separated by its border and shadow.
3. **One primary CTA per screen.** Interactive orange (`--color-orange-600`, via `--button-primary-bg`) marks the primary action on a screen. Do not place two orange buttons in the same view. `--brand-accent` (orange-500) is the brand orange for accent, decoration and print, and no longer carries the primary button – white text on it fails AA.
4. **Status colors for state only.** Error red, warning amber, confirm green, goal colors — never decorative.
5. **Confirm green is an indicator, never a button.** `--color-confirm-500` (#4f866e) marks completion — status dots, badges, chips. It is **not an interactive fill**: white on it is 4.23:1, below the floor at rest, before any interaction. The confirm *button variant* was retired rather than recoloured; a completion action takes the petrol button form plus a required check glyph, so "I'm done" is carried by the glyph and the verb. Keep it distinct from `--color-goal-achieved` (#2e8540), which indicates "target met" on dashboards. **This is the rule of record for confirm; other documents reference it rather than restating it.**
6. **No all caps in content.** Sentence case everywhere. The only exception is abbreviations (AB, MRI) — see `foundation/typography.md`.
7. **Animate what does not move the layout, unless the user moved it.** Transform and opacity animate freely. Colour, background colour, shadow and stroke may transition to show a change of state. Size may animate only when the user caused it and it is contained — opening an accordion, expanding a panel — one element at a time, within `--duration-moderate`. Nothing changes size on its own: not on load, not on scroll, not behind the user's back. `prefers-reduced-motion: reduce` disables all of it.
8. **No animation in email.** All Aleris email layouts are static.
9. **14px is the floor.** `--font-size-xs` (14px) is the smallest text in the system. Nothing goes below it.
10. **No font-weight 400.** Museo Sans has no 400 weight. Using it causes browser synthesis. Use `--font-weight-regular` (500) or `--font-weight-bold` (700).

---

## Surface temperature

Baseline applies `foundation/constant-contextual.md` to surfaces through two modes: **communicative** and **instrumental**. Mode is determined by the situation, never the user's role — a patient managing their treatment plan in MyAleris is in instrumental mode; a staff member reading an internal newsletter is in communicative mode.

**Communicative** — Aleris tells, guides, explains, welcomes. Patient-facing content, marketing, service introductions. ~~onboarding~~ **Removed from this list.** Onboarding names when a flow happens, not what the person is doing in it, so it carries no mode of its own. Read the situation: a welcome sequence that introduces a service is communicative; setting up a treatment plan is instrumental from its first screen. The same holds for any word that names a stage rather than a situation — activation, renewal, offboarding.
- Page background: `--surface-page` (sand-100)
- Cards mandatory for content structure
- Generous spacing (`--spacing-lg` to `--spacing-3xl`)
- Full type scale, typography has room to breathe
- ~~Content spans 6–8 of 12 grid columns~~ **Superseded:** one reading column at every width, `--grid-column-communicative` — 640px up to a 768 window, growing a quarter of every further pixel to 768px from 1280 up. Decided by looking at three candidates rendered side by side (`baseline/conformance/layout/index.html`). The column holds 88 characters of 18px body at 768 and 105 at 1280, past the 45–75 guideline; that was seen and chosen, and the token says so. "6–8 columns" was never a measure proxy — 600–800px is 82–109 characters.
- Max-width: `--grid-max-width-communicative` (1200px)

**Instrumental** — The user works, navigates, configures, monitors. Tools, admin, dashboards, documentation, booking flows (a booking flow is instrumental throughout).
- Page background: `--surface-page-instrumental` (sand-50)
- Cards optional — content can live directly on the surface
- Tighter spacing. ~~compressed type range~~ **Superseded — surface temperature does not change type size.** Type size is the reader's to set and the browser already gives them that control — every size in the scale is a rem value, so a reader who raises their root font size raises the whole page, and the 14px floor is a floor at default settings rather than a cap. Instrumental density comes from layout utilisation and information hierarchy, which is what `aleris-design-governance.md` § Scanning vs. attending already says: *"not from spacing compression"*. "Compressed type range" told a builder to shrink type and named no token to do it with, because no mode-scoped type token has ever existed — the five mode-scoped tokens are all grid or background. Asserted in `tokens.test.ts`.
- Content can span all 12 columns. **Tiles and card grids:** fluid, no breakpoints — `repeat(auto-fit, minmax(var(--grid-tile-min-instrumental), 1fr))`, 14rem tiles, so the column count falls out of the width: one to 390, two at 640, three at 768, four at 1024, five from 1280. Four tiles leave an empty fifth slot from 1280 up; rendered, seen, chosen over the stepped 12→6→1 and 12→1 candidates. Tables scroll inside their own container at every width.
- Max-width: `--grid-max-width-instrumental` (1440px or fluid)

Mode does not shift between steps within a single flow.

**Sticky elements.** Three things may stay in view while the page scrolls: the site header, the portal side nav, and an in-page list of a long page's sections, which also marks the section being read. Nothing else sticks to the page. Inside a table's own scroll area its header row may stick, because it takes no space from the page. Tab navigation sticks only when it is the page's list of sections.

---

## Component shapes

Radius by functional role:

- **Cards**: `--radius-l` (16px) — from computed styles measured live on aleris.se/.no/.dk (every card ships at 16px, not Baseline's former 4px). Derived inward: nested elements take the card's own radius at zero gap, floored at 0. Cards only, for now — panels, modals and tables were never measured live and stay `--radius-s`.
- **General containers** (panels, modals, tables): `--radius-s` (4px)
- **Interactive elements** (buttons, inputs, dropdowns): `--radius-m` (8px)
- **Indicators** (badges, tags, avatars): `--radius-full` (100px)
- No full-pill buttons exist in Aleris interfaces.
- **No generated side accent.** **Never** mark an element as active, selected or important with a coloured bar, border or shadow along one of its sides. **Because** it is a default of AI design tools, not an Aleris choice. It makes the work read as generated rather than designed, and it carries state in a thin strip of colour alone. **Instead** mark the state with weight and fill: bold petrol text on a petrol-100 fill for an active navigation item, a 1px petrol-500 border all the way round for a selected card. A quotation may keep a thin, flat, neutral rule along its side (`--border-default`): that is a typographic convention, not a state marker. A rounded, shadowed or curved side treatment is not.
- **Flush top-of-card media** (a hero image with zero clearance from the card's top edge): carries no radius of its own. The card clips its contents to its own rounded shape (`--card-media-overflow: hidden`), which renders the image's top corners matching the card and its bottom edge square, for free — nothing to keep in sync, and the inversion the 4px card tier used to produce against an 8px image (see the corner-radius reasoning below) cannot recur. Scoped to cards that actually contain flush media, not every card, so a plain card stays free to let content overhang its edge without a second decision. Does not apply to an inset image with clearance on all sides, which keeps `--image-radius-default` (8px) unchanged.
- The radius set is `0 / l / m / s / full`.

**The corner-radius reasoning.** Baseline assigned radius by role — container, interactive, indicator — which is easy to teach but ignores the geometric convention that nested rounded rectangles must be concentric: an outer radius has to be at least its inner radius plus the gap between them, or the two curves aren't parallel and the corner reads as an error. At Baseline's 24px card padding, nothing sits close enough to a card's edge for this to show. It stopped being invisible the moment an image sits flush against the card. Rather than resolve that one pair in isolation, the question was reframed as one problem — what should the default corner state be — and answered from what production had already settled empirically rather than from a rule invented for the occasion. A four-model comparison (`baseline/conformance/radius/index.html`, unpublished) made the candidates visible side by side before the decision, per the same design-by-looking approach as `baseline/buttons/index.html`.

---

## Elevation

Shadows come from the ladder `--shadow-e0` to `--shadow-e3`, through the elevation tokens: a card rests at `--elevation-card` (`--shadow-e1`), a dropdown at `--elevation-dropdown` (`--shadow-e2`), a modal at `--elevation-modal` (`--shadow-e3`). **One featured element may rest one step up.** On a communicative page, a single featured element, such as the text card of a hero, may rest at `--shadow-e2` instead of `--shadow-e1`. One per page; a page where several things rest one step up has no featured element.

---

## Lists

Items in a list are separated by space from the spacing scale. A list of six or more items may also carry a horizontal divider between items (`--border-default`); a shorter list never does. This holds for lists across Aleris design. Tables are not lists and keep their own rules in § Tables.

---

## Typography

See `foundation/typography.md` for the typography system and principles. Baseline implementation:

- Reference typography tokens from `aleris-tokens.css` (`--type-body-*`, `--type-lead-*`, `--type-h1-*`, etc.) rather than specifying pixel sizes.
- Font weights available: `--font-weight-regular` (500) and `--font-weight-bold` (700). Do not use font-weight 400 — Museo Sans has no 400 weight and browser synthesis distorts the letterforms.
- **The licensed Museo Sans has no tabular figures, at either weight.** Setting `font-variant-numeric: tabular-nums` against it changes nothing and says so nowhere — measured in a browser against Arial as a control, digit advance widths are identical with and without the property, and the ten digits spread 8.72px at weight 500 and 7.50px at 700 where Arial spreads 0. At a 16px value size that puts 8.2px between "111,1" and "000,0". Right-alignment is what aligns a numeric column; the property is a silent no-op. *(Measured by `sundviktlakemedel`, 2026-08-25.)*
- **The licensed Museo Sans has no arrow glyphs, and no check mark.** U+2192 →, U+2190 ←, U+2264 ≤, U+2265 ≥ and U+2713 ✓ are absent from both licensed weights, so any of them in running text falls back to another face silently — a different stroke weight and baseline beside the words it sits in. Present and safe: en and em dash, bullet, ×, …, °. Use an icon for an arrow or a check, never the character. *(Measured 2026-09-05 by reading the character maps of `MuseoSans_500-webfont.woff2` and `_700`: 229 and 228 mapped codepoints. First noticed by `patientguide`, 2026-08-13.)*
- `--type-lead-*` is used once per section, after the heading.

---

## Spacing

Modular scale, 1.5 ratio, 4px grid. Use tokens — never fabricate values.

| Token | Value | Typical use |
|-------|-------|-------------|
| `--spacing-3xs` | 4px | Minimum gap, label-to-field |
| `--spacing-2xs` | 8px | Tight inline gaps |
| `--spacing-xs` | 12px | Related elements, input padding-y |
| `--spacing-sm` | 16px | Component padding |
| `--spacing-md` | 24px | Section spacing, card padding |
| `--spacing-lg` | 36px | Between components |
| `--spacing-xl` | 48px | Between sections |
| `--spacing-2xl` | 72px | Major section breaks |
| `--spacing-3xl` | 96px | Page-level (communicative only) |

---

## Buttons

Six variants, each with a job:

- **Primary** (orange): "Do this" — main action, booking, submission
- **Secondary** (petrol): "Go here" — navigation, exploration
- **Outline**: Less prominent alternative
- **Ghost**: Tertiary, minimal visual weight
- ~~**Confirm** (green): "I'm done" — completion, sign-off. Only for: Klarmarkera, Godkänn, Signera, Markera som klar. Not for generic "OK" in dialogs.~~
- **Small**: Compact variant for tight spaces

> **Superseded.** The confirm button variant listed above is retired; hard rule 5 is the rule of record for confirm.

All buttons use `--radius-m` (8px). No full-pill buttons.

**Contrast is the constraint.** No interaction state may take a component's text below the AA floor against its own fill.

**Darkening is the convention for interaction states.** If a component already sits at the palette's highest contrast, darkening will lower contrast. That is permitted so long as it stays above the floor.

> **Superseded.** The two rules above replace *"Hover lightens, never darkens"*: the floor is the rule, the direction is the convention.

**Primary on a petrol surface inverts.** orange-600 is specified for light surfaces and must never be placed on petrol. On a petrol ground the primary action becomes white fill with petrol-500 text (10.27:1) – `--button-primary-inverse-*`.

**No primary on the warm section.** orange-600 measures 2.82:1 against `--surface-section-warm` (orange-300), below the 3:1 a fill needs against the surface it sits on. An action on a warm section takes the secondary form. On `--surface-section-cold` the primary holds at 3.40:1.

**Filled buttons carry a bevel.** At rest a filled button has a soft shadow down and to the left and a thin highlight along the top edge, facing the light; when pressed it sinks into an inward shade. This is a top-edge highlight from the light source, not a side accent, so it does not conflict with § Component shapes. Tokens: `--button-{variant}-shadow` at rest, `--button-press-shadow` when pressed, together with `--state-press-translate-y`.

**Hover on a clickable card.** A card that is a link moves from the card shadow to the next step up (`--shadow-e2`) and rises 2px on hover; an arrow in it moves 4px towards where it leads. Transform and shadow only, `--duration-fast`, ease-out, off under reduced motion.

**States.**

- **Hover is a ring, not a fill.** One petrol ring specified for six variants on two surfaces produced two invisible focus rings – petrol-500 on a petrol-500 fill (secondary) and petrol-500 on the petrol-500 page (primary-inverse), both 1.0:1. Rather than add another fill step to fix hover and a second mechanism to fix focus, the two merged: **hover draws the outer ring only, focus draws both.** No `--button-*-hover-bg` token exists any more, and none should be added – a hover fill that changes colour is what the "hover lightens/darkens" rule used to constrain, and the constraint no longer applies because there is no fill to constrain.
- **Focus is a dual ring: 2px white inner, 2px petrol-500 outer**, both always drawn. Whichever ground a button sits on, one ring contrasts – the inner against the button's own fill, the outer against the page. `primary-inverse` swaps the two, because it sits on petrol rather than sand: `--button-primary-inverse-focus-ring` (outer position) is white, `--button-primary-inverse-focus-ring-inner` (inner position) is petrol.
- **Active (press) is per-variant, not a shared tint.** `--state-active` (sand-500) was retired rather than repointed – it was never a button fill, and reads 1.93:1 under white. Primary and primary-inverse reuse what used to be their hover fill (orange-700, petrol-100); secondary and confirm take a new step, petrol-700 (13.88:1); outline and ghost take petrol-100. A supplementary, unasserted second channel – `--state-press-shade`, a neutral inset, plus a 1px translate dropped under `prefers-reduced-motion` – is deliberately not petrol-tinted, for the same reason a single focus ring failed: a petrol inset on a petrol fill is invisible.
- **Buttons do not disable.** A disabled button dissolves into the page (gray-100 on sand-100 is 1.28:1) and offers no explanation for why it is unavailable. Patient-facing: prefer explain-on-click – clicking reveals the prerequisite rather than the control simply not responding. Instrumental: a transparent, gray-500-edged treatment is acceptable where the workflow context is self-evident to the user. `--state-disabled-*` remains for inputs, which have their own disabled semantics – see § Forms.
- **Buttons must survive text-zoom without causing horizontal scroll.** From a real conformance failure: a normal-length label at 200% text size on a 320px viewport measured wider than its container and pushed the page into horizontal scroll — an EN 301 549 11.7 failure, not a cosmetic one, since 11.7 requires layout to follow the platform's text-size setting. `max-width: 100%` and `white-space: normal` (label wraps to a second line rather than the button overflowing) are required on the button primitive, not optional per-instance styling. **The same failure recurs at the row level**: a flex row of buttons or other controls (a role switch, a toolbar) needs `flex-wrap` for the identical reason — a non-wrapping row overflowed a 375px viewport before the fix, independently of any single button's own width.

See `baseline/buttons/index.html` for every variant against every state, measured live from these tokens, and `tokens/tokens.test.ts` for the fitness check. One documented failure remains: ghost's press fill (petrol-100, reused from outline) has no border to carry its boundary, at 1.13:1 against sand-100 – tracked as `KNOWN_BOUNDARY_FAILURES['ghost/active']`. Ghost has no instances in `app/` or `components/`; fixing it means giving ghost a border, which is a design call about what distinguishes it from outline, not made here.

---

## Forms

- Labels above fields.
- Mark optional fields "(valfritt)", not required fields.
- Error text: "what happened + what to do" below the field, replacing helper text.
- `aria-invalid="true"` + `aria-describedby` for error linking.
- Focus: `focus-visible` with petrol ring, 2px offset. Never `focus`.
- Disabled: use sparingly. Patient-facing: prefer explain-on-click. Instrumental: acceptable when workflow context is self-evident.
- Read-only: looks like content, not a greyed-out input.
- **Error text sits on a white card or an instrumental ground, never directly on the communicative ground.** `--input-error-color` (error-500) measures 5.03:1 on white (clears), 4.74:1 on sand-50 (clears) and 4.28:1 on sand-100 (fails). `--input-error-size` is `--font-size-xs` at regular weight, so the failing case is the ordinary one — a field error at the smallest size in the system. A form on a communicative page goes in a card, which is already the rule there (§ Surface temperature: *"Cards mandatory for content structure"*). Enforced by `tokens.test.ts` § *"error text clears AA on every ground this rule permits"*, which reads the permitted grounds out of this bullet rather than holding its own list.

---

## Status messages

**Icon plus label, on error and on success both.** A status message carries its meaning in an icon and in words, never in the fill or text colour alone – red and green are the pair that collapses under red-green colour vision deficiency, which is roughly 8% of men. Success already required a checkmark in the goal-status rules; error now carries the same requirement, so the two are distinguishable with colour removed.

This is the domain implementation of the constitutional rule that meaning is never carried by colour alone. The rule itself lives once, at the constitutional layer – this section says what it means for a status message, it does not restate the rule.

---

## Tables

Two types: **read tables** (display data) and **work tables** (interactive).

- Three density levels: compact (36px), default (48px), comfortable (64px)
- **Note to builders:** user-selectable density that persists as a preference is the expectation for instrumental work tables, not a hard requirement every table must ship on day one. Build toward it; a first cut that ships with only the default density is a known gap, not a violation.
- Headers quieter than data (smaller, sentence case)
- Identifier in column one, always
- Numbers right-aligned, text left-aligned — right-alignment is what actually aligns a numeric column here, for the reason under Typography above
- Actions near the identifier, not at the far right

---

## Motion

Four durations, two curves.

| Token | Value | Use |
|-------|-------|-----|
| `--duration-instant` | 100ms | Focus rings, checkbox toggles |
| `--duration-fast` | 200ms | Buttons, hover, tooltips |
| `--duration-moderate` | 350ms | Modals, accordions, sidebars |
| `--duration-slow` | 500ms | Page transitions (sparingly) |

- `--ease-out`: elements arriving
- `--ease-in-out`: elements moving
- Never use `ease-in` alone — slow start reads as unresponsive.

Loading: skeleton shimmer, not spinners. < 2s: shimmer only. > 2s: shimmer + progress. > 10s: navigate away + notify.

---

## Anti-patterns (healthcare-specific)

If you experience a conflict between design requirements and anti-patterns, please note it and leave a comment.

1. **Consent asymmetry** — consent and decline must be visually equal. Same size, same prominence, same steps.
2. **Cost burial** — surface pricing before any data entry or emotional investment.
3. **Urgency fabrication** — no countdown timers or scarcity messaging unless clinically real and verifiable.
4. **Obstacle withdrawal** — anything booked online can be cancelled online with equal ease.
5. **Informational hiding** — risks and qualifying information inline at point of decision. Never progressively disclosed.
6. **Data maximalism** — collect only what the current service requires. Optional = labeled "(valfritt)".
7. **Default escalation** — no pre-selection on choices with financial, clinical, or privacy consequences.
8. **Emotional exploitation** — no shame, guilt, or fear to drive action. Motivate through clarity.
9. **Exit penalty** — no degraded experience for choosing a less profitable path.
10. **Notification coercion** — notifications for clinical relevance only. Marketing is separate with distinct opt-in.

**Smell test:** Is it easier to accept than to decline? Does the flow hide information that would change the decision? Does it require something not necessary for care?

---

## Accessibility

- WCAG 2.1 AA minimum
- 14px font floor (enforced by token scale)
- Meaning is never carried by colour alone – the rule is constitutional, see `constitutional/accessibility-is-foundational.md`. In this system it means pairing colour with icon, shape, or text, and it means error and success must be distinguishable from each other with colour removed (see Status messages above)
- **44px minimum touch/click target** (`--button-min-height-touch`, `--button-padding-y-touch`) on patient-facing and touch contexts. **Relaxed for dense instrumental surfaces on desktop pointer input:** 24×24 CSS px is the floor there (WCAG 2.1 AA 2.5.8 Target Size Minimum), not 44×44 (AAA 2.5.5 Target Size Enhanced, which 44px implements) — `--button-min-height-pointer`. A dense work table or admin toolbar built for mouse/trackpad use may use the smaller target; anything a patient touches, or anything that could be used on a touchscreen, keeps 44px.
- `focus-visible` for keyboard navigation, never `focus`. **The ring differs by control, and one rule for all of them cannot hold** — a single ring colour collides with something on six button variants across two surfaces (petrol-500 on a petrol-500 fill is 1.0:1). Inputs keep a single petrol ring, 2px offset – see § Forms. Buttons use a dual ring, inner and outer, so one half always contrasts regardless of ground – see § Buttons
- `prefers-reduced-motion: reduce` disables all animation
- `aria-invalid` + `aria-describedby` for form errors

---

## File reference

**Foundation (read first)**

| File | Purpose |
|------|---------|
| `foundation/index.md` | Brand in Brief — entry point to all Foundation pages |
| `foundation/colour.md` | Colour |
| `foundation/typography.md` | Typography |
| `foundation/voice.md` | Voice — "den nära experten" |
| `foundation/constant-contextual.md` | Constant and contextual — a reasoning tool |
| `foundation/emotional-modes.md` | Emotional modes — a reasoning tool |
| `foundation/imagery.md` | Imagery |

**Baseline (implementation)**

| File | Purpose |
|------|---------|
| `data-products/tokens/aleris-tokens.css` | Source of truth — all token values |
| `data-products/tokens/baseline-tokens.json` | Generated — token lookup with usage/constraint |
| `data-products/tokens/aleris-tailwind.css` | Optional Tailwind v4 theme bridge — `bg-aleris-petrol-500`, `p-aleris-md`, and so on, each compiling straight to a token. Generated from the token file, holds no value of its own, adds a namespace and switches Tailwind's own colour palette off; import it after Tailwind |
| `schemas/design-token.md` | The token record's shape, the layer architecture, framework/Figma integration. Not yet in the `agent-baseline` package |
| `principles/design-tokens.md` | Why each token value is what it is. Corpus-only — reasoning, not a build constraint |
| `governance/aleris-anti-patterns.md` | 10 healthcare-specific behavioural guardrails |
| `governance/aleris-design-governance.md` | Design decisions with certainty levels |
| `governance/aleris-privacy-jtbd-analysis.md` | Privacy as interaction design |
| `reference/aleris-progressive-enhancement.md` | Foundational design principle |
| `reference/aleris-grids-tables-dataviz.md` | Grid, table, and dataviz specs |
| `reference/aleris-baseline-animation.md` | Motion system (implements constant-contextual and emotional-modes) |
| `reference/aleris-baseline-images.md` | Image formats, aspect ratios (implements imagery) |
| `voice/` | Digital voice patterns, behaviour notes (implements voice) |
| Icon path data | **3,772 canonical FA Pro Regular SVGs are already extracted and reachable — no FA kit token required.** Fetch a glyph at `https://brand.dev.aleris.ai/icon-picker/svgs/regular/<name>.svg`, with the name index at `public/icon-picker/metadata/icons.json`. Only *re-extraction* on an FA upgrade needs the kit. Lives at `public/icon-picker/svgs/regular/` in the Brand OS repo, regenerable via `scripts/build-icon-metadata.py`. Any name may be used except the motifs `principles/iconography.md` § What we never use excludes |
| `logo/` | The logo files, the four ratified constants (clear space, primary variant, minimum height, prohibitions) and the sender rule. Read `logo/README.md` before placing the logo anywhere. New in `agent-baseline` v0.7 |

---

*Baseline — maintained by Torfinn Almers, Head of Design, Aleris Group.*

> *What changed on this page, and when, is in `workspace/status/changelog.md`. This page carries the rules and the reasoning behind them; the dates live there. A struck-through value beside its replacement is not a change note — it is the rule telling you what it no longer is.*
