Skip to content

Common expressions

An expression is the concept a theme serves — the reason it exists, and it is the theme’s address: a theme’s record key. A theme saved at …/theme/dark serves dark. The name is just a string, compared for exact equality and never fetched. This page shows how expressions are named and lists a handful of common ones as examples. Nothing here is required or official — they’re illustrations of the convention — save for one reserved word, default (below).

There’s really one rule, and you can read it at a glance:

  • A bare, lowercase worddark, critical — is a common concept: the obvious shared spelling that anyone might use.
  • A dotted, reverse-DNS stringcom.homedepot.easter, com.alice.midnight — is your own concept, rooted in a domain or handle you control, yours to define however you like.

The presence of a dot tells you which kind you’re looking at — no lookup required. An expression is both the theme’s record key and the data-mode value an app writes into the page (<main data-mode="dark">), so it must be a valid record key — case-sensitive (keep it lowercase by convention), with dashes within a word (high-contrast). Any valid record key is automatically a safe data-mode token: the delimiters that data-mode parsing relies on — the slash (the aspect delimiter, as in dark/color) and whitespace (which separates aspect fragments) — are deliberately characters that can’t appear in a record key at all, so there is nothing to avoid and no word is off-limits as an expression.

Making the expression the record key is what gives the standard free uniqueness: a repo is a key→record map, so you can hold at most one theme per expression — a second dark overwrites the first. Two of your themes can never ambiguously both claim dark (or default); the collision is impossible, not merely resolved.

Illustrative only — reach for these where they fit, or don’t.

Expression Commonly conveys
light A light appearance, for content on a light background.
dark A dark appearance, for content on a dark background.
critical A state that should give someone pause — an error, a destructive or dangerous action.
selected Content the person has actively chosen — a picked row or card.
compact A denser treatment: closer spacing, tighter type, more in view.
hero A roomy, spacious treatment: larger type, generous space.
promo A promotional treatment that pulls focus to a feature or campaign.

When a concept is yours — a brand moment, a personal look, something niche — name it in a namespace you control:

com.homedepot.easter a brand's seasonal look
com.alice.midnight a personal look

A look that means two things at once is a single concept with its own name — com.acme.critical-dark, not critical and dark blended together. Matching stays one rule: exact string equality. The reasoning is in the Mode Contract and the FAQ.

There’s a practical reason to namespace, too: two authors on one page can’t share a bare word for different looks. An app targets a look by its expression ([data-mode="dark"]), and that matches every dark on the page — so if two independent authors each ship their own distinct dark under bare dark, the two would collide. Give each its own name (com.alice.dark, com.bob.dark) and they coexist cleanly, each applying only where its own marker sits. Bare dark is for the one shared dark everyone agrees on — the one a viewer’s dark can override across the whole page.

One look, many names — a tooling job, not a protocol one

Section titled “One look, many names — a tooling job, not a protocol one”

One look can absolutely serve several concepts — a dark that is also your default, or that also overrides com.homedepot.dark. But because the expression is the record key, a theme serves exactly one. So a look with several names is several self-contained records, one per key, each carrying the same values:

…/theme/dark # my dark
…/theme/default # same values — also my baseline
…/theme/com.homedepot.dark # same values — also overrides that brand's dark

Keeping those copies identical is a job for your authoring tooling, not the protocol. Your design tokens are the single source of truth; a build step compiles them to one record per key and writes each. The network stores independent, self-contained artifacts — like compiled CSS, not a live-linked database. The protocol has no notion of aliasing: no expresses list, no reference from one theme to another. That’s deliberate — with nothing pointing anywhere, nothing can dangle, and if a build drops an intent, your tooling’s pre-publish schema check catches it (on your side, loudly — see Publish a theme; most hosts won’t validate a third-party record for you) rather than silently breaking every app downstream.

This is also the easy way to override a specific concept: have your tooling emit a copy at that concept’s key (see Generic invites).

None of this is a blend. Saving the same values at dark and default says “these values are both my dark and my baseline.” A look that genuinely combines concepts is a single composite name (com.homedepot.easter-dark), saved as its own theme at that one key. One record is always one complete set of values.

Everything else on this page is illustrative — but the standard reserves one name, because apps rely on its meaning: default.

default names the viewer’s baseline look — the one that fills the page root and any scope no specific concept has claimed. It works like any other concept, by string-matching — it’s just a reserved record key:

  • An app marks its baseline scopes data-mode="default".
  • A viewer makes a look their default by saving it at record key default (…/theme/default) — usually their tooling writes a look they already have to that key too.
  • An app’s own default look lives at its default key, so it fills the baseline (in the app layer) when there’s no viewer.

Because it resolves by ordinary key-matching, default needs no special field — it’s what a look designates instead of the (removed) profile.defaultTheme pointer. Adopting someone else’s look as your default is fork-and-copy: your tooling writes their values into your own …/theme/default.

So don’t use default as the name of your own distinct concept — it carries this baseline meaning everywhere.

Choosing a bare word or a namespaced one isn’t only about avoiding collisions — it’s a statement about how open a slot is to your own look.

When an app marks a region with a bare word like dark, it opens that region to the shared concept: “this is dark in the common sense — if you have a dark, yours goes here, and yours takes precedence.” The obvious spelling is an invitation.

When it uses a namespaced word like com.homedepot.easter, it reserves the region for its own concept: your generic dark won’t match it, so the brand look holds for anyone who didn’t specifically ask for something else.

But reserved isn’t locked. Because looks line up by exact record key, you can always make your own version of someone else’s concept — save a theme at their key (e.g. …/theme/com.homedepot.easter, carrying the values you want there; your tooling can copy them from a look you already have). Then wherever they placed that concept, your take on it applies instead (a conforming app honors the viewer). The brand owns where the concept lands; you own how your version of it looks.

So there’s a spectrum, and it runs on how generic the string is:

  • The more generic the string, the more it expects to adopt your expression. Bare dark is maximally open — almost everyone has a dark, so it readily gives way to yours.
  • The more specific the string, the more deliberate an override has to be. com.homedepot.easter won’t budge for a generic preference, but it still yields to someone who deliberately curated that exact concept.

Nobody is ever truly locked out — the only difference is whether your look slots in automatically (you already have the shared word) or on purpose (you adopt the specific one). That guaranteed-but-deliberate override is the Mode Contract’s accountability valve.