57 lines
3.2 KiB
Markdown
57 lines
3.2 KiB
Markdown
# WebUI Theme Contract
|
|
|
|
GovOPlaN supports `system`, `light`, and `dark` as persisted user preferences.
|
|
`system` follows `prefers-color-scheme` live; it is not resolved permanently at
|
|
save time. Core applies the resolved mode through `data-theme` on the document
|
|
root and exposes the selected preference through `data-theme-preference`.
|
|
Each user may also choose a validated `default`, `civic_blue`, `forest`, or
|
|
`plum` accent palette. Core applies it through `data-palette`; every module
|
|
inherits the result through semantic tokens without module-specific CSS.
|
|
|
|
## Ownership
|
|
|
|
- Core owns semantic CSS tokens, native `color-scheme`, preference persistence,
|
|
the Settings selector, and the shared shell.
|
|
- Modules consume semantic tokens such as `--surface`, `--text`, `--line`, and
|
|
the status token families. They may define domain aliases whose values resolve
|
|
to shared tokens.
|
|
- Palette defaults form a provenance chain: system, tenant, then an explicit
|
|
user choice. Invalid stored values are ignored. Reset means inheritance and
|
|
does not copy the current parent value into the child scope.
|
|
- A policy lock is separate from the default. A system lock wins over every
|
|
child scope; otherwise a tenant lock suppresses a personal override. The
|
|
authenticated profile reports the effective palette, source, inherited
|
|
palette, and lock state.
|
|
- Advanced personal overrides are a separately governed surface. The system
|
|
must opt in, a tenant may inherit or block that decision, and palette locks
|
|
always suppress overrides. Changing either policy requires
|
|
`admin:policies:write` in addition to the owning settings permission.
|
|
|
|
## Palette safety and scope
|
|
|
|
The Settings preview shows the chosen or inherited accent in every applicable
|
|
light/dark preview before Save. Presets are checked for WCAG AA contrast in the
|
|
theme contract. When policy permits, the shared advanced editor can atomically
|
|
override accent, surface, and semantic status pairs for both modes. Every
|
|
foreground/background pair must meet WCAG AA contrast, and success,
|
|
information, warning, and danger colors must remain distinct. Invalid stored
|
|
documents fail closed and are not partially applied.
|
|
|
|
Import and export use the exact versioned JSON schema `schema_version: "1"`.
|
|
Both `light` and `dark` must contain every supported token exactly once as a
|
|
six-digit hex value. Import changes only the local draft; Save persists the
|
|
whole document. Removing overrides returns to palette and policy inheritance.
|
|
The system default is disabled so upgrades do not unexpectedly admit arbitrary
|
|
branding. Tenant `null` means inherit, `false` blocks, and `true` is accepted
|
|
only while the system permits overrides.
|
|
|
|
Do not introduce fixed foreground/background colors in a module merely to make
|
|
one mode look correct. Add or reuse a semantic Core token, then define both
|
|
light and dark values. Bitmap content and externally authored HTML are exempt,
|
|
but their surrounding controls must still use the shared tokens.
|
|
|
|
`npm run test:theme-contract` verifies root mode/palette behavior, preset and
|
|
custom-override validation/application, and representative
|
|
Campaign, Calendar, Files, and Mail token consumption. The check runs before a
|
|
production WebUI build.
|