Skip to content

Themes

A theme is a CSS file that masks loads after its own stylesheet on the sign-in, consent, and account pages. In production, masks reads themes when the server boots, so a new or changed file applies after a restart. In development, it reads them on every request.

masks reads themes from /rails/themes, or from the directory MASKS_THEMES_PATH names. A file’s name sets where it applies.

File Applies to
acme.css Every sign-in and account page of the tenant acme.
acme/<client_id>.css The sign-in and consent pages of acme while the person signs in to that client.

A page loads the tenant’s file first and the client’s file second, so the client’s rules take precedence. The account page names no client, so it loads only the tenant’s file. A file larger than 256 KB is skipped, and the server logs a warning.

  1. Write the stylesheet. Set the custom properties below on .auth-page, once for light and once inside @media (prefers-color-scheme: dark):

    .auth-page {
    --ground: #f4f7fb;
    --surface: #eaf0f7;
    --line: #d3dce7;
    --ink: #14202e;
    --signal: #1f5fbf;
    --signal-ink: #ffffff;
    }
    @media (prefers-color-scheme: dark) {
    .auth-page {
    --ground: #151c26;
    --surface: #1d2632;
    --line: #2e3a49;
    --ink: #e6edf5;
    --signal: #7fb0ff;
    --signal-ink: #0d1520;
    }
    }

    A property left unset keeps masks’ default.

  2. Mount the directory into the container:

    services:
    masks:
    volumes:
    - masks-storage:/rails/storage
    - ./themes:/rails/themes:ro
  3. Restart the server. masks serves the file from /themes/<digest>.css. The digest changes with the file, so browsers can cache each version indefinitely.

Property
--ground The page background.
--surface Panels, cards, and fields that sit on the ground.
--line Borders and dividers.
--line-firm Borders of fields and of hovered or focused controls.
--ink Body text.
--ink-soft Secondary text, hints, and labels.
--came Eyebrows, status labels, and the name beside an avatar.
--brass Structural rules and the edge of the tenant’s mark.
--brass-lit Focus rings and the border of a focused field.
--signal The primary button, links, and selected controls.
--signal-ink Text on a solid --signal fill.
--bad, --bad-field Error text and the background behind it.
--warn, --warn-field Warning text and the background behind it.
--info, --info-field Informational text and the background behind it.
--glow The background of pills and plain notes.
--window The image cast as light across the top of the page, url("/masks-public/icon.svg") by default. none removes it.
--window-strength The opacity of that light, from 0 to 1.
--pane The translucent fill of panels, which blur what is behind them.
--pane-rim The border of panels.
--pane-shine, --pane-sheen The highlight along a panel’s top edge and the gradient across its face.
--pane-shadow The shadow under panels.
--bend The corner radius of panels and controls.

A theme may also style any other selector on these pages. The properties above stay the same between releases. Class names can change.

A theme does not change the pages’ content security policy. Images and fonts load only from masks itself or from data: URIs, so a theme embeds each image or font as a data: URI or names a font installed on the device. The browser refuses an @import or url() that points at another host.