Skip to main content

CSS Token Architecture

This document describes how NL Design System tokens are mapped to Nextcloud styling in the thematiq app.

Three-Layer Architecture​

NL Design System repos thematiq app Nextcloud
(upstream source of truth) (translation layer) (target)

ams.color.interactive.default --nldesign-color-primary --color-primary
utrecht.color.blue.35 --nldesign-color-link --color-primary-text
rhc.color.lintblauw.500 --nldesign-color-header-* #header { background }
... ... ...
  1. NL Design System repos define tokens using their own namespaces (ams.*, utrecht.*, rhc.*)
  2. thematiq token files (css/tokens/*.css) translate those into a unified --nldesign-* namespace
  3. theme.css maps --nldesign-* tokens to Nextcloud's CSS variables and element styling

Token Reference​

Core Tokens (required for every token set)​

TokenPurposeExample
--nldesign-color-primaryInteractive/accent color (buttons, links, accents)#004699
--nldesign-color-primary-textText on primary color#ffffff
--nldesign-color-primary-hoverPrimary hover state#003677
--nldesign-color-primary-lightLight tint of primary#e8f0f8
--nldesign-color-primary-light-hoverHover on light primary#d4e4f2

Action colour (optional)​

A token set may give the one main button of a page a colour of its own, apart from its primary. Declare these in the set's file:

TokenPurposeWithout it
--nldesign-color-actionFill of the main buttonThe primary
--nldesign-color-action-hoverThe same on hoverThe action colour, 15% darker. Without an action colour: the primary hover
--nldesign-color-action-textText and icons on the main buttonThe text on the primary

No default is declared for them, on purpose. A set that leaves them out renders as it always did. Of the shipped sets only Cunningham declares them: a green main button (#1f883d) on its blue brand, as OpenProject draws it.

The action colour reaches the filled main button and nothing else: the primary button of a page, the confirm button of a dialog and the login button. Checked boxes, radio buttons, switches, progress bars, the selected navigation entry, focus rings, counter badges, folder icons and links stay with the primary. An administrator moves the main button on one instance with its own tokens in the token editor (primary button background, label and hover).

Check the text on your action colour: it needs 4.5:1. Thematiq warns when it does not reach that, as it does for the text on the primary. In a dark variant the action colour is treated as the primary is: it keeps its value when its text already reads on it, and its text is adjusted until it does.

The NL Design System and La Suite (Cunningham) bundles read these tokens. Summer Breeze and High contrast do not.

An imported theme brings them too. In a theme's CSS, name them --<prefix>-color-action, --<prefix>-color-action-hover and --<prefix>-color-action-text. In a design tokens (DTCG) file, use the paths color.action, color.action-hover and color.action-text. A theme that names only the action colour gets a readable text on it: white or black, whichever reads better.

Header Tokens​

TokenPurposeExample
--nldesign-color-header-backgroundNextcloud header bar background#154273
--nldesign-color-header-textText/icon color in header#ffffff
--nldesign-header-icon-filterCSS filter for header SVG iconsbrightness(0) invert(1)

The default icon filter is brightness(0) invert(1) (white icons). Override to none for light-colored headers where dark icons are needed (e.g., Amsterdam's white header).

Logo and Lint/Ribbon Tokens​

The lint (ribbon) system follows the Rijkshuisstijl concept of rhc.logo.image.* tokens. A colored bar appears behind the logo only when these tokens are defined.

TokenPurposeDefault (fallback)Example (Rijkshuisstijl)
--nldesign-logo-urlLogo image pathnone (no logo)url('../../img/logos/rijkshuisstijl.svg')
--nldesign-color-logo-backgroundLint/ribbon background colortransparent (no ribbon)#154273
--nldesign-color-logo-textText color on ribbon(unused)#ffffff
--nldesign-size-lintRibbon width in header0px (no ribbon)48px
--nldesign-size-lint-heightRibbon/bar height on login page0px (no bar)96px
--nldesign-logo-filterCSS filter applied to logo imagenone (natural colors)brightness(0) invert(1)

Conditional behavior:

  • Lint tokens defined (currently only Rijkshuisstijl): Colored ribbon behind logo, logo displayed in white (inverted), login page shows colored bar
  • Only --nldesign-logo-url defined: Logo displayed in natural/original colors, no ribbon, login page shows logo on white background
  • Neither defined: No logo shown, Nextcloud's default logo behavior

Text Tokens​

TokenPurposeExample
--nldesign-color-textDefault body text#202020
--nldesign-color-text-mutedSecondary/muted text#767676
--nldesign-color-text-lightText on dark backgrounds#ffffff

Status/Feedback Tokens​

TokenPurpose
--nldesign-color-errorError states
--nldesign-color-error-rgbRGB values for rgba() usage
--nldesign-color-error-hoverError hover state
--nldesign-color-warningWarning states
--nldesign-color-warning-rgbRGB values
--nldesign-color-successSuccess states
--nldesign-color-success-rgbRGB values
--nldesign-color-infoInfo states
--nldesign-color-info-rgbRGB values
TokenPurpose
--nldesign-color-linkLink text color
--nldesign-color-link-hoverLink hover color
--nldesign-color-link-visitedVisited link color

Button Tokens​

TokenPurpose
--nldesign-color-button-primary-backgroundPrimary button background
--nldesign-color-button-primary-textPrimary button text
--nldesign-color-button-primary-borderPrimary button border
--nldesign-color-button-primary-hoverPrimary button hover

Other Tokens​

TokenPurpose
--nldesign-color-background-hoverHover background for elements
--nldesign-color-background-darkDark variant background
--nldesign-color-background-darkerDarker variant background
--nldesign-color-borderStandard border color
--nldesign-color-border-darkDark border color
--nldesign-color-focusFocus ring color (rgba)
--nldesign-color-focus-rgbFocus color RGB values
--nldesign-color-nav-backgroundNavigation panel background
--nldesign-font-familyFont stack
--nldesign-border-radiusBase border radius
--nldesign-border-radius-smallSmall border radius
--nldesign-border-radius-largeLarge border radius
--nldesign-border-radius-roundedRounded border radius
--nldesign-border-radius-pillPill/full border radius

Brand Color vs Interactive Color​

Many Dutch municipalities have different colors for their brand identity and their interactive elements:

MunicipalityBrand/IdentityInteractive/PrimaryNote
AmsterdamRed (#ec0000)Blue (#004699)Red is only for the logo emblem and error feedback
UtrechtRed (#cc0000)Blue (#24578F)Red is the header/brand banner, blue is for buttons/links
Den HaagGreen (#1a7a3e)Green (#1a7a3e)Same color for both
RotterdamGreen (#00811f)Green (#00811f)Same color for both
RijkshuisstijlLintblauw (#154273)Lintblauw (#154273)Donkerblauw (#01689b) used for links

--nldesign-color-primary always maps to the interactive color, not the brand color. This is because Nextcloud's --color-primary drives buttons, links, and accent styling throughout the interface.

The brand/identity color may appear in:

  • --nldesign-color-header-background (e.g., Utrecht's red header)
  • --nldesign-color-logo-background (lint/ribbon behind logo)
  • Municipality-specific palette variables (e.g., --amsterdam-color-red)

Official Sources​

Each token file maps values from the upstream NL Design System repositories:

Token fileSource repositoryToken prefix
rijkshuisstijl.cssnl-design-system/rijkshuisstijl-communityrhc.*
amsterdam.cssAmsterdam/design-systemams.*
utrecht.cssnl-design-system/utrechtutrecht.*
denhaag.cssnl-design-system/denhaagdenhaag.*
rotterdam.cssnl-design-system/rotterdamrods.*
Othersnl-design-system/themesVarious

Adding a New Token Set​

  1. Create the CSS file at css/tokens/{id}.css with all required --nldesign-* variables
  2. Add metadata to token-sets.json:
    {
    "id": "myorg",
    "name": "My Organization",
    "description": "Design tokens for My Organization"
    }
  3. Optional: Add a logo at img/logos/{id}.svg and set --nldesign-logo-url in the CSS file
  4. Optional: Add theming metadata to token-sets.json for Nextcloud theming sync:
    {
    "id": "myorg",
    "name": "My Organization",
    "description": "...",
    "theming": {
    "primary_color": "#hexcolor",
    "background_color": "#FFFFFF",
    "logo": "img/logos/myorg.svg"
    }
    }
  5. Optional: Add lint tokens if the organization uses a ribbon/banner behind their logo (following the Rijkshuisstijl pattern)

The admin dropdown picks up new token sets automatically from the filesystem. No PHP changes are needed.

Mapping Guidelines​

When translating tokens from an upstream NL Design System repo:

  • --nldesign-color-primary = the interactive color (buttons, accents), NOT the brand/logo color
  • --nldesign-color-header-background = the background of the Nextcloud top navigation bar
  • --nldesign-color-link = may differ from primary (e.g., Rijkshuisstijl uses donkerblauw for links)
  • --nldesign-color-error = map from *.color.feedback.error or equivalent
  • Always check the municipality's actual website to verify how colors are used in practice
  • Reference the upstream repo's component tokens to understand which palette color maps to which purpose