Design tokens for the Chassis Design System: multi-brand, multi-theme and multi-app tokens from Tokens Studio, built into SCSS for the web, Swift for iOS and resources for Android.
Chassis Tokens holds the design tokens of the Chassis Design System in Tokens Studio format, synced with Figma, and a Style Dictionary build that turns them into files for each platform. The output is committed and published to npm as @chassis-ui/tokens; Chassis CSS is built on it. The documentation site covers the tokens and how to use them on each platform.
Chassis Tokens is meant to be owned and customized: a team can clone it, change the tokens in Tokens Studio, and build its own brands, apps and platforms.
- Brands, themes, apps and screen sizes: each combination of token sets in Tokens Studio becomes its own set of files; the configured build writes two brands, light and dark themes and three screen sizes.
- Web: SCSS variables for Chassis CSS, and presets with plain SCSS variables in
rem,pxorvwfor other CSS frameworks. - iOS: Swift constants with
UIColorvalues that follow dark mode, an icon asset catalog, and SwiftUI output, installed as a Swift package. - Android: a resource tree with night and screen-size qualifiers, vector drawables for the icons, and Jetpack Compose output, installed as an Android library from the GitHub release.
- References: with
outputReferences, a token names the token it references (a SCSS variable, a Swift constant or an Android resource) wherever that compiles and keeps the value. - Checked output: a fresh build must equal the committed
dist/, checked in CI and before every release, and the tests run on real tokens.
npm install @chassis-ui/tokensThe package holds the files of packages/tokens/dist/: dist/web/docs/<brand>/ (SCSS), dist/ios/demo/<brand>/ (Swift and Icons.xcassets) and dist/android/demo/<brand>/ (the res/ tree), for the brands chassis and sinefil.
iOS apps install the same Swift files with Swift Package Manager, from https://github.com/chassis-ui/tokens.git: the libraries ChassisTokensDemoChassis and ChassisTokensDemoSinefil. Android apps download the resource tree as an Android library from the GitHub release: chassis-tokens-demo-<brand>-<version>.aar.
Web, with Sass modules:
@use '@chassis-ui/tokens/dist/web/docs/chassis/main' as tokens;
.card {
padding: tokens.$cx-space-context-medium;
}iOS: add the library of your brand to your target and import ChassisTokensDemoChassis, or add the Swift files and Icons.xcassets of one app and brand, then read ChassisTokens.SpaceContextMedium or ChassisTokensColor.ColorContextDefaultBgMain.
Android: add the Android library of your brand to your module, or dist/android/<app>/<brand>/res as a resource folder, then read R.dimen.space_context_medium or R.color.color_context_default_bg_main.
The guides for web, iOS and Android cover themes, screen sizes and setup in detail.
The repository is a pnpm workspace with two packages, in the layout of the other Chassis repositories such as chassis-react:
| Folder | Package | Contents |
|---|---|---|
packages/tokens/ |
@chassis-ui/tokens (published) |
source/ (the Tokens Studio token files), build/ (the Style Dictionary build), test/, dist/, the build configuration in package.json, CHANGELOG.md |
packages/site/ |
chassis-tokens-site (private) |
The documentation site and its dependencies |
The root holds the workspace configuration, the lint and format configurations with their dependencies, the CI workflows, the Changesets configuration in .changeset/, the repository scripts in build/ (sync-version-refs.js, release-notes.js, sync-submodules.js, html-validate.js), the assets submodule in vendor/assets and the site output in _site/. Paths in the sections below (source/, build/, test/, dist/, package.json) are relative to packages/tokens/.
You need Node.js 22 or later and pnpm. Clone the repository and install the dependencies; run every command from the root:
git clone https://github.com/chassis-ui/tokens.git chassis-tokens
cd chassis-tokens
pnpm installTo work on the tokens without the site's dependencies, install only the tokens package (and the root's lint tools):
pnpm install --filter @chassis-ui/tokensCONTRIBUTING.md describes how to change tokens, the build and the site, and what a pull request needs.
pnpm tokensThis writes the files of every brand, app, theme, platform and screen in the configuration to dist/<platform>/<app>/<brand>/. Filters build a part of it, and can be combined:
pnpm tokens --brand chassis
pnpm tokens --app docs --platform web
pnpm tokens --brand sinefil --platform ios android --screen large small
pnpm tokens --theme light dark --dry-run| Option | Effect |
|---|---|
--brand <brands...> |
Only these brands, such as chassis sinefil |
--theme <themes...> |
Only these themes, such as light dark |
--app <apps...> |
Only these apps, such as docs demo |
--platform <platforms...> |
Only these platforms, such as web ios android |
--screen <screens...> |
Only these screen sizes, such as large medium small |
--out <dir> |
Write to another output root instead of dist, such as dist-next |
--config <file> |
Read the build configuration from a JSON file instead of chassis.build in package.json |
--dry-run |
List the builds and the files each would write, without building |
--help, -h |
Show the help |
--version, -v |
Show the version |
A filter value that the configuration does not have, such as --brand chasis, fails the build and names the configured values, and so do filters that together select nothing, such as --app docs --platform ios.
Each brand and app is built once per list of token sets, writing the files of every platform of the app; a full build runs 16 builds. Set DEBUG=1 for verbose output with stack traces:
DEBUG=1 pnpm tokens --brand chassisCheck source/ for mistakes the build would accept, before building:
pnpm tokens:lint:sourceIt reads the token sets that source/$themes.json selects and fails, naming the token set and the token, when:
- the options of the
themeorscreengroup declare different names in their sets, as when one screen names a tokenheaders-gapand the othersheader-gap - a name has characters other than letters, digits and single hyphens, starts with a digit, or becomes the same SCSS, Swift, Android or Compose name as another token
- a font weight is not one the build knows (
Semi Bold,SemiBoldandsemi-boldare all600; a style word such asItalicmay follow) - a token has no
$type, or a value is not a token (such asvalueandtypewithout$) - two sets of one token-set list declare the same name with different types
Check that the committed dist/ matches a fresh build of source/:
pnpm tokens:verifyThe check builds into dist-next/ and compares every file with dist/, ignoring the timestamp and version lines of the file headers. It fails when a token name appears twice in one file, and when a reference names nothing the output declares: an Android @type/name without a <type name="name"> in the same file, or a SCSS $name that no SCSS file of the directory declares. It never writes to dist/.
# Build and check one platform only
pnpm tokens:verify --platform ios
# Compare the dist-next/ of the last run again without building, with the same
# --platform (from packages/tokens/)
node build/verify.js --skip-build --platform iosThe presets write nothing into dist/. Their reference output is in test/golden/, checked by:
pnpm tokens:verify:presetsSee what a change does to the tokens without reading dist/: the names added, removed and changed in value in each file, and a removed and an added name with the same value as a possible rename. Removed and renamed names are marked breaking. Header lines are ignored, and the icon assets are compared as whole files.
# The working dist/ against main's
pnpm tokens:diff
# Against another branch, tag or commit; --head takes a ref or a directory too
pnpm tokens:diff --base v0.5.3The report is Markdown. On a pull request, CI writes it to the job summary.
After changing tokens, run pnpm tokens, commit the updated dist/ and write the preset baselines again; CONTRIBUTING.md has the commands.
The tests use real tokens and the committed dist/, not mocks of Style Dictionary. They cover the golden check, the build plan, the CLI and platform configurations, the value encoders for iOS, Android and web and their references, the web reference policies, the preprocessor, filters, transforms, token order and the token diff report.
pnpm tokens:testSee packages/tokens/test/README.md for the test files, fixtures and preset baselines.
The build code is JavaScript with JSDoc types. TypeScript checks it (checkJs, not yet strict) against the types of Style Dictionary and Node.js 22:
pnpm tokens:typecheckThe build uses Style Dictionary 5.5 and @tokens-studio/sd-transforms 2.0 (type alignment, math, color modifiers and theme permutations). A build runs in three steps:
- Preprocess:
preprocessor.jsaligns types, splits every font weight into weight and style, and numbers the tokens in source order. - Transform: Style Dictionary transforms only what is safe before references are resolved: names, math, color modifiers,
remsizes and CSS shadows. - Format: The formats print one line per resolved token. Platform values (
UIColor(…), ARGB colors,sp/dp, quoting,em,var(--…),$…,@type/…and Swift constant references) come from pure functions invalues/and the web reference policies, which run after resolution.
Key modules in build/:
build.js: Build plan (one Style Dictionary instance per token-set list) and CLIconfig/: Platform configurations; the web presets sharewebConfig()fromweb.jspreprocessor.js: Token preprocessingfilters.js: Which tokens go into which filetransforms.js: Custom value transforms that run before resolution (rem,pxandvwsizes, CSS shadows)formats.jsandtemplates/: Output formats;templates/references.jsfinds the token that an iOS or Android reference namesvalues/: Value encoders for iOS, Android and webreference-policy.js: Which web tokens print a reference and which token it names, shared by both web formatscss-var-policy.js: Thevar(--…)names of the Chassis CSS formatscss-var-policy.js: The values and$…names of the SCSS variables formaticons.js: The icon assets, an Xcode asset catalog and Android vector drawables, written by Style Dictionary actions from the SVG icon tokenstheme-colors.js: The iOSColor.swiftwhose colors follow the appearance, written after the builds from the light and dark color filesverify.js: Golden check againstdist/and the preset baselineslogger.js: Loggingutils.js: Token type groups and number formatting
docs/architecture.md explains why the build works this way, and lists the output contract, the rules of every platform and the known oddities of the output.
The token files are in Tokens Studio format, with one theme group per collection. source/$themes.json has these groups and options:
| Group | Options |
|---|---|
brand |
default, chassis, sinefil, demo-a, demo-b |
app |
docs, demo |
theme |
light, dark |
screen |
large, medium, small |
See the Tokens Studio documentation and the Tokens Studio guide of this project.
The chassis.build key of package.json defines which brands, themes, screens and apps the build writes:
"chassis": {
"build": {
"brands": ["chassis", "sinefil"],
"themes": ["light", "dark"],
"screens": ["large", "medium", "small"],
"apps": {
"docs": ["web"],
"demo": ["ios", "android"]
}
}
}brands: Brand namesthemes: Theme names, such aslightanddarkscreens: Screen sizes for responsive tokens. Set to[]or omit to generate single number files without screen suffixes;source/$themes.jsonmust then have no screen groupapps: App names mapped to their platformsoptions: (Optional) Style Dictionary options by platform name, merged into the options of that platform, e.g.{ "web-px": { "outputReferences": true } }. The build fails when it names a platform that no app uses
The token sets of each file come from source/$themes.json. Color files use the sets of their theme, number files the sets of their screen, and every other file the sets of the first theme and the first screen listed here. Only the brands, themes, screens and apps listed here are built.
Platforms:
web: SCSS variables for Chassis CSS (rem units,var(--…)references)web-scss,web-px,web-vw: SCSS variables for other CSS frameworks (see below)ios: Swift types, one caseless enum per file (PascalCase naming)android: XML resources (snake_case naming)ios-swiftui: Swift types with SwiftUI values (Color,Font.Weight), written todist/ios-swiftui/<app>/<brand>/android-compose: Kotlin objects for Jetpack Compose (Color,.dp,.sp,.em,FontWeight), written todist/android-compose/<app>/<brand>/, in the packagechassis.tokensoroptions["android-compose"].packageName
File names:
- Web:
main.scss,color-light.scss,number-large.scss - iOS:
ChassisTokens.swift,ColorLight.swift,NumberLarge.swift(typesChassisTokens,ChassisTokensColorLight,ChassisTokensNumberLarge), andColor.swift(ChassisTokensColor), whose colors follow the light and dark appearance - Android:
main.xml,color_light.xml,number_large.xml, and the same resources as a resource tree underres/(values,values-night,values-sw600dp,values-sw840dp; the screen folders are set withoptions.android.screens)
The default web platform writes SCSS for Chassis CSS: theme-aware values print the CSS custom properties that Chassis CSS generates, such as var(--default-fg-main). A team with its own CSS framework needs plain SCSS variables instead. Select one of these platforms for a web app:
| Platform | Format | Sizes |
|---|---|---|
web |
cx/scss-chassis-css |
rem |
web-scss |
cx/scss-variables |
rem |
web-px |
cx/scss-variables |
px |
web-vw |
cx/scss-variables |
vw (16 px is 1vw) |
"apps": {
"docs": ["web-scss"]
}All web platforms write the same files to dist/web/<app>/<brand>/. The SCSS variables format prints resolved values:
$cx-color-accordion-item-fg-color: #161a1b !default;
$cx-border-radius-accordion-main: 0.375rem !default;With outputReferences, it prints the SCSS variable of the referenced token instead, where the Chassis CSS format prints a custom property (except shadows):
"apps": { "docs": ["web-px"] },
"options": { "web-px": { "outputReferences": true } }$cx-color-accordion-item-fg-color: $cx-color-context-default-fg-main !default;
$cx-border-radius-accordion-main: $cx-border-radius-context-medium !default;With references, load a color file before main.scss: its color tokens reference variables of color-<theme>.scss. The other files use only variables they declare themselves. The build fails when a reference names a variable that no file declares.
outputReferences also works on android: tokens print @color/…, @dimen/… references to resources of the same file. A token prints its value instead when the reference would not compile or would change the value, for example a font size in sp that references a size in dp.
On ios, outputReferences names another constant of the same class:
"options": { "ios": { "outputReferences": true } }public static let SizeUnit4 = DimensionBase4
public static let ColorAccordionItemFgColor = ColorContextDefaultFgMainiOS and Android follow the same rule: a token names a constant or resource of its own file only when that one has the same type and value. Base colors and sizes computed with math print their values. A Swift constant cannot name one of another file.
Each platform file in build/config/ can also be edited directly; web-px.js, web-vw.js and web-scss.js each call webConfig({ unit, format }) from web.js.
The documentation site in packages/site/ is built with Astro and the shared @chassis-ui/docs package. It covers the token categories, the Tokens Studio and Style Dictionary setup, and guides for web, iOS and Android projects. Its pages are in packages/site/content/.
# Build the chassis tokens of the docs app and run the site at http://localhost:4322/tokens/
pnpm dev
# Build the tokens and the site into _site/
pnpm build
# Build the site only
pnpm site:buildEvery pull request runs .github/workflows/ci.yml:
- Tokens (Node.js 22 and 24):
tokens:lint,tokens:lint:source,tokens:typecheck,tokens:test,tokens:verify,tokens:verify:presets - Site:
lint:prettier(the whole repository),site:lint,check:astro,site:build - Audit:
pnpm auditfor moderate advisories and above - Token diff: the token diff report of the pull request against its base branch, in the job summary
- Changeset: a pull request that changes
source/,build/ordist/must add a changeset (pnpm changeset) - Native iOS (macOS, Xcode): every Swift file of
dist/and of the preset baselines type-checked against the iOS simulator SDK, the asset catalogs compiled withactool, and a sample built that depends on the libraries ofPackage.swiftand reads their tokens - Native Android: the resources of
dist/and of the preset baselines compiled and linked against the Android SDK, the Compose objects compiled against Jetpack Compose, and a sample app built that depends on the Android libraries and reads their resources, with the Gradle project intest/native/android/
The two native jobs run on a pull request only when it changes source/, build/, dist/, the preset baselines or the checks themselves, and on every push to main. See Native compile checks.
Releases use Changesets. Pushing main runs the release workflow, after the same CI checks on that commit: pending changesets open or update a "Version Packages" pull request, which bumps the version and writes the CHANGELOG. Merging it publishes @chassis-ui/tokens to npm with trusted publishing and provenance, and creates a GitHub release from the CHANGELOG entry, with the Android libraries attached; its tag is the version of the Swift package. See Releases. Dependabot opens weekly pull requests for npm packages, GitHub Actions and the Gradle project of the native checks; the actions are pinned to commit SHAs.
This project is part of the Chassis Design System's multi-repository architecture:
| Project | Description |
|---|---|
| chassis-website | Main website and shared documentation package |
| chassis-css | CSS framework and component library |
| chassis-react | React component library |
| chassis-tokens | Design token generation and management (this repository) |
| chassis-icons | Icon library and build toolkit |
| chassis-assets | Multi-platform asset management |
| chassis-figma | Figma component documentation |
All documentation sites share the @chassis-ui/docs package for consistent layouts, components, and styling.
Contributions are welcome. CONTRIBUTING.md covers the setup, the conventions and what a pull request needs; please follow the Code of Conduct. Report security vulnerabilities privately, as described in SECURITY.md.
MIT License — see LICENSE file for details.