Part of the approved Iteration 2 design pack. Read together with design-decision.md, which records binding corrections, including the token-naming corrections that override this file's --syrf-accent/--syrf-brand-ink hex values and the final token names (--syrf-warning-container, --syrf-warning-accent, --syrf-info-container — the --syrf-color-* spellings below are provisional and superseded).
Iteration 2 — token mapping (Wave 3 handoff)¶
Every colour/typography role used by the Study management designs, mapped from the design-system
CSS variable used in the prototype to the production Angular theme. Rule: no raw hexes in
product code — hexes below are the light-mode fallbacks baked into the DS variables.
The supported production dark boundary is .global-dark-theme; --syrf-brand-ink stays navy by design. Material system colors are generated from the same palettes in separate light and dark layers, without requiring CSS light-dark(). Light status values remain unchanged. Within the dark boundary, warning container/accent use Material orange tones 20/80, and the information container uses the SyRF primary palette tone 20. Both modes are measured by the shared token contrast contract; no theme setting is introduced.
Surfaces and text¶
| Role in design | DS token (fallback) | Angular / M3 mapping |
|---|---|---|
| Page + card surface | --syrf-surface (#fdfcff) |
--mat-sys-surface |
| Row detail / op strip / code block bg | --syrf-surface-container-low (#f8f9fd) |
--mat-sys-surface-container-low |
| Progress track | --syrf-surface-variant (#e1e2ec) |
--mat-sys-surface-variant |
| Reduced-motion indeterminate track | --syrf-surface-dim (#dddce4) |
--mat-sys-surface-dim |
| Primary text | --syrf-on-surface (#1a1c1e) |
--mat-sys-on-surface |
| Secondary text, column headers, meta | --syrf-on-surface-variant (#393b42) |
SyRF-bumped --mat-sys-on-surface-variant (see styles.scss override) |
| Toolbar / active-group heading | --syrf-brand-ink (#1c3053) |
--syrf-brand-ink (non-M3 brand surface, both modes) |
Interactive and semantic¶
| Role in design | DS token (fallback) | Angular / M3 mapping |
|---|---|---|
| Links, progress fill, focus outline | --syrf-accent (#4b5e84) |
--mat-sys-primary (M3 tone 40; tone 80 in dark) |
| Cancel button (destructive outline) | --syrf-error-accent (#b3261e) |
--mat-sys-error |
| Needs-attention row tint | --syrf-warning-container (#fdecd3) |
--syrf-color-warning-container |
| Needs-attention group heading | --syrf-warning-accent (#8a5000) |
--syrf-color-warning-accent |
| Revealed (deep-linked) row tint | --syrf-info-container (#dfe8f7) |
--syrf-color-info-container |
| Field/select border | --syrf-outline (#74777f) |
--mat-sys-outline |
| Op strip / error list / Copy borders | --syrf-outline-variant (#c5c6cf) |
--mat-sys-outline-variant |
M3 Expressive controls on the Studies workspace and the Searches page (slice 12)¶
The C+B′ board (Studies-tc.dc.html) draws its buttons and progress with prototype tokens. Production maps them as
follows. They apply with studiesWorkspace on; see slice 12 in implementation-plan.md.
| Board token | Board use | Production token | Why |
|---|---|---|---|
--syrf-accent |
links, focus outline, "Checking" PDF state | --syrf-focus-ring for focus, --syrf-link for links, --mat-sys-primary for fills and text |
--syrf-accent is navy in both modes: 8.39:1 on the light surface, 1.88:1 on the dark one |
--syrf-success-accent |
Available PDF state | --syrf-success |
not defined in production |
--syrf-warning-text |
proposed PDF state | --syrf-warning-accent |
not defined in production |
--syrf-primary-tint (as --mat-sys-secondary-container) |
tonal fill, progress track | --mat-sys-secondary-container |
the board aliases it |
--mat-sys-surface on --mat-sys-error |
Activity badge text | --mat-sys-on-error on --mat-sys-error |
keep production's role pair |
The PDF state colours apply when a slice adds the board's coloured labels; slice 12 adds none.
| Control | Roles | Contrast (light / dark), measured by the token contrast contract |
|---|---|---|
Filled button (b-f) |
--mat-sys-on-primary on --mat-sys-primary |
6.49 / 7.74 (needs 4.5) |
Tonal button (b-t) |
--mat-sys-on-secondary-container on --mat-sys-secondary-container |
7.28 / 7.28 (4.5) |
Text button (b-x) |
--mat-sys-primary on the surface ladder |
at least 5.03 / 6.43 (4.5) |
Outlined button (b-o) |
label --mat-sys-on-surface-variant, boundary --mat-sys-outline |
label 7.20 / 6.10 (4.5), boundary 3.46 / 3.51 (3), lowest of slice 12's surfaces |
Destructive text button (b-x b-e) |
--mat-sys-error label and state layer |
5.00 / 5.51 (4.5), lowest of slice 12's surfaces |
| Focus ring | outline: 2px solid var(--syrf-focus-ring); outline-offset: 2px (m3x.focus-ring inside :focus-visible) |
5.03 / 5.51 (3), lowest of slice 12's surfaces |
Linear progress (.lp) |
indicator --mat-sys-primary on track --mat-sys-secondary-container, 4 px, at most 320 px wide |
5.05 / 5.51 (3) |
Slice 12's surfaces are surface, surface-container-low, surface-container, secondary-container,
error-container (an attention item in Activity) and --syrf-info-container. The text, filled, tonal and progress
rows are enforced by the token contrast contract (scripts/token-contrast-contract.mjs) in both modes.
The progress track is decorative (secondary-container on surface is 1.23 light, 1.76 dark). That's why a
determinate bar keeps its stop dot and an indeterminate one carries no position: under reduced motion it shows a
static stripe in --mat-sys-primary at 32% (Highlight in forced colours), never a frozen fill.
Status chip kinds (shared component — StatusView contract)¶
Shipped roles (2026-09-21): the kinds below map onto emitted theme tokens, measured in both modes by
scripts/token-contrast-contract.mjs— success:--syrf-successon--syrf-success-container; info:--syrf-info-accenton--syrf-info-container; warning:--syrf-warning-accenton--syrf-warning-container; error:--mat-sys-on-error-containeron--mat-sys-error-container; neutral:--syrf-status-neutral-texton--syrf-status-neutral-container. Body text inside a tint uses the matching--syrf-on-*-container. Consumers pass a kind, never a colour.
Chip colours live inside the DS Chip component; consumers pass kind only, never colour.
| StatusView kind | DS Chip kind | Semantic |
|---|---|---|
| ok | success | Terminal success (Complete, PDFs uploaded, Completed parsing) |
| info | info | Active/running states |
| warn | warning | Needs attention, recoverable (Error, Timed Out) |
| stopped | error | Hard stop (Infected/Upload stopped) |
| muted | neutral | Cancelled, Abandoned, Expired, Unknown status |
Literals that stay literal (documented exceptions)¶
| Value | Use | Note |
|---|---|---|
rgba(0,0,0,0.12) |
Card/hairline borders | DS-documented divider value |
#eef0f4 |
Row dividers inside tables | Map to --mat-sys-surface-container in Wave 3 if a token is preferred |
#ffffff |
Table/card interiors on tinted page | --mat-sys-surface acceptable substitute |
Prototype-only (never ship)¶
#26282e chrome bar, #e9eaef/#dfe1e7 board backgrounds, frame border rgba(0,0,0,0.16),
frame shadow — these belong to the review harness, not the product.
Type and spacing¶
Roboto only; Roboto Mono for job IDs, raw enum values and error lines. Sizes used: h1 28/24 (narrow),
group heading 12 uppercase, row body 13, meta 12–12.5, column header 11.5. All spacing on the 4px grid
(--syrf-sp-*): card padding 24, row padding 9×14, group gap 14. Radii: 4 buttons/selects, 6 op strips,
8 cards/groups (--syrf-radius-md), 999 chrome pills (prototype only). --syrf-sp-* above is the
prototype-only naming from the Iteration 2 handoff; the shipped spacing scale is the --syrf-space-*
ladder below.
Spacing¶
Design review (2026-09-08/09), slice C1: the shared page shell introduces a 4px spacing ladder as CSS
custom properties, replacing the three unrelated ad-hoc rhythms Library/Searches/Processing each
invented on their own (15/17.5/20px, 20/25px + inline styles, and a hard-coded 4/8/12/16/24/48px
sequence respectively). Twelve steps, each --syrf-space-N = N × 4px:
| Token | Value | Token | Value |
|---|---|---|---|
--syrf-space-1 |
4px | --syrf-space-7 |
28px |
--syrf-space-2 |
8px | --syrf-space-8 |
32px |
--syrf-space-3 |
12px | --syrf-space-9 |
36px |
--syrf-space-4 |
16px | --syrf-space-10 |
40px |
--syrf-space-5 |
20px | --syrf-space-11 |
44px |
--syrf-space-6 |
24px | --syrf-space-12 |
48px |
Defined once, in global-styles/, alongside the --mat-sys-* token layer; feature SCSS consumes
these rather than literal px margins/paddings/gaps.
Breakpoints¶
SCSS media-query breakpoints used anywhere in Study Management (or the shared page shell) must mirror
the values in src/services/web/src/app/shared/layout/break-points.ts exactly — that file, not any
page's SCSS comment, is the source of truth. As of the 2026-09-08/09 design review the two values that
matter for this feature are:
lt_lg(large-down / shell compact rail threshold) —max-width: 1239.98px(layout.service.tsdrives the sidenav rail/compact switch from this same alias). Library's table previously switched to cards at1279.98px, a stale value 40px off this source of truth; the design review's action plan corrects it to match.lt_sm(small / table-to-card threshold) —max-width: 599.98px. This is the dominant small breakpoint already used by five of the feature's six page files; the one outlier (599px, no.98) is corrected as part of the same slice.
No SCSS partial exported these values before the design review; slice C1 adds SCSS custom-property or
Sass-map breakpoint constants generated from (or kept in lockstep with) break-points.ts, so a page's
media query and the shell's layout-driven behaviour cannot drift apart again.