Fonts, images, icons and other design files of the Chassis Design System, built from one source tree into the names, formats and folders of the web, iOS and Android.
Chassis Assets holds the design files a designer exports, in source/<brand>/<app>/<type>/, and a build that copies them to dist/<platform>/<app>/<brand>/ with the naming and the formats of each platform. The build is file-driven: add a file to source/ and it is in the output of the next build. There is no manifest to maintain.
The repository is not published to npm. The Chassis documentation sites vendor it as a Git submodule and build it; an app takes the archive of its platform, app and brand from a GitHub release, or vendors the repository too. The documentation site covers the asset types and how to use the output on each platform.
The Quick Start page of the site has the same steps, and goes on to adding assets and using them in a project.
You need:
- Node.js 22.12 or later, see
enginesinpackage.json. The repository pins 24 in.nvmrc. - pnpm, the version named by
packageManagerinpackage.json. Runcorepack enableonce and pnpm is used at that version. - Git LFS, from git-lfs.com. Run
git lfs installonce on your machine before cloning. Fonts and raster images are Git LFS files.
Clone the repository, or add it to a project as a Git submodule:
git clone https://github.com/chassis-ui/assets.git chassis-assets
cd chassis-assetsgit submodule add https://github.com/chassis-ui/assets.git assets
cd assetsBuild the assets:
pnpm assetsThe build imports Node.js modules only, so it runs on a fresh clone with nothing installed. Install the dependencies to run the tests, the checks and the documentation site:
pnpm installA clone made without Git LFS has pointer files in place of the fonts and the images. The build fails on them and lists them; run git lfs pull.
A pnpm workspace. Run every command from the root.
source/ -> The assets, as source/<brand>/<app>/<type>/
dist/ -> The build output, as dist/<platform>/<app>/<brand>/. Not committed
package.json -> The `chassis` configuration and the commands
chassis.checks.json -> What the checks verify: the consumer contracts, the names the lint keeps
packages/assets/ -> @chassis-ui/assets: the build in build/, its tests in test/
packages/site/ -> The documentation site, built with Astro
build/ -> Scripts of the repository: releases, the site's checks, the font instancer
docs/architecture.md -> How the build works, module by module
docs/decisions.md -> The decisions behind the build, by number
docs/fonts.md -> How the fonts of a brand are prepared
ref/OPEN.md -> What is left to do, decide or check
.changeset/ -> The changesets of the next version
source/
├── default/ -> The fallback brand: the files every brand gets
│ ├── docs/ -> The app `docs`
│ │ ├── images/ -> Images, with the @2x and @3x variants the designer exported
│ │ └── icons/ -> cx-sprite.svg
│ └── demo/ -> The app `demo`, with the build output of @chassis-ui/icons in icons/
└── <brand>/ -> The files of a brand: `chassis` and `sinefil`
└── <app>/
├── fonts/ -> Font files, their stylesheet and their licenses
├── images/ -> The logos, the favicons and the other images of the brand
└── other/ -> Any other folder name is copied as it is
A file under source/<brand>/<app>/ replaces the file of the same path under source/default/<app>/. A brand needs only the files that differ. In this repository each brand holds its fonts, its logos and its favicons, and default holds what every brand shares.
dist/
├── web/
│ └── docs/
│ ├── chassis/ -> What the Chassis sites read, built by pnpm assets:site
│ └── sinefil/
├── ios/
│ └── demo/
│ ├── chassis/
│ └── sinefil/
└── android/
└── demo/
├── chassis/
└── sinefil/
What each platform gets:
| Platform | Names | Fonts | Images | Icons |
|---|---|---|---|---|
| Web | kebab-case, @2x and @3x kept |
WOFF, WOFF2 and the stylesheets | Every format | Every file |
| iOS | snake_case, @2x and @3x kept |
TTF and OTF | Every format but WebP | SVG, PDF and PNG |
| Android | snake_case, ic_ prefix under icons/ |
TTF and OTF | Every format, in density folders, indicator removed | SVG |
On Android an image without an indicator goes to drawable/, @2x to drawable-xhdpi/ and @3x to drawable-xxhdpi/, under the subfolder the image is in. The license of a font is copied with it on every platform. Files such as .DS_Store are left out, and two files that get the same name after renaming are reported.
pnpm assets # Every brand, app and platform
pnpm assets --brand chassis # One brand
pnpm assets --brand chassis sinefil # --brand, --app and --platform take one or more values
pnpm assets --app docs --platform web # Filters combine
pnpm assets:site # The output the Chassis sites read: --clean --brand chassis --app docs| Option | What it does |
|---|---|
--brand <name...> |
Only these brands |
--app <name...> |
Only these apps |
--platform <name...> |
Only these platforms |
--type <name...> |
Only these type folders of an app, such as images and icons |
--include <pattern...> |
Only the files that match a pattern, by their path in the folder of the app, such as "images/home/**" |
--clean |
Remove the output first, of the selected jobs when filtered |
--no-clean |
Keep the output even for a full build |
--out <dir> |
Output folder, default dist |
--cwd <dir> |
Repository root, default the nearest folder upward whose package.json has a chassis block |
--watch |
Build, then build the jobs of a brand and an app again when a file of theirs under source/ changes |
--dry-run |
Print the jobs and their file counts, write nothing |
--allow-lfs-pointers |
Copy Git LFS pointer files instead of failing. Also CHASSIS_ALLOW_LFS_POINTERS=1 |
--vector-drawables |
Write the SVG icons of Android as vector drawables, .xml in place of .svg. Needs pnpm install |
--asset-catalog |
Write the images of iOS as an asset catalog, Assets.xcassets in place of images/ |
--res |
Write the fonts, images and icons of Android as a res/ folder, res/font/ and res/drawable*/ |
--optimize |
Write the images again under their names where that makes them smaller, with the settings of chassis.optimize. Needs pnpm install |
--webp |
Write the PNG and JPEG images as WebP too: beside the file on the web, in place of it on Android. Needs pnpm install |
--avif |
Write the PNG and JPEG images of the web as AVIF too. Needs pnpm install |
--subset [range...] |
Write the WOFF and WOFF2 fonts of the web again under their names with the characters of the ranges only, such as latin or U+0370-03FF. Default: chassis.subset, or latin and latin-ext. Needs pnpm install |
--split, --no-split |
With --subset, write each declared font once per range too, with a rule per range in its stylesheet, or one file. Default: split of chassis.subset, or one file |
--quiet |
Print errors only |
--help, -h |
Print the options |
--version, -v |
Print the version |
A full build removes dist/ first; a filtered build keeps it. A filter value that is not configured fails the build and names the configured values.
| Command | What it does | Options |
|---|---|---|
pnpm test |
The tests of the build, on the fixture in packages/assets/test/fixtures/. Needs no Git LFS files |
|
pnpm test:ci |
The Lint, Assets, Site and Audit jobs of CI in one command, without the iOS catalogs, the changeset check and the report of pnpm audit |
|
pnpm test:golden |
Writes packages/assets/test/golden/ again, after an intended change to the output |
|
pnpm test:ios |
Compiles the asset catalogs of an output with actool, after pnpm assets --asset-catalog. Needs Xcode |
A folder, default dist |
pnpm assets:analyze |
Sizes, types, largest files and files with the same content, of source/ and of an existing output |
--brand, --app, --platform, --out, --cwd, --quiet |
pnpm assets:validate |
Checks an existing output against source/ and the configuration |
--out, --cwd |
pnpm assets:lfs |
Prints the Git LFS paths that a build with the same filters reads, for git lfs pull --include |
--brand, --app, --platform, --type, --include, --cwd |
pnpm assets:contract |
Checks that each output has the files of its contracts in chassis.checks.json |
--out, --cwd |
pnpm assets:verify |
The validator, then the contract check | --out, --cwd |
pnpm assets:lint:source |
The names and the layout of source/, LFS pointers, the fonts against their stylesheets and against each other |
--cwd, --allow-lfs-pointers |
pnpm assets:lint |
ESLint over the build, its tests and the repository scripts | |
pnpm assets:typecheck |
TypeScript over packages/assets/build/, from its JSDoc |
|
pnpm lint:prettier |
Formatting, across the repository | |
pnpm check |
The types of the site, then pnpm check:pnpm: pnpm audit --prod --audit-level moderate |
Every pnpm assets* command prints its options with --help. docs/architecture.md says which checks to run for which change.
pnpm dev # Build the docs assets and run the site at http://localhost:4325/assets/
pnpm site:build # Build the docs assets, the site and its search index into _site/
pnpm site:assets # Build the docs assets with the fonts of the site: subsetted, a file per range
pnpm astro:dev # Run the site without building the assets
pnpm astro:build # Build the site without the assets and the search index
pnpm site:lint # ESLint, Stylelint, unused Sass variables and Prettier over the site
pnpm site:lint:html # html-validate over _site/, after a build
pnpm site:lint:vnu # The Nu Html Checker over _site/, after a build; skipped without Javapnpm changeset # Describe a change to source/ or to the build
pnpm changeset:version # Bump the version and write the changelog
pnpm release:archives # Write the archives of a release to .cache/release/, after pnpm assets --optimizeSee Releases and the changelog.
The chassis block of the root package.json names the brands and, for each app, its platforms:
"chassis": {
"defaults": {
"brandFolder": "default"
},
"build": {
"brands": ["chassis", "sinefil"],
"apps": {
"docs": ["web"],
"demo": ["ios", "android"]
}
}
}defaults.brandFolderis the folder ofsource/that every brand falls back to.build.brandsare the brands to build. A brand without a folder insource/is built from the fallback alone, aschassisis.build.appsmaps each app to its platforms:web,iosorandroid.optimize, optional, holds the settings of--optimize,--webpand--avif, such as"jpeg": { "quality": 80 }. Without it the options change no visible pixel. It turns nothing on.ignore, optional, lists names of files and folders that the build leaves out beside its own list, such as["*.bak", "drafts"]. A pattern is a name with*for any run of characters, and matches at any depth. The checks read the same list.formats, optional, names for a platform and a type folder the extensions the platform keeps, such as{ "web": { "fonts": [".woff2", ".ttf", ".css", ".txt"] } }. The types arefonts,imagesandicons. A list replaces the rule of the platform for the type; what the block does not name keeps its rule.subset, optional, holds the settings of--subset.rangesare the ranges, such as["latin", "greek"]; without it the option keepslatinandlatin-ext.axesare the axes of a variable font that are fixed at a value for a brand, such as{ "chassis": { "opsz": 14 } }; without it no axis is fixed.split,trueorfalse, writes a font that a stylesheet declares once per range too, asui-latin.woff2, and the rule of the stylesheet once per range with itsunicode-range; without it a font is one file, and--splitand--no-splitwin over it. It turns nothing on.
What the checks verify is in chassis.checks.json, beside package.json. The build does not read it, and the file is optional:
contractslists, for a job, the files that a consumer reads by name from its output.pnpm assets:contractchecks them.lint.allowlists the source files that break a naming rule and are kept, each with the reason.pnpm assets:lint:sourcewarns about them instead of failing.
A team that adopts the repository changes source/, the chassis block of package.json and this file, which it fills with its own lists or deletes. The build in packages/assets/build/ names no brand, app or file. The build system page has the schema of both.
The build writes one job per brand, app and platform: the files of source/default/<app>/, replaced by those of source/<brand>/<app>/, with the processing of the platform, into dist/<platform>/<app>/<brand>/.
Every Chassis site vendors this repository as the submodule vendor/assets, pinned to a commit of the app/docs branch, and builds it with the two commands below. A change that breaks a row breaks the sites; pnpm assets:contract checks the files on every commit.
| Consumers rely on | Detail |
|---|---|
The app/docs branch |
chassis-docs sync-submodules moves the submodule of a site to its tip |
| Git LFS | git lfs pull gives the real fonts and images |
pnpm install --ignore-workspace |
Installs the root package only. The build needs none of it |
pnpm assets:site |
Writes dist/web/docs/chassis/, which a site copies to /static/ |
images/ of that folder |
site-logo.svg, site-logomark.svg, the favicons, apple-touch-icon.png, social-image.png, the home page images under home/, the logos under logo/ |
icons/cx-sprite.svg |
The sprite of the home page icons |
images/figma/components/<component>/<mode>/ |
The screenshots that the pages of chassis-figma read by name |
sinefil.org takes the archive chassis-assets-web-docs-sinefil-<version>.zip of a release instead, and reads its fonts, logo, logomark, favicons, other/manifest.json and social image by name. The full list for both, with the code that reads each file, is contracts in chassis.checks.json.
Work goes to develop. CI runs there and on pull requests, with the jobs Lint, Assets, Site and Audit, and Changeset on pull requests. The commit that passed is then pushed to staging, main and app/docs, which accept only a commit with those checks. A push to main with a new version creates its tag and its GitHub release, see Releases.
| Project | What it is | Docs |
|---|---|---|
| chassis-website | chassis-ui.com and @chassis-ui/docs, which every site is built on |
chassis-ui.com |
| chassis-tokens | Design tokens, published as @chassis-ui/tokens |
/tokens/ |
| chassis-css | The CSS framework, published as @chassis-ui/css |
/css/ |
| chassis-icons | The icon library, published as @chassis-ui/icons |
/icons/ |
| chassis-assets | This repository: fonts, images and other assets. Not on npm | /assets/ |
| chassis-figma | Documentation of the Figma libraries | /figma/ |
| chassis-react | React components, published as @chassis-ui/react |
Not yet routed on chassis-ui.com |
Branch from develop and open a pull request against develop. .github/CONTRIBUTING.md covers the setup, changing assets, the build and the site, the checks to run for each, changesets and releases. WRITING.md is the style guide of the documentation, and AGENTS.md holds the rules for AI coding agents. Everyone taking part follows the Code of Conduct; report a vulnerability as SECURITY.md says.
MIT License. See LICENSE.