mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-10 12:47:04 +00:00
The inline design script carries a CSP sha256 computed at render time. On an onion request body_filter.lua rewrites the clearnet CDN base inside the whole head payload, the script's bytes stop matching their hash, and every role whose script held a CDN URL has its branding refused wherever script-src-elem has no 'unsafe-inline'. web-svc-logout and web-svc-html are on nearly every deployment, so this was not a corner case. The config now rides in the script tag's data-design attribute, which CSP does not hash and onionize may rewrite freely, and the first statement of the script reads it back into INFINITO_DESIGN. The executable body is identical on both vhosts, so one hash authorizes both. All 35 design scripts now read the config instead of interpolating design.urls. The 13 that still needed converting keep their render-time guards, so a role with branding disabled still emits no script at all, exactly as before. slots left the config object: no script has ever read it. A new check refuses a branding URL in a script body, so the class cannot return, and a unit test drives the config through to_json, Jinja's escape and an HTML parser with a hostile title to prove the attribute round trip. Not yet proven: that the hash matches on a real onion vhost. This stack runs without tor and every role deployed here has 'unsafe-inline', so the check needs a tor-enabled deploy of one of the seven roles that disable logout. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
7.1 KiB
7.1 KiB
style.css 🎨
This page covers role-local CSS and theming.
Use this page for repository wiring, inventory keys, and the design token contract.
For implementation scope, override strategy, and live review, see Agent style.css.
For browser-side validation requirements after visible UI changes, see Playwright Tests.
style.css.j2 ⚙️
- A role ships its override as
files/style.csswhen it needs no Jinja, otherwise astemplates/style.css.j2. - Rules that apply only while the role ships a logo go into
files/logo.css.sys-front-inj-designappends that file only whendesign.logoresolves to a path, so the guard lives in one place instead of in every sheet. sys-front-inj-designrenderstemplates/css/app.css.j2ahead of both: a:rootblock declaring--design-url-<slot>-<png|svg>,--design-url-favicon-ico,--design-slot-<slot>-<width|height>and--design-url-app-<key>for every entry of the role'sdesign_css_urls. A sheet reads the branding through those properties instead of interpolating a URL.- Those properties reach the composed sheet only, meaning
templates/style.css.j2orfiles/style.css, plusfiles/logo.css. A sheet the role deploys on its own path never sees them, so reading one there leaves the declaration invalid and the rule silently does nothing. sys-front-inj-designrenders it whenservices.design.enabledis enabled.- The shared element styles (
body, links, buttons, form fields, tables, focus outline) sit in the cascade layerinfinito-design.layer.cssis linked first in<head>and declares that layer before the app's own stylesheets, so every rule of the app wins over them, layered or not. They only style what the app leaves unstyled. - The app's own configuration outranks the role stylesheet. A theming configuration carries the palette first (Nextcloud takes the primary color, logo, name and slogan via
occ theming:configand ships no stylesheet), then an in-house theme or custom CSS option. - An in-house theme carries a variable mapping natively (see web-app-gitea). The role stylesheet maps the app's CSS variables onto the tokens only for what no in-house option reaches.
- The Bootstrap component mapping in
bootstrap.cssis unlayered. A role built on Bootstrap links it by settingbootstrap: truein itsdesign:entry. Set inweb-svc-design, the value applies to every role. - A role stylesheet or design script that renders empty counts as absent: nothing is deployed, linked or injected for it.
- A role MAY ship
templates/design.js, ortemplates/design.js.j2when it needs Jinja.sys-front-inj-designcarries the per-app config{logo, title, urls}in the script tag'sdata-designattribute and prepends a line that reads it intoINFINITO_DESIGN, collapses the result, adds its CSP hash and injects it wheneverservices.design.enabledis enabled, independent ofservices.javascript.enabled. The config rides in the attribute because CSP hashes only the script body: an onion vhost rewrites the CDN host in the payload, and a body holding that host would no longer match its hash. For the same reason a design script MUST read branding throughINFINITO_DESIGNand MUST NOT interpolatedesign.urlsitself.
Inventory 📋
- You MUST set
applications.web-svc-design.services.design.colors.basein the inventory as the single base color. - You MUST keep
services.design.enabledenabled in the role configuration or override it in the inventory when CSS injection should run. - You MAY set
applications.web-svc-design.services.design.font.typeandfont.import_urlto change the typography.
Design Tokens 🎨
The design_palette lookup derives every token from the base color in OKLCH and guarantees WCAG 2.2 contrast by construction.
| Token | Use |
|---|---|
--design-surface-1 |
Page background |
--design-surface-2 |
Cards, panels, inputs |
--design-surface-3 |
Hover, selected and inset areas, table heads |
--design-surface-hover, --design-surface-active |
Hovered and pressed or open neutral controls; body text stays AAA, muted text, links and status colors AA on both |
--design-text |
Body text, AAA against every surface |
--design-text-muted |
Secondary text, AA against every surface |
--design-border |
Decorative dividers |
--design-border-strong |
Input and control boundaries, 3:1 against --design-surface-1 and --design-surface-2 |
--design-primary / --design-on-primary |
Brand surfaces, 3:1 against --design-surface-1 and --design-surface-2, and the text on them |
--design-frame / --design-on-frame |
Large brand surfaces such as a sidebar, masthead or top bar: a deep brand tone in light and dark mode, and the text on it at AAA |
--design-frame-hover, --design-frame-active |
Hovered and selected entries inside the frame; the on-color stays AAA |
--design-focus |
Color of the default focus outline. Unset, the outline uses --design-link. A frame sets it to --design-on-frame on its own element; every focusable it hosts then draws its outline in that color |
--design-link |
Links and focus, AA against every surface and never as dark or as light as body text |
--design-success, --design-warning, --design-danger, --design-info |
Status text and icons, AA against every surface and against their own -subtle background |
--design-on-<status> |
Text on a filled status surface |
--design-primary-hover, --design-primary-active, --design-<status>-hover, --design-<status>-active |
Hovered and pressed filled controls, shifted away from their on-color so its contrast only grows |
--design-<status>-subtle, --design-<status>-border |
Alert backgrounds and borders |
--design-primary-50 to --design-primary-950 |
Brand scale |
--design-neutral-50 to --design-neutral-950 |
Tinted neutral scale |
--design-radius-sm, --design-radius, --design-radius-lg |
Corner radii |
--design-space-1 to --design-space-6 |
4/8 px spacing steps |
--design-shadow-1 to --design-shadow-3 |
Elevation |
--design-focus-ring |
Focus indicator for Bootstrap buttons and nav links |
--design-focus-ring-inset |
Focus indicator for Bootstrap form fields, drawn inside the field so it never overlaps neighbours |
--design-invert |
0 in light and 1 in dark mode. Use filter: brightness(0) invert(var(--design-invert)) to render a monochrome image in the text tone |
- Every color token also exists as
<token>-rgbforrgba()use. - Scale step
50sits next to the surface and950next to the text in light and dark mode, so a mapping onto the scale follows dark mode without a media query. - Dark mode follows
prefers-color-scheme. A role whose app has its own theme switch setsdata-design-theme="light"ordata-design-theme="dark"on<html>from its design script; the attribute wins over the browser preference. - Role CSS MUST use only
--design-*tokens for colors. Hard-coded colors break the base color override.