# compiling with a different theme

> `--theme <file>` compiles an app with the theme in that file INSTEAD of its own — a replacement, never a merge, so the look you get is one you can point at a file for. `--theme Looks.Relay` dresses it in a look the way deriving from it would: the look's tokens win, and the app's own tokens the look does not declare are kept.

<!-- id: ui-theme-override · area: ui · stability: stable · html: https://osysharp.com/reference/ui/theme-override/ -->

## Signature   {#signature}
```text
osy compile  --theme <file>
osy validate --theme <file>
```

## Summary        {#summary}
An app's look lives in its `theme` block ([theme tokens](https://osysharp.com/reference/ui/theming/)). **`--theme <file>` compiles the app with a different
one**, without editing a line of it:

```console
$ osy validate --theme ../brand/acme.osy     # offline, no database — check it resolves first
$ osy compile  --theme ../brand/acme.osy     # then compile the app wearing it
```

Two 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   {#description}

`--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   {#replaces}

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](#look).)

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   {#missing-tokens}

Because it replaces, an app that names a token your file does not declare will not compile:

```text
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](https://osysharp.com/reference/ui/theming/)) — an app that shadows kit tokens rather than inventing parallel ones is an
app almost any override fits.

## The swap is announced   {#announced}

Every run says what it did:

```text
⚠ 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   {#the-file}

One `theme` block, and normally nothing else:

```osy title="a theme file to pass to --theme" test app=ui-theme-override
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`   {#look}

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](https://osysharp.com/reference/ui/looks-kit/)) on your own pages before you derive
from it:

```console
$ 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 it
```

The 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:

```text
⚠ 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?   {#test}

`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   {#see-also}
- [Looks for business apps — start from a finished design and make it yours](https://osysharp.com/reference/ui/looks-kit/) — five finished looks to try with `--theme Looks.X`, then derive from.
- [theme tokens](https://osysharp.com/reference/ui/theming/) — declaring tokens, the groups they live in, and light/dark modes.
- [color palettes](https://osysharp.com/reference/ui/palette/) — turning one seed colour into a full ramp.
- [Osysharp.Ui (the UI kit)](https://osysharp.com/reference/ui/kit/) — the starter theme every app inherits, and the token names to shadow.
