Skip to main content

Custom Token Sets (eigen huisstijl)

Beyond the 40+ bundled government token sets, an administrator can upload their own house style as a token set. This closes government-feature F-04 ("Aangepaste token sets uploaden") and is the supported path for organizations that are not bundled, or whose huisstijl has been refreshed, to bring their own brand without forking the app or hand-editing files on the server.

Uploaded sets land alongside the bundled sets in the token-set dropdown, participate in the live preview, the apply dialog, and the Nextcloud theming sync — exactly like a shipped set.

Where

Admin settings → NL Design System Theme (/settings/admin/theming) → Custom token sets section. The feature is admin-only (delegated theming admins included); every endpoint is CSRF-protected.

Supported formats

You can upload a single file in either of two formats.

1. NL Design CSS

A CSS file containing a single :root { … } block of --nldesign-* custom properties — the same format as the bundled css/tokens/*.css files:

:root {
--nldesign-color-primary: #007bc7;
--nldesign-color-primary-text: #ffffff;
--nldesign-color-background: #ffffff;
}

Organization-palette extras using your own slug prefix (--{slug}-*, e.g. --gemeente-voorbeeld-color-accent) are also accepted, mirroring how bundled sets carry e.g. --utrecht-color-*.

2. W3C Design Tokens JSON

A file in the Design Tokens Community Group draft format ($value / $type, nested groups). Recognized tokens are mapped onto the --nldesign-* vocabulary via the table below; unmapped tokens are skipped and reported (imported / skipped counts).

{
"color": {
"primary": { "$type": "color", "$value": "#154273" },
"on-primary": { "$type": "color", "$value": "#ffffff" }
}
}

Mapping table (DTCG path suffix → --nldesign-*)

Token path (suffix match)--nldesign-* target
color.primary / brand.primary--nldesign-color-primary
color.primary-text / color.on-primary--nldesign-color-primary-text
color.primary-hover--nldesign-color-primary-hover
color.primary-light--nldesign-color-primary-light
color.background--nldesign-color-background
color.text--nldesign-color-text
fontFamily.base / typography.font-family--nldesign-font-family
border-radius.base / dimension.border-radius--nldesign-border-radius

The longest matching suffix wins, so color.primary-text is preferred over color.primary. The mapping is deliberately tolerant: format drift degrades to a higher skipped count, never an error.

Validation rules

The uploaded file is served as CSS to every user on every page (including the login page), so it is validated strictly and the served file is always re-serialized from the parsed declarations — never from the raw upload bytes. An upload is rejected when:

  • it is larger than 512 KB (HTTP 413);
  • it contains any selector other than a single :root block, or any at-rule (@import, @font-face, @media, …) (HTTP 422);
  • a value contains @import, expression(, javascript:, raw markup (<), or a url() with a scheme or host (HTTP 422). Relative url('../../img/…') and data:image/… URIs are permitted, matching bundled logo usage;
  • the display name slugifies to an empty string (HTTP 422);
  • a set with the same name already exists (HTTP 409 — delete or rename first).

Properties that are not part of the supported vocabulary (e.g. Nextcloud --color-* variables) are skipped and listed in the response so you can move them to custom-overrides.css instead.

WCAG 2.1 AA contrast warnings

On upload, the server computes WCAG 2.1 relative-luminance contrast ratios for two fixed token pairs:

  • --nldesign-color-primary vs --nldesign-color-primary-text — threshold 4.5:1;
  • --nldesign-color-primary vs --nldesign-color-background — threshold 3:1.

Failures are returned as non-blocking warnings (you can still upload and apply an in-progress huisstijl), persisted with the set, and resurfaced in the apply dialog above the change list. Pairs whose values are not literal colours (e.g. var(...)) are reported as unevaluated — never as passing.

Manage uploaded sets

Each uploaded set in the Custom token sets list offers:

  • a WCAG AA OK / Contrast warning status badge;
  • Download — exports the exact CSS that is served (text/css attachment);
  • Delete — removes the file and its metadata. Deleting the active set resets the active token set to nextcloud in the same operation.

⚠️ Upgrade caveat

Uploaded sets are stored as files in the app directory (css/tokens/custom-{slug}.css), the same accepted trade-off as custom-overrides.css. An app-store upgrade that replaces the app directory removes uploaded sets. Export your custom sets (Download) before upgrading and re-upload them afterwards. Set metadata lives in the nldesign appconfig key custom_token_sets and survives the upgrade, but a manifest entry without a backing file is ignored by discovery.