Updated: 2026-08-29 · Status: reconciled against the shipped Mobile Content API (AB#2609)
TL;DR — Tabular reference for the theme data structure Vlad Oprica's
Mobile Content API actually shipped: SiteDto/PageDto
theme assignment (activeThemeId/activeThemeKey),
the ThemeDto field schema, how Container/Item content overrides
individual tokens, a worked example for UAL, and the token catalog (95 tokens
across 15 categories per the Admin Portal's token registry).
Reconciled 2026-08-29 (AB#2609) — the cascading model below was not built.
Earlier revisions of this doc were the tabular reference for a proposed
ThemeNode schema with scopeType/scopeKey/
parentThemeId cascading inheritance (Site → Page → LEC, sparse
overrides layered root-to-leaf) and a mainBanner asset override. None
of that shipped. The real, live theme model is simpler: a theme is an independent
row (GUID id + semantic key + flat token dictionary),
assigned to a Site via activeThemeId/activeThemeKey,
with an optional full-swap override at the Page level — there is
no cascade below Page, no per-node ThemeNode/scopeType
row, and no mainBanner field. This page has been rewritten to describe
the real schema. See Theme Token
Inheritance Model for the full account of what was proposed vs. what shipped.
A theme is a single, independent row — not a node in an inheritance chain. The
Dynamic Feed guide and the Admin Portal guide document slightly different field sets
for the same underlying row (read-side vs. write/manage-side); both are shown below.
See Theme Token Inheritance Model for why
the cascading ThemeNode schema previously documented here was never
built.
Field
Type
Where documented
Description
id
int (Dynamic Feed guide) / guid (Admin Portal guide)
Both
Database PK — the FK used as SiteDto.activeThemeId / PageDto.activeThemeId. The type disagreement between Vlad Oprica's two docs is unresolved upstream; treat as opaque client-side.
key
string
Both
Unique, lowercase semantic identifier, e.g. ual-default. Matches activeThemeKey on Site/Page.
title
string?
Admin
Human-readable display name. Used as the slug source when key is omitted on create.
description
string?
Both
Additional notes / label.
tokens
dictionary<string, string>
Both
Flat token-path → value map. Every theme carries a full set — there is no sparse-override/inheritance concept between themes.
typography
dictionary<string, TypographyStyle>
Admin
Named typography bundles (font family/size/weight/style/line-height/transform/letter-spacing/color together), managed separately from tokens via the Typography endpoints. Empty when none are set.
workflowState
int (0=Draft, 1=Published, 2=Unpublished, 3=Archived)
Admin
Same 4-state workflow as Sites and Pages — not a per-node Live/Draft flag.
activeSnapshotId
guid?
Admin
GUID of the last-published snapshot. Null if never published.
hasDraftChanges
bool
Admin
True when live token rows changed since the last publish.
isLocked / lockedByUser
bool / string?
Admin
Edit-lock state, same mechanism as Page locking.
createdAt / updatedAt
DateTime
Admin
UTC record timestamps.
No per-node exhaustiveness invariant to enforce. Because there is
no cascade, there is no "root must define every token" rule — every theme row is
independently a full set. The token-count denominator for any "N of M tokens set"
UI is simply the size of the canonical registry (95 — see the
catalog note below), the same for every theme.
Theme assignment: Site and Page
Level
Fields
Behavior
Site
activeThemeId: guid?, activeThemeKey: string?
The default theme for every Page under this Site, unless a Page overrides it.
Page
activeThemeId: guid?, activeThemeKey: string?
When non-null, replaces the Site's theme entirely for that Page — a full swap to a different theme row, not a merge/cascade of individual tokens on top of the Site's theme.
There is no tier below Page — an LEC is itself a Page (same as before), but it has
no special theming behavior beyond the ordinary Page-level activeThemeId
override described above. There is no scopeType/scopeKey
concept; Site and Page are identified by their own native keys
(siteKey, pageKey).
Container / Item token references
Container and Item content records do not carry a merged override dictionary.
Instead, each carries its own set of individual *Token fields that
reference a token path by name — the value is resolved from whichever theme
is active for that Page at render time:
Field (on Item; Container has a subset)
Type
Description
backgroundToken, cornerRadiusToken, paddingToken
string?
Also present on ContainerDto. Token-path reference for background/corner-radius/padding.
Item-only, Admin-write-side. Ad hoc JSON object of token overrides scoped to this item (e.g. {"Surface/Brand":"#ff0000"}), stored as-is and applied by the mobile client on top of the active theme.
This is the real analog of what earlier revisions of this doc modeled as a single
structured themeTokenOverrides dictionary merged at resolution time. The
actual shape is the reverse of that description: most overrides are individual,
per-field, direct token-path references (not a merged bag), and only Items get one
additional free-form JSON override field (tokenOverrides) for ad hoc
cases — Containers don't have an equivalent free-form field.
Worked example: UAL hierarchy
Two independent theme rows, a Site assignment, and a Page-level full-swap
override:
Theme
id
key
workflowState
updatedAt
description
ALPA base
7c9e6679-…
alpa-default
Published
2026-06-26T00:00:00Z
Default ALPA palette
UAL default
3fa85f64-…
ual-default
Published
2026-06-26T00:00:00Z
United Airlines default theme
UAL HomePage promo
91a2c3d4-…
ual-homepage-promo
Draft
2026-08-14T15:42:00Z
Seasonal promo palette, still being tuned — not published
Entity
siteKey / pageKey
activeThemeId
Resolved theme
Site
ual
3fa85f64-… (UAL default)
UAL default — applies to every Page in this Site by default
Page
ual-home
null
Falls back to the Site's theme (UAL default) — no override set
Page (once published)
ual-home, after an Admin publishes the promo theme
91a2c3d4-… (UAL HomePage promo)
UAL HomePage promo — replaces the Site's theme entirely for this one Page; every other Page in the ual Site is unaffected
There is no walk/merge step: resolving a Page's effective theme is "does this Page
have a non-null activeThemeId? If yes, fetch that theme. If no, fetch the
Site's activeThemeId instead." — a single conditional, not a chain.
A Container and Item under this Page layer their own token references on top of
whichever theme resolved above (see Container / Item
token references):
Entity
Relevant field
Value
Effect
Container hero-container-3
backgroundToken
null
No override — background follows the container's own rendering default.
Item promo-tile-1
titleToken
"Surface/Brand"
The item's title color resolves against Surface/Brand in whichever theme is active for ual-home at render time.
Item promo-tile-1
tokenOverrides (free-form)
{ "Surface/Brand": "#0057a3" }
Ad hoc override applied on top of the active theme for this one item only — the mobile client applies this after resolving the theme, not the backend.
Canonical token catalog
Tokens — the registry, not the values
Tokens is a separate, simpler structure from everything above — it
doesn't belong to any one theme or scope. It's the flat list of which token keys
are allowed to exist at all, and how they're grouped:
Field
Type
Description
tokenName
string
The canonical key, e.g. "Surface/Brand". Matches exactly what appears as a key in any ThemeDto.tokens dictionary, or as the value of any Container/Item *Token field or tokenOverrides entry.
category
string
Grouping label, e.g. "Surface" — matches the namespace prefix before the /. Used for UI grouping (the per-category tables below), not parsed out of tokenName at runtime. The real registry has 15 such categories — see the warning below.
Tokens (the registry) answers "what tokens exist";
ThemeDto.tokens (the field, on any individual theme) answers
"what value does this theme give every one of them."
Because there is no inheritance between themes, every ThemeDto must
provide a value for every Tokens registry entry — there's no partial or
sparse theme. Tokens itself carries no values — it's schema, not data —
so it changes far less often than any individual theme does, and is enforced
server-side via the Admin API's POST /theme/validate-tokens endpoint
(admin-portal.html).
Relationship to design-tokens.html#theme-tokens. A
richer, already-shipped per-token reference exists at
design-tokens.html
— Theme Endpoint Token Namespace Reference — descriptions, paired
On-* tokens, CSS variable names, and per-namespace grouping tables for
every token below. Tokens as defined here is the minimal subset of
that same information (name + category only) needed to drive resolution and
validation; it is not a competing source of truth. The
token registry
extensibility point (type/lockable/deprecated
per token) is best read as formalizing metadata design-tokens.html
already documents in prose today, not as a new idea invented from nothing.
Token count is unsettled across this doc set — 2026-08-29. This
section previously counted 45 tokens (a pre-shipped test-data baseline), then a
later revision cited a corrected count of 107 based on one live FDX payload,
describing new namespaces (FontSize/*, FontWeight/*,
TextTransform/*, LetterSpacing/*) and additional keys
(Surface/Secondary, Text/Eyebrow). The
authoritative source is the Admin Portal guide's token registry
(admin-portal.html
— Token Endpoints, updated 2026-08-26, the most recently updated of Vlad
Oprica's docs): 95 tokens across 15 categories — Surface, Text,
Border, Icon, Action, Navigation, Status, Typography, Font Size, Font Weight, Text
Transform, Letter Spacing, Spacing, Border Radius, and Icon Size. (The Ingestion
API guide separately says "91 registry tokens" — a minor inconsistency in Vlad's
own docs this doc does not attempt to resolve.) None of Vlad's docs enumerate the
full 95-token list by name — the tables below cover only the subset already known
from earlier examples in this doc set (the original ~45 tokens across the 10
categories below); the five categories the real registry adds beyond these ten
(the finer-grained Typography/Font Size/Font Weight/Text Transform/Letter Spacing
split, plus Icon Size) are not enumerated here — this is the same open item tracked
as OT-8
in the Theme Endpoint contract.
The token subset already known from earlier examples in this doc set, grouped by
category, comparing the ALPA base value against the UAL theme's value for each. This
is illustrative, not the full 95-token registry (see the warning above).
Surface
Token
Default
UAL theme
Differs from ALPA base?
Surface/Brand
#05273e
#002243
Yes
Surface/Default
#ffffff
#ffffff
No
Surface/Primary
#efefef
#f7f7f7
Yes
Surface/Subtle
#dfedf9
#dfedf9
No
Text
Token
Default
UAL theme
Differs from ALPA base?
Text/Primary
#05273e
#05273e
No
Text/Secondary
#5d6471
#5d6471
No
Text/On-Brand
#ffffff
#bed6fb
Yes
Text/On-Subtle
#05273e
#05273e
No
Text/Link
#007bc2
#94ebfe
Yes
Text/Disabled
#d2d4d6
#d2d4d6
No
Border
Token
Default
UAL theme
Differs from ALPA base?
Border/Brand
#05273e
#0008ce
Yes
Border/Default
#d2d4d6
#d2d4d6
No
Border/Subtle
#e3e3e3
#e3e3e3
No
Icon
Token
Default
UAL theme
Differs from ALPA base?
Icon/Default
#05273e
#05273e
No
Icon/On-Brand
#ffffff
#ffffff
No
Icon/Active
#007bc2
#94ebfe
Yes
Action
Token
Default
UAL theme
Differs from ALPA base?
Action/Primary
#007bc2
#0008ce
Yes
Action/PrimaryText
#ffffff
#ffffff
No
Action/Accent
#007bc2
#94ebfe
Yes
Nav
Token
Default
UAL theme
Differs from ALPA base?
Nav/Background
#05273e
#002243
Yes
Nav/Active
#ffffff
#ffffff
No
Nav/Inactive
#9ba1a8
#9ba1a8
No
Nav/Hover
rgba(255,255,255,0.12)
rgba(255,255,255,0.12)
No
Status
Token
Default
UAL theme
Differs from ALPA base?
Status/Error
#c02126
#c02126
No
Status/Success
#1a7340
#1a7340
No
Status/Warning
#b7770d
#b7770d
No
Status/Info
#007bc2
#007bc2
No
Status/OnTime
#00ea75
#00ea75
No
Font
Token
Default
UAL theme
Differs from ALPA base?
Font/Heading
FuturaPT
LeagueSpartan
Yes
Font/Body
FuturaPT
LeagueSpartan
Yes
Spacing
Token
Default
UAL theme
Differs from ALPA base?
Spacing/xtiny
4px
4px
No
Spacing/tiny
8px
8px
No
Spacing/xxsmall
12px
12px
No
Spacing/xsmall
16px
16px
No
Spacing/small
20px
20px
No
Spacing/medium
24px
24px
No
Spacing/large
28px
28px
No
Spacing/xxlarge
40px
40px
No
Spacing/page-margins
16px
16px
No
BorderRadius
Token
Default
UAL theme
Differs from ALPA base?
BorderRadius/None
0px
0px
No
BorderRadius/S
4px
4px
No
BorderRadius/M
8px
8px
No
BorderRadius/L
12px
12px
No
BorderRadius/XL
16px
16px
No
BorderRadius/Full
50%
50%
No
See also
Theme Token Inheritance Model — the narrative account of the cascading model that was proposed here and not built, and what shipped instead.
Theme Model — Future Extensibility — extension points for the (never-built) cascade proposal; largely superseded by the same reconciliation.