Get started

DTCG typography tokens: model composites and verify the CSS result

Use the final DTCG 2025.10 reports as the exchange-format authority. Model reusable foundations as atomic tokens, then assemble semantic text roles as composites when consumers support them. Treat CSS emission, font delivery, project mapping, and browser rendering as separate implementation stages.

Updated October 24, 2026

Use the final report, not the later preview

As of October 24, 2026, the final DTCG 2025.10 publication governs the exchange format for this workflow. It describes the specification as stable and intended for implementation. Because it is a Community Group report rather than a W3C Standard, record the authority precisely as DTCG 2025.10 final.

The detailed drafts-path page uses the same Format Module version name, but it's a later preview dated September 8, 2026. Its warning says not to implement it or cite it as authoritative. The page can explain proposed changes, but it can't silently replace the final report in an implementation contract.

What it governsWhat it cannot prove
Final DTCG 2025.10 reportsThe portable format and resolver contract for the pinned releaseThat a particular transformer supports every construct or that an application renders the intended result
Later DTCG previewProposed changes and discussion contextStable requirements for a production implementation
Tokens Studio documentationCurrent fields and behavior within Tokens StudioUniversal behavior across DTCG tools or output from a transformer that was not run
Project transform contractHow one named pipeline resolves, normalizes, names, and emits target artifactsFont availability, component adoption, or browser rendering
Browser observationWhat rendered under the recorded browser, content, and loading conditionsPortable syntax conformance or behavior in untested environments
Authority map as observed October 24, 2026

Pin the authority and the implementation

Record the final DTCG release for portable syntax and the transformer and version for emitted output. Calling the whole chain "DTCG compatible" hides which claim has actually been checked.

Decide what the token should govern

A typography token can carry reusable values and relationships. A composite groups a font family, size, weight, letter spacing, and line height into a semantic role such as body text. Aliases let several roles reuse one foundation without copying its literal value.

That contract stops before font delivery and component adoption. The token doesn't download a font, create an @font-face rule, establish that a requested face exists, choose fallback behavior for every script, or apply itself to a component. Those decisions belong to the web transform, font resources, project mapping, and component implementation.

  • Authored source: token names, types, values, references, and composite structure.
  • Resolved value: concrete DTCG values after the resolver has followed valid references.
  • Transformed output: target names, unit serialization, composite expansion, and project-added fallbacks.
  • Font resources: family names, static faces or variable ranges, styles, character coverage, and delivery locations.
  • Project mapping: the variables, utilities, classes, or components that consume the emitted artifact.
  • Rendered observation: computed values, selected fonts, wrapping, and fallback behavior under recorded conditions.

Choose atomic, composite, or mixed representation

Start with the needs of named consumers. "The web" isn't a useful consumer description. Marketing-site CSS build, React Button label, and documentation preview are specific enough to inspect. For each one, determine whether its toolchain accepts composites, needs resolved atomic values, or requires a local mapping.

Atomic tokensComposite tokensMixed model
Best fitConsumers request individual properties or cannot expand typography compositesA complete text role should change and travel togetherFoundations are shared while semantic roles remain explicit
Reuse behaviorFamilies, sizes, weights, spacing, and line heights can be reused independentlyReuse centers on the complete roleAliases reuse atomic foundations inside role-level composites
Main riskConsumers can assemble combinations that were never intendedA composite can conceal whether required font resources existMore resolution and transformation steps must be documented
Transformer requirementMap each atomic type to a target propertyExpand every composite property correctlyResolve aliases, validate types, then expand each semantic role
Use whenSeveral platforms or utilities need individual valuesAll named consumers support the selected composite contractConsumers differ or both portability and coherent roles matter
Typography representation decision matrix

A mixed model is a useful starting point, not a standard requirement. Before choosing it, inventory the transformer's capabilities, the font resources each role requires, the fallback owner, and at least one consumer where you can inspect the entire route.

  1. List the roles the product currently uses, such as body, label, heading, and code.
  2. Mark which properties must change together and which are reused independently.
  3. Check composite and alias support in the pinned transformer version.
  4. Inventory the static faces or variable ranges and styles required by those roles.
  5. Assign fallback policy to the portable source or a named target transform.
  6. Select one representative consumer and write its expected result before running the pipeline.

Build an illustrative mixed DTCG model

The fixture below is original and illustrative. Atlas Sans is fictional, and no transformer was run for this article. The source combines atomic foundations with one composite body role. Before using this pattern in production, check the final DTCG 2025.10 Format and Resolver reports and the contract of your chosen tool.

{
  "font": {
    "family": {
      "sans": {
        "$type": "fontFamily",
        "$value": "Atlas Sans"
      }
    },
    "size": {
      "body": {
        "$type": "dimension",
        "$value": { "value": 16, "unit": "px" }
      }
    },
    "weight": {
      "regular": {
        "$type": "fontWeight",
        "$value": 400
      }
    },
    "lineHeight": {
      "body": {
        "$type": "number",
        "$value": 1.5
      }
    },
    "letterSpacing": {
      "body": {
        "$type": "dimension",
        "$value": { "value": 0, "unit": "px" }
      }
    }
  },
  "text": {
    "body": {
      "$type": "typography",
      "$description": "Default product body text",
      "$value": {
        "fontFamily": "{font.family.sans}",
        "fontSize": "{font.size.body}",
        "fontWeight": "{font.weight.regular}",
        "letterSpacing": "{font.letterSpacing.body}",
        "lineHeight": "{font.lineHeight.body}"
      }
    }
  }
}
Illustrative DTCG source with atomic foundations and one semantic composite

Expected resolved DTCG value

After reference resolution, the expected composite still contains DTCG dimension objects. This is an expectation derived from the fixture, not output captured from a resolver.

{
  "fontFamily": "Atlas Sans",
  "fontSize": { "value": 16, "unit": "px" },
  "fontWeight": 400,
  "letterSpacing": { "value": 0, "unit": "px" },
  "lineHeight": 1.5
}
Expected resolved DTCG value, not an executed result

Keep this form intact. Converting the two dimension objects to 16px and 0px is a target transformation, not reference resolution. If resolution fails because of a missing target, cycle, or type conflict, fix that problem before investigating CSS serialization.

Illustrative CSS emission

A web transform could serialize those dimensions, expand the composite, and add a project-owned fallback stack. The CSS below represents that target contract. It isn't output from an executed tool run.

:root {
  --type-body-font-family: "Atlas Sans", system-ui, sans-serif;
  --type-body-font-size: 16px;
  --type-body-font-weight: 400;
  --type-body-letter-spacing: 0px;
  --type-body-line-height: 1.5;
}

.text-body {
  font-family: var(--type-body-font-family);
  font-size: var(--type-body-font-size);
  font-weight: var(--type-body-font-weight);
  letter-spacing: var(--type-body-letter-spacing);
  line-height: var(--type-body-line-height);
}
Illustrative post-transform CSS, not output from an executed transformer

The fallback families are a project decision added during transformation. If the fictional primary family is unavailable, the CSS remains valid while the browser selects system-ui or another fallback.

Inspect a complete typography source before defining the transform

Ambient Sage exposes public typography roles alongside its broader design system. Inspect the real upstream artifact, then document how your transformer and named consumer will handle that input.

Keep font resources beside, not inside, the token claim

Typography delivery has two inputs that must agree: the role's declared values and font resources capable of fulfilling them. A role requesting weight 600 doesn't establish that a 600 static face is delivered or that a variable font covers that coordinate. Likewise, an available file doesn't establish that a component requests it.

  • Family identity: deliberately map the token family to the CSS family used by @font-face.
  • Weight availability: record each required static face or the relevant variable range.
  • Style availability: identify required normal, italic, or oblique faces and deliberate omissions.
  • Synthesis policy: decide whether synthetic bold or italic is acceptable, then include that condition in testing.
  • Fallback order: choose ordered families for the role, scripts, symbols, and expected failure conditions.
  • Loading policy: define font-display and delivery behavior in the font layer.
  • Character coverage: test the content and scripts used by the named consumer.

The font-display descriptor belongs to an @font-face rule and controls display behavior while a face is loading or unavailable. Keep it out of a semantic typography role unless a separate, explicit project convention says otherwise. The token requests a family. The asset layer offers faces, the loading policy affects their display, and the browser chooses what renders.

A plausible screenshot is weak font evidence

Similar sans-serif families can hide fallback selection. Inspect computed styles and the browser's rendered-font information, then compare wrapping with representative content.

Record Ambient Sage as bounded upstream intake

Ambient Sage v1 provides a real public source record. It declares Plus Jakarta Sans for heading and body roles at weights 400, 500, 600, and 700. JetBrains Mono serves the mono role and technical strings at weights 400, 500, and 700. The published scale is compact-product, and the kit offers a DTCG export.

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 & @ # % →

Public Ambient Sage v1 typography roles

This intake establishes the declared roles, weights, scale, and artifact availability. It doesn't expose protected serialization or establish compatibility with a particular transformer. Font-file delivery, application mapping, accessibility, and rendering remain project checks.

{
  "source": "Ambient Sage v1",
  "sourceStatus": "public upstream intake",
  "declaredRoles": {
    "heading": "Plus Jakarta Sans",
    "body": "Plus Jakarta Sans",
    "mono": "JetBrains Mono"
  },
  "publishedWeights": {
    "headingAndBody": [400, 500, 600, 700],
    "mono": [400, 500, 700]
  },
  "scale": "compact-product",
  "serializationInspected": false,
  "transformCompatibility": "unresolved",
  "fontResourcesVerified": false,
  "namedConsumerObserved": false
}
Bounded intake record with downstream fields left open

Verify one role from source to rendered text

Start with one role in one named consumer. Write the expectation first, then inspect the chain in order. Stop at the first divergence because later differences may follow from that earlier fault.

  1. 1

    1. Validate the pinned source

    Record the final DTCG report version, source revision, token path, expected type, and expected value. Fail this stage for invalid source structure, an unexpected type, or a validator configured for another contract.

  2. 2

    2. Inspect reference resolution

    Compare the resolver result with the expected DTCG object. Fail this stage for a missing target, cycle, incompatible reference, stale value, or premature target serialization.

  3. 3

    3. Inspect transform normalization

    Record the transformer and version, target platform, name mapping, unit serialization, composite expansion, and fallback policy. Fail when the documented transform and actual configuration disagree.

  4. 4

    4. Inspect the emitted artifact

    Open the CSS or target file distributed to the project. Confirm that the expected names and serialized values exist there, not only in an intermediate build location.

  5. 5

    5. Verify font resources

    Inspect the family mapping, requested weights or variable range, required styles, character coverage, delivery route, and loading policy. Fail when the requested face can't be supplied under the test condition.

  6. 6

    6. Verify project mapping

    Trace the emitted property into the named class, utility, theme entry, or component contract. Confirm that the consumer imports the intended artifact version.

  7. 7

    7. Check component overrides

    Inspect local declarations, inheritance, responsive rules, states, and approved exceptions. Record any deliberate exception. Treat an unexplained override as a failure at this layer.

  8. 8

    8. Observe the rendered result

    Record the browser, viewport, loading condition, and test content. Inspect computed styles and the selected font, then compare the result with the written expectation.

Likely ownerStart byDo not start by
Invalid source or wrong typeToken source ownerCompare the source with the pinned DTCG contractEditing component CSS
Reference does not resolveSource or resolver ownerInspect the target path, reference graph, and resolver resultChanging font files
Resolved value is right, emitted CSS is wrongTransformer or configuration ownerInspect naming, units, normalization, and composite expansionRenaming the source token without evidence
CSS is right, requested face is unavailableFont asset or delivery ownerInspect @font-face, files, ranges, styles, coverage, and deliveryRewriting valid token values
Artifact is right, project maps another valueProject integration ownerTrace imports, mappings, utilities, and artifact versionsChanging the DTCG composite
Project mapping is right, component overrides itComponent ownerInspect local declarations, states, inheritance, and exceptionsBlaming the transformer
Computed values match, rendered face differsFont, browser, content, or test ownerInspect selected fonts, synthesis, glyph fallback, and test conditionsClaiming source conformance proves rendering
Route the first divergence to its owning layer

Typography tokens do not certify accessibility

Text spacing, zoom, reflow, contrast, language coverage, and assistive-technology behavior require checks scoped to the actual product and content.

Copy a layer-correct implementation record

Keep the resolved DTCG value and emitted CSS in separate fields. Fill in expectations before execution. Add observations only after collecting evidence; not-run is a valid status.

{
  "governingContract": "DTCG 2025.10 final",
  "sourceVersion": "commit-or-release-id",
  "sourceToken": "text.body",
  "expectedResolvedValue": {
    "fontFamily": "Atlas Sans",
    "fontSize": { "value": 16, "unit": "px" },
    "fontWeight": 400,
    "letterSpacing": { "value": 0, "unit": "px" },
    "lineHeight": 1.5
  },
  "transformer": {
    "name": "record-the-actual-tool",
    "version": "record-the-actual-version",
    "target": "web-css"
  },
  "expectedEmittedCss": {
    "font-family": "\"Atlas Sans\", system-ui, sans-serif",
    "font-size": "16px",
    "font-weight": "400",
    "letter-spacing": "0px",
    "line-height": "1.5"
  },
  "namedConsumer": "Account settings body copy",
  "testContent": "Profile details 0123456789 Äé中",
  "inspectionCriteria": [
    "Expected CSS variables reach the component",
    "Computed values match expectedEmittedCss",
    "The intended face supplies the inspected glyphs",
    "No unrecorded component override changes the role"
  ],
  "failureConditions": [
    "A reference is unresolved or resolves to another value",
    "The emitted CSS differs from the recorded transform contract",
    "The requested face is unavailable or not selected",
    "A stale mapping or local override replaces the role"
  ],
  "observationStatus": "not-run",
  "observedResult": null,
  "firstDivergence": null,
  "correctionOwner": null,
  "nextTest": "Run the pinned transform, inspect the distributed CSS, then check computed values and the rendered font in the named consumer"
}
Source-to-render record with resolved and emitted values kept separate

Run this record first for one body role. If it passes, add a heading at a different weight and a mono role with representative technical content. The next test will then cover distinct faces and use cases instead of repeating the same path under more token names.

Sources