Skip to content

Schema reference

The standard is three lexicons (AT Protocol schemas) under the place.mode.standard.* namespace. Two are records you save to your account; one is a set of shared type definitions the others reuse.

Lexicon Kind What it is
place.mode.standard.theme record A saved theme — one complete look, keyed by the expression it serves.
place.mode.standard.profile record Your participation (no default-theme pointer).
place.mode.standard.defs definitions Shared building blocks the theme record references.

One complete look, saved to your account. Its record key is the expression it serves…/theme/dark, …/theme/com.homedepot.easter, …/theme/default — so a repo holds at most one theme per expression (a second write to the same key overwrites). You can still publish many themes; each just serves a distinct expression. The record is self-contained: it carries its own full copy of the values and references no other theme.

Field Type Required Notes
(record key) string · valid rkey The expression this theme serves (dark, com.homedepot.easter, default). Case-sensitive; any valid record key is a safe data-mode token (the delimiters data-mode uses — / and whitespace — can’t appear in one). This is the identity — there is no expresses field, and displayName below is presentation only. See Common expressions.
displayName string · ≤64 graphemes Optional display name, presentation only — identity and matching stay on the record key; an app never matches or dedupes on it. (Same limits as a Bluesky profile’s displayName.)
description string · ≤256 graphemes Optional human-readable description.
aspects aspects The theme’s renderable style, grouped by aspect — the only part rendered to CSS variables. A required container; each aspect inside is optional but complete when present.
fonts array of fontDescriptor Font faces the theme’s text relies on — theme-level assets referenced by text.*.family, rendered to document-global @font-face (not variables), which is why they sit here beside aspects rather than inside it. Optional.
createdAt string · datetime ISO-8601 timestamp.
// saved at rkey "dark" → at://did:plc:…/place.mode.standard.theme/dark
{
"$type": "place.mode.standard.theme",
"displayName": "Midnight Ride",
"description": "My dark look.",
"aspects": {
"color": {
"control": { "background": "#0b1020", "foreground": "#e6e9f5", "border": "#1c2540" },
"action": { "primary": { "…": "" }, "secondary": { "…": "" }, "auxiliary": { "…": "" } },
"surface": { "primary": { "…": "" }, "secondary": { "…": "" }, "auxiliary": { "…": "" } }
}
},
"createdAt": "2026-07-09T00:00:00Z"
}

A singleton (record key self) that declares you participate in the standard. It’s a participation flag and the anchor for domain verification — and the natural home for future user-level settings. There is no default-theme pointer: your baseline look is simply the theme you saved at the reserved record key default, matched like any other concept (see Common expressions).

Field Type Required Notes
createdAt string · datetime
{
"$type": "place.mode.standard.profile",
"createdAt": "2026-07-09T00:00:00Z"
}

The shared building blocks. The theme record references these by name — you never write them standalone. This is where the 33 intents live, grouped by aspect.

The container the theme record’s aspects field points at — the renderable style, grouped by aspect. Each aspect is optional but complete when present; new aspects (motion, density, …) are added here as the core grows.

Field References Notes
color colorAspect The 21 color intents. Optional, complete when present.
typography typographyAspect The 12 text intents. Optional, complete when present. (Fonts its text relies on are theme-level — see the theme’s fonts.)

The three color values for one purpose. All required.

Field Type Notes
background string · ≤128 Any CSS <color>.
foreground string · ≤128 Text / icon color.
border string · ≤128 Border color.

Typography metrics for one text priority. All required. (No color — a text’s color comes from the surface it sits on.)

Field Type Notes
size string · ≤128 CSS <length-percentage> (e.g. 1rem).
weight string · ≤128 CSS <font-weight>.
family string · ≤256 CSS font-family; matched to a fonts entry by name.
lineHeight string · ≤128 CSS line-height.

Three priority levels, each referencing the intent above. All three required.

Field References
primary colorIntent / textIntent
secondary colorIntent / textIntent
auxiliary colorIntent / textIntent

The complete color aspect — 21 intents. All required.

Field References Purpose
control colorIntent Form inputs. No priority — all controls are peers.
action colorPriorities Buttons / links, by priority.
surface colorPriorities Non-interactive containers, by priority.

The complete typography aspect — 12 text intents.

Field Type Required Notes
text textPriorities The 12 text intents.

The font faces the text relies on are not part of this aspect — they’re theme-level assets in the theme’s fonts, matched to a text.*.family by name. (An aspect renders to scoped variables; fonts render to document-global @font-face, so they live separately.)

One CSS @font-face. Per the CSS spec only two things are necessary, so only family is schema-required and a source (src or file) is required by the standard. A minimal entry is { family, src }.

Field Type Required Notes
family string · ≤256 The font-family name.
src string · ≤1024 see note A URL, or a CSS src value.
file blob · font/* · ≤5 MB see note A font file stored on your account; preferred over src (it travels with you and can’t rot).
weight string · ≤32 font-weight descriptor or range.
style string · ≤32 font-style.
stretch string · ≤64 font-stretch / width.
unicodeRange string · ≤2048 unicode-range — lets several entries share one family for subsetting.
display string · ≤32 font-display.

Each intent’s path becomes a CSS custom property — color.surface.primary.background--color-surface-primary-background (camelCase segments kebab-cased: lineHeightline-height). That deterministic naming is what lets one person’s theme travel between apps. An app applies a theme by setting those variables on an element marked with a data-mode attribute (never :root) and ranking competing sources with cascade layers; the Mode Contract has the full rules for emitting and resolving these.