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. (A LOOK named instead of a file keeps them —
see below.)
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.
Can I name a look instead of a file? — --theme Looks.Relay#
Yes. An argument that is not a file but a theme's NAME dresses the app in a theme deriving from it, with nothing written to disk — the quickest way to try a finished look (Looks for business apps — start from a finished design and make it yours) on your own pages before you derive from it:
$ osy validate --theme Looks.Ledger # does the app resolve in it?
$ osy compile --theme Looks.Ledger # the browsable app, wearing it
$ osy test --pixels --theme Looks.Civic # every UI test photographed in itThe name is resolved like any theme base — Ledger, Looks.Ledger or the full Osysharp.Ui.Looks.Ledger — so a
typo is refused with the looks there are. An argument that is neither an existing file nor a dotted name is refused
naming both things it could have been. The app must use the look package (use Osysharp.Ui.Looks@0;).
A look keeps the app's own tokens. Unlike a file, a look is applied exactly as theme Brand : Looks.Relay { … }
would apply it: every token the look declares comes from the look, and every token the APP declares that the look
does not — a pane width of its own, a colour only its pages use — is kept. So an app with tokens of its own wears any
look with no theme file written for it, and the run names what it kept:
⚠ theme override: Looks.Relay.osy replaces `Helpdesk` in model/theme.osy. Kept 3 tokens of the app's own that the
look does not declare: Length.ViewsW, Length.ListW, Length.ContextW.What that compiles is an ordinary derived theme — the app's theme name, : Looks.Relay, and the kept tokens — so it
is reproducible by writing it down, which is also how you make the look the app's own once you have picked one.
Does osy test take it too?#
osy test --theme <file or look> runs the same tests over the app dressed in that theme — so one set of UI tests
photographs every look (--pixels), and one set of Assert.Contrast checks ([[testing-theme#contrast]]) proves each
look's colours read. The run says the theme was swapped, exactly as a compile does.
See also#
- Looks for business apps — start from a finished design and make it yours — five finished looks to try with
--theme Looks.X, then derive from. - theme tokens — declaring tokens, the groups they live in, and light/dark modes.
- color palettes — turning one seed colour into a full ramp.
- Osysharp.Ui (the UI kit) — the starter theme every app inherits, and the token names to shadow.