OverviewGetting startedInstallationComponentsFoundationsToken architectureColorTypographySpacingBorders and radiiElevationMotionThemesResponsive behaviorCore and stateTheming and layoutAgent toolingAI toolingChange impactRecipesArchitectureRoadmapHelp and releasesOverviewToken architectureColorTypographySpacingBorders and radiiElevationMotionThemesResponsive behavior

Same roles, paired appearances

Each mode-dependent role is one expression switched by two inherited toggles, so mode, product, and density scopes nest in any order. The page follows prefers-color-scheme unless data-odd-mode is set; data-odd-mode="system" restores the system behaviour inside an explicit subtree.

Light
  • color.canvas
  • color.surface.default
  • color.text.primary
  • color.action.default
Dark
  • color.canvas
  • color.surface.default
  • color.text.primary
  • color.action.default

Scope contract

Density changes spacing and control sizing only, never font sizes, and never takes a target below the documented minimum. The scopes are plain attributes on any element: data-odd-mode, data-odd-theme, data-odd-product, and data-odd-density, with data-odd-profile for profiles.

Scopes in the default theme
ScopeTokens it setsAliases it re-declares
[data-odd-mode]140
[data-odd-density="compact"]1312
[data-odd-profile~="navigation.compact"]40
[data-odd-profile~="layout.compact"]21

Composition and precedence

The generated CSS has no rule per combination of scope values: each product, density, and profile is one rule, and mode is two toggles. A profile, or a product with its own colour roles, adds one rule that re-applies it inside nested scopes.

Scope axes together
SituationResult
Axes on nested elements (any order, any depth, across shadow roots)The nearest scope of each axis applies; each scope re-declares its alias closure, so outer scopes of other axes keep working (§5, §5.3)
Several axes on one elementRules have equal specificity; emission order decides overlapping roles: products, then densities, then profiles, all after the mode rules. In practice only densities and profiles overlap (both touch layout and component roles), and the profile wins
Two profiles on one element (data-odd-profile="a b") setting the same roleThe profile that comes later in the configuration wins; the attribute's list order does not matter. Reported as profile.overlap with both paths
A profile on an ancestor and a density nested inside itThe profile still applies (its re-application selector)
A mode scope inside a product with its own colour rolesThe product's roles apply (its re-application selector, 0,2,0); a profile's re-application, emitted later, wins where both set a role
A product nested inside a product that overrides rolesNot supported: mode scopes inside the inner product take the outer product's roles
A nested data-odd-themeRe-declares every token: outer products, densities, and profiles stop applying inside it; mode toggles still come from outside
overrides vs. scopesOverrides change the base values every scope starts from; a scope that re-declares a role still wins inside it

Create a theme

Theme Studio, part of @oddkin/workbench, edits a theme configuration, runs the same generator in the browser, previews the components in isolated scopes, and exports the configuration and its CSS. The theming guide shows how a page applies a theme.