Signature#
osy compile --theme <file>
osy validate --theme <file>Summary#
An app's look lives in its theme block (theme tokens). --theme <file> compiles the app with a different
one, without editing a line of it:
$ osy validate --theme ../brand/acme.osy # offline, no database — check it resolves first
$ osy compile --theme ../brand/acme.osy # then compile the app wearing itTwo things this is for:
- One brand, many apps. A theme cannot be imported across apps — a manifest globs its own directory — so sharing a house style otherwise means copying a file into every project. One file passed at compile time dresses all of them.
- Presenting several apps together. Documentation and demos show an app to explain a feature. When the apps each have their own palette, a reader compares styling instead of reading the point. Compile them under one theme for the occasion; the apps keep their own.
What it does#
--theme takes the theme block out of the app's own sources and puts the one in your file there instead. The
rest of the app — its entities, its pages, its components — compiles exactly as it always does; only the tokens
change. Nothing in the app has to be written differently to be themeable this way.
It replaces, it does not merge#
Every theme declaration in the app's own sources is removed, and the file you pass supplies the theme. Tokens
the app declared and the override omits are gone, not inherited.
This is deliberate. A merge would produce a theme that exists in neither file — so the thing on screen could not be reproduced by compiling anything, which is exactly what you need from a screenshot or a shipped build.
A token the override lacks is an error, by name#
Because it replaces, an app that names a token your file does not declare will not compile:
model/chrome.osy:12:53 ERROR RESOLVE_ERROR UI: the `Colors` group declares no token 'Bulb'
— its tokens are Bg, Border, Danger, Muted, OnBg, OnPrimary, Primary, Success, Surface, …That is the useful answer, not an obstacle: it tells you either to add the token to your theme, or that this app's look is not substitutable. An app built on a handful of semantic colours takes almost any theme; one whose palette is its subject — a game, a visual demo — will list everything it needs, which is a fair description of why it should keep its own.
So a theme meant to dress several apps is a superset: it declares every token those apps name. Start from the kit's own token names (theme tokens) — an app that shadows kit tokens rather than inventing parallel ones is an app almost any override fits.
The swap is announced#
Every run says what it did:
⚠ theme override: acme.osy replaces `Doc` in model/theme.osy.The app is not wearing its own look, so the output says so — a build or a screenshot taken from it should never be mistaken for the app as it ships.
What goes in the file you pass#
One theme block, and normally nothing else:
theme Docs {
Colors {
Primary = "#125E7A";
OnPrimary = "#FFFFFF";
Bg = "#FAF9F7";
OnBg = "#14181D";
Surface = "#FFFFFF";
Border = "#E4E2DC";
}
Radius { Md = "10px"; }
}A file declaring no theme is refused, and so is one declaring two — with two, which one dressed the app would depend on declaration order, which is not something you should have to know to read a screenshot.
Anything else in the file compiles as usual, and a theme block in the app that shares a file with components
loses only the block: the rest of the file is untouched.
See also#
- theme tokens — declaring tokens, the groups they live in, and light/dark modes.
- color palettes — turning one seed colour into a full ramp.
- Osyrin.Ui (the UI kit) — the starter theme every app inherits, and the token names to shadow.