Design Tokens

Design tokens are Lolly's single source of truth for brand primitives - colours first, then dimensions, type and the rest. One canonical token document feeds three things at once: the values baked into tools, the swatches the colour picker offers and an import/export bridge to other design tools. The format is the W3C Design Tokens (DTCG) standard - the same format Penpot imports and exports - so a brand defined in Lolly round-trips with Penpot and Tokens Studio. (Tokens carry the brand variables both ways; finished Figma/Penpot files come in through the Design tool's Import a design.)

This page is the spec. The engine model is engine/src/tokens.ts; the format contract is pinned by tests/tokens.test.ts.

Status. The colour slice is shipped: brand colours are canonical tokens, the picker's swatches come from them and a chosen brand colour stays linked to its token. Dimension tokens (radius, spacing, sizing, stroke, opacity, rotation, shadows) and typography (brand fonts) are shipped too, editable in the Brand Studio's Tokens and Type tabs. User import/export is shipped - import W3C DTCG / Tokens Studio / Penpot in the Studio (or via ingest:brand), and export a LollyBrand pack or a design-tokens palette. Token-aware tool injection into templates remains on the roadmap.

The Tokens panel - pick a kind, name it and the token joins the document your brand travels insigned by Lollyvector SVGПровери самGet the signed file8 paths~6.1k nodes13 groups1 image69 KBThe Tokens panel - pick a kind, name it and the token joins the document your brand travels insigned by Lollyvector SVGПровери самGet the signed file8 paths~6.1k nodes13 groups1 image69 KB

Why tokens

Before tokens there were three disagreeing colour "truths": a hard-coded swatch array in the web shell, a palette catalog asset that tools could reference and the engine's colour-management maths. Tokens collapse the first two into one canonical, versioned, git-reviewed source - and make it portable. The goal the project states plainly: keep as many things as possible canonical via tokens, and be able to import/export them with tools like Penpot.

The format (a DTCG profile)

A token document is DTCG JSON. A token is an object with $value (required) and $type; groups are objects without $value and pass their $type down to descendants. References between tokens use the curly-brace alias {dotted.path}.

{
  "color": {
    "$type": "color",
    "brand": {
      "jungle": {
        "$value": "#30ba78",
        "$description": "Jungle",
        "$extensions": { "com.suse.lolly": { "cmyk": [70, 0, 65, 0] } }
      }
    },
    "semantic": {
      "primary": { "$value": "{color.brand.jungle}" }
    }
  }
}

Two deliberate choices keep us interoperable with Penpot while serving Lolly's print needs:

Sets and themes. Top-level keys can be token sets; a $themes array selects and orders sets per theme (later sets override earlier - Tokens-Studio layering), with $metadata.tokenSetOrder fixing the order. A document with no $themes is one implicit set, and token paths keep their color.brand.x shape.

The four layers

1. Engine - engine/src/tokens.ts. Platform-agnostic, like units.ts. createTokenSet(doc, { theme }) parses a document, inherits group $type, resolves {} aliases (chains, cycle-safe), applies the theme and returns a flat lookup with get / resolve / query({type}) / colors() / themes(). It knows DTCG and nothing else - no DOM, no storage, no SUSE - so it ships as part of the open-source engine.

2. Catalog - a tokens asset. Each brand pack ships one core-tier tokens asset and the active profile decides which is canonical - suse/tokens/brand in the SUSE pack, lolly/tokens/brand in the blank brand, <brand>/tokens/brand for a pack built by ingest:brand (a tokens-type catalog asset, core tier so it's always available offline). It is generated from the shell palette by scripts/build-brand-tokens.ts and validated against schemas/tokens.schema.json by npm run validate:catalog. Versioned and checksummed like any asset.

*3. User tokens - shipped.* A device-local store for the tokens a user creates or imports (from Penpot, DTCG or a LollyBrand pack), travelling as the tokens.json part of a LollyBrand pack, and inside the full data backup as a user asset in assets.json (see Data Transfer). The Brand Studio handles import, editing, the active theme and export back to DTCG.

4. Bridge - host.tokens. An additive, optional v1 capability (like net/text): get / colors / resolve / themes. Each shell implements it over the engine model and its sources; a shell that doesn't is simply not token-driven. Loading is offline-safe (prefers the core-prefetched blob, falls back to a direct fetch, then to the built-in palette).

Lolly UI tokens

Your brand describes identity. Lolly also carries a small application UI system so menus, fields, panels and editor chrome remain coherent even when a brand has no complete component library. It travels under a reserved namespace:

lolly.foundation.*  # Lolly’s compatible base scale
lolly.ui.*          # stable application roles a design system may override
brand.*             # your identity tokens

The Tokens room shows the Lolly roles as placeholders. They affect the app and the Penpot UI-library export, but they do not bloat a brand document merely because it was opened. Changing one value saves only that owned lolly.ui.* leaf; Reset removes it and restores Lolly’s stock role.

The supported UI contract is deliberately narrow: semantic text/surface/action, selection, border and focus colours; control/panel/page spacing; role-based radius; elevation/edge/focus effects; UI fonts/type sizes; feedback/overlay/ navigation motion; and chrome/overlay/toast layers. Lolly validates each value before it reaches CSS. Unknown paths, unsafe CSS, incompatible types and unresolved aliases stay out of the live app rather than partially applying.

Two foundation values are especially useful when starting with a token library:

Choose Export Lolly UI to Penpot in Components to get the stock library as editable frames plus both token layers and the active brand. The archive names every declared app component/pattern and uses sample data by default. Token records are present in tokens.json; a property that Penpot cannot bind natively remains an editable resolved value rather than a misleading claim of a live binding. Lolly does not export personal sessions or current content as an app surface unless that surface explicitly records consent.

How tools consume tokens

Platform hydration (the common case). Most tools never mention tokens. The platform feeds them:

*Direct injection - (planned).* A token-aware tool (a brand sheet, a style guide, a chart that maps series to brand colours) can receive the resolved token tree in its template context via the existing extras mechanism, opted into from the manifest.

The value model: reference + cached value

When a user picks a token-backed colour, the stored value is a reference plus the hex it last resolved to:

{ "ref": "{color.brand.jungle}", "value": "#30ba78" }

A token-backed swatch reads as its token in the sidebar: the trigger carries the swatch's name, not a frozen hex, which is the visible difference between a linked value and one typed in by hand.

A tool's colour row holding a token-backed value, the trigger showing the swatch circle and its name instead of a hex codesigned by Lollyvector SVGПровери самGet the signed file1 path145 nodes6 groups5 KBA tool's colour row holding a token-backed value, the trigger showing the swatch circle and its name instead of a hex codesigned by Lollyvector SVGПровери самGet the signed file1 path145 nodes6 groups5 KB

The ref keeps the value canonical - editing the token, or switching themes, re-resolves it everywhere. The cached value is the graceful fallback for a device where that token is absent. The path is fully backward-compatible: a plain colour string (a custom pick, or any existing tool) flows through untouched.

How it moves through the engine:

Nothing in the link below names a colour. Its three colour params are token paths ({color.spectrum.violet}, {color.spectrum.teal}, {color.spectrum.amber}), resolved at render time against whatever brand the device is carrying.

A mesh gradient whose violet, teal and amber all arrived as token references in the link rather than as hex valuessigned by Lollyvector SVGПровери самGet the signed file16 paths299 nodes45 groups17 KBA mesh gradient whose violet, teal and amber all arrived as token references in the link rather than as hex valuessigned by Lollyvector SVGПровери самGet the signed file16 paths286 nodes45 groups17 KB

Penpot interop

The download pill parked at the palette's bottom edge, with the format menu that carries the same colours back out as DTCG JSONsigned by Lollyvector SVGПровери самGet the signed file3 paths644 nodes7 groups1 image12 KBThe download pill parked at the palette's bottom edge, with the format menu that carries the same colours back out as DTCG JSONsigned by Lollyvector SVGПровери самGet the signed file3 paths644 nodes7 groups1 image12 KB

Take your tokens to Penpot

The round trip above, step by step - no code involved.

Export from Lolly. Open the Brand Studio and go to the Colours room. The download pill parked at the bottom of the palette pane offers several formats; choose Design tokens (JSON). The file you get is a W3C design-tokens document - the format Penpot and Tokens Studio read. If you have chosen faces in the Type room (Primary, Headings, Code, Italic), those travel in the same file as font tokens. Roles you left on the platform default stay out of the file: the export only carries choices you actually made.

Import into Penpot. Open a Penpot file and switch to the Tokens panel in the left sidebar. Import the JSON you just downloaded. Your sets appear in the panel, and the colours and fonts can be applied to shapes and text from there.

Two more paths exist beyond the token file. Once Penpot is connected under Profile → Connected services, a finished render can be sent from Lolly into a Penpot project as an image. And the Lolly bridge plugin for Penpot - installed by URL from a Lolly instance - can place a working Lolly session on the Penpot canvas as editable shapes.

Migration & status

The brand colours moved into tokens without changing what anyone sees: scripts/build-brand-tokens.ts derives the token document from the existing palette, so the picker shows the same colours - now sourced canonically. The shell palette remains the fallback (and, for now, the source of CMYK anchors on export). With dimension/typography tokens and user import/export now shipped, most of the app resolves from tokens and the standalone palette recedes; template-level token injection is the main piece still to land.

The corner radius is the plainest of the shipped dimension tokens: one slider writing one shape.radius value that the app chrome, the panels and every opted-in tool then follow.

The Rounded corners control in the Tokens tab - a live preview square, a slider and the value it writes, all standing for a single dimension tokensigned by Lollyvector SVGПровери самGet the signed file4 paths~2.3k nodes11 groups29 KBThe Rounded corners control in the Tokens tab - a live preview square, a slider and the value it writes, all standing for a single dimension tokensigned by Lollyvector SVGПровери самGet the signed file4 paths~2.3k nodes11 groups29 KB

Reference