Get started

Design tokens for density modes: what should scale and what should stay fixed

Treat density as an independent mode for selected semantic decisions, not as a multiplier for the whole interface. It can change the space needed to scan and operate repeated UI, while fixed constraints, application behavior, locale adaptations, and viewport rules keep their own owners.

Updated October 30, 2026

Density is a mode, not a scale factor

A density mode changes how much information and interaction fit into a working area, so compact and comfortable modes may resolve the same semantic role to different values. Atlassian describes compact and comfortable views as possible non-color themes. The SAS implementation shows why the practical work reaches several property families rather than a single spacing number.

The tempting shortcut is a global multiplier: take every numeric value and apply 0.8 for compact mode. It's easy to describe and hard to defend. Reducing a row gap may improve scanning in a data table, but reducing a focus outline may weaken a critical state. A smaller font can change readability and wrapping. A smaller hit area can affect operation. The values are all numeric, but they do different jobs.

Do not make density a universal transform

A global multiplier hides ownership and couples decisions that need different constraints, evidence, and release criteria. Route each property separately.

Separate the six authorities

Before naming density tokens, write down which layer controls each kind of decision. Otherwise, a source token, component override, media query, and product preference can compete without anyone noticing.

  1. Source values define reusable primitives and approved semantic roles.
  2. Density aliases select values for compact or comfortable presentation.
  3. Component contracts map those roles to named parts, variants, and states.
  4. Application policy selects a mode, stores any preference, and decides where the mode applies.
  5. Controlled exceptions document deliberate departures for a named component or surface.
  6. Consumer evidence records what a particular application resolved, emitted, mapped, and rendered.

These layers are related, but none proves the next. A valid density alias doesn't prove that a build emitted it. An emitted variable doesn't prove that a component uses it. Even a correct component mapping doesn't prove that the product selected the intended mode or rendered it correctly.

Use a density-ownership matrix

For each candidate property, ask four questions: who owns it, what variation is allowed, what must remain fixed, and what evidence is required? The matrix below is a starting contract, not a universal set of values.

Governing layerPermitted density variationFixed constraint or separate ownerRequired evidence
Internal spacingSemantic density role, then component mappingPadding and gaps inside repeated controls or cellsContent order and required affordancesResolved values plus representative component renders
Control dimensionsComponent contract backed by density rolesHeight and internal padding where the control remains operableSupported variants, states, and any minimum requirement set by the teamDefault, focus, disabled, error, and content-extreme checks
Table and list rowsSemantic row role with component mappingRow height, cell padding, and repeated vertical gapsData hierarchy, selection behavior, and status meaningScanning task in one named table or list consumer
Layout gapsLayout contract or semantic density roleLocal gaps where density is the controlling conditionViewport transitions and container behaviorChecks on both sides of every relevant layout boundary
TypographyTypography role plus explicit density decisionLine height or role mapping only when approved for that roleScript coverage, locale needs, font availability, and product readability constraintsWrapping, clipping, fallback, zoom, and locale observations
IconsComponent or icon contractSize only when the icon's role supports itMeaning, stroke legibility, alignment, and accessible name policyRepresentative icon controls at each density
Hit areasComponent and application policyVisible geometry may change independently from the operable areaThe team's accessibility and device-input requirementsPointer and keyboard operation in named consumers
Focus treatmentSemantic focus roles plus component and selector policyOffset only if the complete focus contract remains validVisibility, contrast policy, forced-colors behavior, and state meaningKeyboard checks across controls, themes, and density modes
Radius and bordersStable semantic or component roles by defaultChange only when density has an explicit visual requirementBrand shape language and state communicationControlled comparison showing the intended affected surfaces
BehaviorApplication and component policyUsually noneSorting, validation, disclosure, navigation, permissions, and side effectsFunctional tests separate from visual density checks
Route density decisions property by property. Set exact values and constraints for the product and its users.

Start narrow. Let density affect repeated spatial relationships and selected component dimensions. Require a specific reason before changing typography, icon geometry, focus treatment, or operable areas. Keep radius and behavior fixed unless the product has a named requirement that says otherwise.

Preview unavailable here. Browse complete kits in the kit gallery.

Compose density with other axes

Theme, density, locale, and viewport behavior are independent axes. Light versus dark changes color relationships, while compact versus comfortable changes selected spatial decisions. Locale may change text, script, direction, font support, or wrapping. Viewport and container conditions may change layout. Combining all of them into one mode produces an expanding list of combinations and makes conflicts harder to locate.

Primary questionTypical ownerFallback
ThemeWhich visual values fit the active color scheme?Theme policy and semantic color mappingsDeclared default theme
DensityHow much space should selected repeated UI consume?Application preference plus density aliasesComfortable unless the product declares another default
LocaleWhat language, script, direction, and content conditions apply?Localization and typography policyDeclared product locale and supported font path
Viewport or containerHow should structure adapt to available space?Layout and component CSSBase layout outside defined conditions
Component exceptionWhy may one named consumer depart from the shared mapping?Versioned component or product exceptionShared contract when the exception is absent or expired
Keep each axis responsible for one kind of condition.

Set precedence before implementation

Precedence should describe responsibility, not merely file order. One practical rule is to apply fixed constraints first, followed by active axis mappings, component contracts, and approved exceptions. Application policy selects the active axes. Viewport rules may change layout, but they shouldn't silently select density unless that coupling is an explicit product requirement.

  1. Apply non-negotiable product and accessibility constraints.
  2. Resolve theme, density, and locale independently from their declared defaults.
  3. Apply the component's supported mapping for the resolved roles.
  4. Apply a versioned exception only to its named target and conditions.
  5. Record unsupported or unresolved combinations instead of inventing a fallback in the consumer.

Responsive does not mean compact

A narrow viewport may need larger, easier-to-operate controls, while a wide data workstation may benefit from compact rows. Couple viewport and density only when a named requirement supports the relationship.

Write an explicit multi-axis contract

The following contract is illustrative and unexecuted. It isn't an Identity Forge export, a DTCG conformance example, or evidence that these values work for a product. It shows where decisions and unresolved fields belong.

contract: density-v0-proposal
status: illustrative-unexecuted
axes:
  theme:
    supported: [light, dark]
    default: light
  density:
    supported: [comfortable, compact]
    default: comfortable
  locale:
    supported: [en, de]
    default: en
  viewport:
    owner: application-layout
    selectsDensity: false
roles:
  space.control.inline:
    comfortable: 12px
    compact: 8px
  space.control.block:
    comfortable: 10px
    compact: 6px
  size.row.standard:
    comfortable: 44px
    compact: 36px
fixed:
  typography.body.family: source-typography-role
  focus.indicator.contract: shared-focus-contract
  behavior.table.sort: application-policy
exceptions:
  - id: orders-table-touch-target
    target: OperationsConsole.OrdersTable.SelectionControl
    reason: unresolved
    owner: unassigned
    expires: unresolved
unsupported:
  - compact typography remapping by locale
  - viewport-selected density
fallback:
  unknownDensity: comfortable
  missingDensityRole: fail-review-no-local-guess
Illustrative contract only. Values, constraints, compatibility, and rendered behavior remain unverified.

The sample pixel values aren't the point. What matters is the separation among roles, fixed decisions, exceptions, unsupported combinations, and fallback. A consumer that encounters an unknown density can use the declared default. A missing required role should remain visible as an integration problem instead of being replaced by a local guess.

Use bounded fallback rules

  • Unknown density selection: use the declared default and record the invalid input.
  • Missing optional density mapping: inherit the documented base role only if the contract permits it.
  • Missing required component role: block that consumer's migration or keep it on the legacy mapping.
  • Unsupported axis combination: keep the last supported combination or disable the new mode for that scope, according to application policy.
  • Expired exception: don't silently renew it; review whether the shared contract now covers the need.

Inspect a real upstream token system

Ambient Sage provides a published typography, spacing, color, and export baseline. Use it as source intake, then define and verify density behavior in the consuming application.

Control component exceptions

A shared density role won't fit every component, but that doesn't justify hidden local values. Record an exception when a named component needs independent change authority, such as a touch-oriented selection control inside an otherwise compact table.

Every exception should name its target, reason, owner, permitted modes, protected surfaces, review date or removal condition, and verification cases. It should also state whether it replaces a semantic role, adds a component alias, or changes only application behavior. If those fields are unknown, the exception remains a proposal.

Protect unrelated consumers

For every exception, name at least one surface that must not change. This turns a vague override into a bounded contract you can test.

Migrate without pretending delivery is adoption

Move one high-use component at a time. A compatibility alias can keep unmigrated consumers working, but it needs a removal gate. The goal isn't to make the old name disappear quickly. It's to prove that named consumers no longer depend on it.

  1. 1

    Freeze the current behavior

    Before changing token names, record current values, component mappings, supported states, named consumers, local overrides, and known exceptions.

  2. 2

    Add semantic density roles

    Create comfortable and compact mappings for the approved property set. Keep primitives stable unless a separate source decision changes them.

  3. 3

    Preserve a compatibility route

    Keep the legacy token or value mapped to the comfortable role when that matches existing behavior. Mark the route deprecated, not removed.

  4. 4

    Migrate one named consumer

    Update one representative table, form, or navigation surface. Record the exact component and project mapping instead of saying the application is migrated.

  5. 5

    Verify both modes and adjacent axes

    Check representative states in comfortable and compact modes, then repeat the relevant cases in light and dark themes, supported locales, and viewport conditions.

  6. 6

    Set rollback triggers

    Roll back the consumer mapping if required states fail, content becomes unusable, the wrong artifact resolves, or an unapproved exception is needed.

  7. 7

    Remove compatibility only after evidence closes

    Remove the old route when dependency search, generated artifacts, project mappings, and named-consumer observations show that no supported consumer needs it.

Use Ambient Sage as bounded upstream intake

Ambient Sage is a published Identity Forge kit with a compact-product typography scale. Its public typography uses Plus Jakarta Sans for body and heading roles at weights 400, 500, 600, and 700. JetBrains Mono covers the mono role at weights 400, 500, and 700. Identity Forge makes design-kit artifacts available through formats that include DESIGN.md, DTCG, Tailwind, CSS, and shadcn routes.

This evidence establishes the available upstream decisions and artifacts. It doesn't establish comfortable and compact variants, application mode selection, component mappings, locale behavior, accessibility results, or rendered observations. Those fields stay unresolved until a consuming team defines and checks them.

Token specimen · real values

Ambient Sage

Live render

Ambient Sage's actual tokens — the same values its exports use.

Type scaleHeading, body, and mono in the kit's fonts

Typography

Ambient Sage

Scale: compact-product

Density: balanced

Heading · Plus Jakarta Sans · 1.875rem

Ship beautiful product faster

Subheading · Plus Jakarta Sans · 1.375rem

A warm-sage neutral-surface mobile kit with a single vivid yellow accent, flat tonal cards, and oversized display numerals.

Body · Plus Jakarta Sans · 1rem

Ambient Sage uses a near-white warm-sage canvas (#f3f4ef) with card panels distinguished only by a tonal shift to #e5e6e0, never by shadows or borders. A single vivid yellow (#fee951) is the only saturated color and appears sparingly at component scale as orbs, button fills, and focus rings. Primary data values render as oversized bold hero numerals with a small superscript unit. Typography is a friendly rounded geometric (Plus Jakarta Sans) with no uppercase and no tight tracking, while JetBrains Mono is reserved for hex codes and technical strings. Generous rounding and luminance-only contrast give the whole system a calm, minimal feel.

Mono · JetBrains Mono · 0.8125rem

npx shadcn add ambientsage.json

Aa

Plus Jakarta Sans · Heading

400500600700

Aa

Plus Jakarta Sans · Body

400500600700

ABCDEFGHIJKLM NOPQRSTUVWXYZ

abcdefghijklmnopqrstuvwxyz

0123456789 & @ # % →

Radius & spacingCorner radius, elevation, and spacing steps

Tokens

Ambient Sage primitives
density: balanced

Radius scale

sm · 0.375rem
md · 0.75rem
lg · 1.25rem
xl · 1.75rem

Component radius

button
card
input

Elevation

level 1
level 2
level 3
level 4

Spacing · base 4px

1x
2x
3x
4x
6x
8x
Ambient Sage shows published upstream type and spacing decisions. It doesn't claim to show a generated density mode.

Verify one named consumer

Use a record that keeps expectations and observations together without confusing them. The example below names the Operations Console Orders Table, but it remains unexecuted. Replace every placeholder with evidence from the actual consumer.

record: density-acceptance-001
status: unexecuted
consumer:
  product: Operations Console
  surface: Orders Table
  component: DataTable
source:
  contractVersion: unresolved
  densitySelection: [comfortable, compact]
  sourceValues: unresolved
resolution:
  resolvedAliases: unresolved
  emittedArtifact: unresolved
  artifactVersionOrHash: unresolved
projectMapping:
  packageOrFile: unresolved
  semanticToComponentMap: unresolved
exceptions:
  approved: []
  proposed: []
representativeCases:
  - table/default/light/en/wide
  - table/selected/dark/en/wide
  - table/loading/light/en/narrow
  - table/error/dark/de/wide
  - table/long-content/light/de/narrow
  - selection-control/focus/light/en/wide
expectations:
  comfortable: unresolved
  compact: unresolved
  fixedConstraints: unresolved
observations:
  comfortable: not-run
  compact: not-run
  themeInteraction: not-run
  localeInteraction: not-run
  viewportInteraction: not-run
owner: unassigned
rollbackTrigger: unresolved
disposition: pending-evidence
Copyable named-consumer record. Write expectations before observations, and leave unrun checks marked as such.

A useful record captures the source version, active density, resolved values, emitted artifact, project mapping, component contract, exceptions, representative cases, expectations, observations, owner, rollback trigger, and disposition. It should cover the tables, forms, navigation, content extremes, component states, themes, locales, and viewport conditions that matter to the named consumer.

Don't promote a source value into an observed result. If the compact row token resolves to 36px in the source, the record can state that source fact. It can't claim that the Orders Table rendered at 36px until someone has inspected the emitted artifact, project mapping, computed result, and rendered consumer.

Find the first divergent layer

When the result is wrong, compare expected and observed evidence in ownership order. Fix the first layer that diverges. A later override may explain the screenshot, but changing it can conceal an earlier source or transformation defect.

  1. 1

    Check the governing source

    Does the approved contract contain the expected role, mode value, fixed constraint, and version?

  2. 2

    Check alias resolution

    Does the active density resolve the semantic role to the intended value without a missing target, cycle, or unintended fallback?

  3. 3

    Check transformation and emission

    Did the pipeline preserve the role and value in the artifact consumed by this project?

  4. 4

    Check the project mapping

    Is the project loading the intended artifact version and mapping the semantic role to the expected component property?

  5. 5

    Check the component contract

    Does the component support the relevant mode, variant, state, and content condition?

  6. 6

    Check approved exceptions

    Is a bounded, current exception intentionally changing this consumer?

  7. 7

    Check the rendered consumer

    Under the recorded theme, locale, viewport, content, and input conditions, does the computed and visible result match the expectation?

Accessibility needs its own evidence

This workflow can expose density-sensitive risks and preserve constraints, but it doesn't certify accessibility. Test the shared component and representative product consumers under the conditions required by your accessibility scope.

Start with one high-use component

Choose one component where density has a clear user benefit, such as a frequently used table. Route each property through the ownership matrix and write the comfortable and compact expectations. Declare the other axes and fallback, then trace both modes from source to the rendered consumer. Expand the system only after that record shows which decisions transferred cleanly and which need a different owner.

Sources

  • Refactoring Design System Tokens for a Multidimensional Future: The SAS case study documents density work across spacing, dimensions, typography, icons, responsiveness, accessibility, theme, and language, including a concrete file order and a locale-related typography conflict.
  • Design tokens explained: Atlassian defines tokens as named design decisions, recommends names based on intended use, and identifies compact, comfortable, and cozy views as possible non-color themes.
  • Introduction | Backbase Design System: Backbase documents primitive, semantic, and component token tiers and presents modes as a multi-dimensional theming mechanism.
  • Design tokens | Cloudscape Design System: Cloudscape presents design tokens as abstractions of visual properties and documents structured naming and usage guidance for downstream consumers.
  • Design Token Architecture | Joelle Gregory: The HPE case study records density and viewport as interacting dimensions within a broader problem of token-library structure, access, publication, and adoption.
  • Ambient Sage Design Kit: The published Ambient Sage kit uses Plus Jakarta Sans for body and heading roles, JetBrains Mono for mono roles, and a compact-product typography scale; it is an upstream kit example rather than proof of density-mode implementation.
  • Design token handoff checklist: prove the path from source to consumer: Identity Forge's handoff guide separates governing source, delivered artifact, project mapping, named consumers, and observed results instead of treating delivery as proof of adoption.