Contribute to the Playbook
The documentation on this site is created and edited by a variety of contributors. If you would like to contribute to the documentation, follow the steps below.
That is contributing to the documentation. For how work gets into the design system itself — who designs it, where the styleguide-versus-custom call is made, and who builds what — see How we work together.
-
Learn about using Markdown basic syntax.
-
Create or edit any
.md(or.mdx) files insrc/content/docs/directory. All files within are mapped to a filename based URL structure.Eg. the content at
src/content/docs/start/contribute.mdxis available at/start/contributeas you can see in the current URL.Any documentation nested within directories will be automatically included in the sidebar navigation. For advanced use-cases you can configure
sidebarinastro.config.js. -
Additional reading for more advanced content.
Page frontmatter: the dxp: namespace
Section titled “Page frontmatter: the dxp: namespace”Every page’s frontmatter has two halves, and the split is the thing to remember: top-level keys belong to Starlight, everything under dxp: belongs to this repo.
---title: Accordion # ── Starlight's, top-leveldescription: Collapsible content container…sidebar: order: 2tableOfContents: false
dxp: # ── ours, all of it status: ready tile: ./accordion.svg links: figma: https://www.figma.com/design/… subpage: css: team: platform related: - button - '!icon-button'---If you are adding a key the Playbook invented, it goes under dxp:. If you are setting something documented in the Starlight frontmatter reference, it stays at the top level. That is the whole rule — before it existed, status: ready sat next to tableOfContents: false with nothing to tell an author which of the two this repo was responsible for.
The keys under dxp:, all optional:
| Key | What it does |
|---|---|
status |
Lifecycle badge — ready, quarantine or deprecated. On a CDS component / layout / module / utility page, leaving it unset renders a red Unknown badge; treat that as “needs triage”, not as a default. |
statusReviewed |
YYYY-MM-DD. Only while quarantined: the release-time review looked and decided “not yet”, which resets the two-month expiry clock. |
statusNote |
One line on what a held page is waiting for. Maintainer-facing — the badge report prints it, the page never renders it. |
tile |
Index-card illustration, e.g. ./accordion.svg. Inlined as raw SVG so it re-tints per brand and theme. |
related |
A plain list of match patterns for the Related aside. Substrings are tested against every page’s id and title; prefix ! to exclude, suffix #anchor to deep-link. |
links.figma |
The component’s entry point in the CDS Figma library. Promoted into the hero strip, and stripped from the public production build. |
subpage.<tab> |
Per-tab metadata, authored on the root page rather than the tab file — css.team, react.team, react.storybook, design.figma, design.notes. |
keywords |
Short taxonomy-style descriptors rendered as the subtitle under the page title, joined with “ – “. |
internal |
true hides the page from the public production build entirely — no route, no sidebar entry — while keeping it on the internal previews and in dev. |
Two things that look like exceptions but are not:
_meta.ymlfiles are not namespaced. A folder’stile,label,order,collapsedandtabssit at the top level, because every key in that file is already ours or the auto-sidebar plugin’s — there is no boundary there to mark. A page’sdxp.tileand a folder’stileare the same concept and share one schema definition.- There are no external links in
related. It resolves page links only. A Figma link belongs indxp.links.figma, and a Storybook link indxp.subpage.react.storybook.
The schema is dxpSchema in src/content.config.ts — adding a key there is what makes it available to every page.
Tabbed component pages
Section titled “Tabbed component pages”Components under cds/components/ use a tabbed layout — Design / CSS / React / Accessibility — driven by per-tab MDX files. The leaf page sits next to a <name>-pages/ folder; each MDX inside becomes a tab.
cds/components/├── <name>.mdx # overview paragraph + <SubpageTabs />└── <name>-pages/ ├── _meta.yml # hidden: true, tabs: true ├── hero.astro # optional: the hero demo (not a tab) ├── design.mdx # sidebar.order 1 ├── css.mdx # sidebar.order 2 — optional ├── react.mdx # sidebar.order 3 — optional └── accessibility.mdx # sidebar.order 4Only direct-child .mdx files become tabs, so the .astro files that sit alongside them — recipes, and hero.astro — never appear in the tab bar.
The two implementation tabs are both optional, and neither is required. A component documents CSS, React, both, or neither, and the file’s presence is the entire signal — there is no frontmatter flag. A component with neither is a legitimate state (designed, not built), and the implementation glyphs on the sidebar and the component index read exactly this (PLAT-3636).
What each tab carries:
- Design — anatomy, when to use it, properties, states, modes, styling tokens, usage guidelines. Sourced from Figma.
- CSS —
<DxpContent.Code>snippets for the canonical markup, variants, sizes, and states. Snippets include ARIA wiring so they are copy-pasteable. (This tab was called Develop until PLAT-3650;?tab=developlinks still resolve here.) - React — the React component’s props and examples, plus its Storybook link from
dxp.subpage.react.storybook. Owned by the Dev team. - Accessibility —
## For designersand## For developerssections covering canonical markup, keyboard interactions, and the relevant WAI-ARIA APG pattern.
Each tab file’s headings start at ##, which is what the panel’s “On this page” TOC lists.
The component hero
Section titled “The component hero”Dropping a hero.astro into a <name>-pages/ folder gives that component a live, interactive preview above the tab bar. Nothing else is needed — no frontmatter, no import, no entry in _meta.yml; the file’s presence is the whole switch, and a component without one simply has no hero.
The file holds only the demo markup, hand-written, importing nothing:
<button class="tng-button is-primary"> <i class="tng-icon icon-download"></i> <span>Download</span></button>Everything around it — the stage framing, the CSS / React pills, Maintained by, the Figma and Storybook links — is derived and rendered by the hero component. Don’t author any of it here.
Two things to know when writing one:
- Show the component working, not its parts. Copy the component’s recipe or its Design-tab
<DxpContent.LiveAnatomy>markup as a starting point, then edit freely — de-annotate the pin labels, drop emptytng-slotplaceholders (they read as a hole rather than a component), and give it real content. Unlike the Anatomy stage, this preview is interactive: buttons click, accordions open. export const fill = truefrom the component script widens the demo out of the default narrow frame, for components that can’t lay out inside it — a table, a header bar.
<DxpLayout.*> and <DxpContent.*> helpers
Section titled “<DxpLayout.*> and <DxpContent.*> helpers”Design tab MDX files use a small set of helpers. The card / grid primitives live in ~/components/layout/; the anatomy + token-table helpers live in ~/components/content/:
import * as DxpContent from '~/components/content/index.astro';import * as DxpLayout from '~/components/layout/index.astro';The alias names must be DxpContent and DxpLayout, not Content or Layout — Astro’s MDX integration appends Content[Symbol.for('mdx-component')] = true to every compiled MDX, and a user-side import * as Content shadows that and crashes the build (module-namespace objects are non-extensible).
Helpers for the Design tab template:
| Component | Purpose |
|---|---|
<DxpLayout.CardGrid hasHero? min?> |
Responsive auto-fit grid. hasHero spans the first child; min controls the auto-fit floor (default 280px). |
<DxpLayout.CardStack> |
Single-column stack of cards. Used for the Properties section. |
<DxpLayout.TitleCard title tile? isHero? hasPlaceholder?> |
The everyday content card — <h3> title + body slot. tile="…" inlines an SVG from src/content/; frame slot accepts custom media. No frame renders unless one of those is given, or hasPlaceholder opts into a tng-slot wireframe. isHero is the lead-article variant: a larger title and an uneven, wide-guttered split. |
<DxpLayout.Card> |
Pure layout primitive. Two children: a .tng-frame element + a single caption. Container-query horizontal/vertical at ≥ 600px. Use directly only when TitleCard’s shape doesn’t fit. |
<DxpContent.Overview> + <DxpContent.OverviewItem number href?> |
A preview above a numbered two-column legend. On a component-group landing (buttons.mdx) the preview slot holds live markup of the whole family and each item links to its component; with no preview slot it falls back to a tng-slot wireframe, which is what it stands in as for an unwritten Anatomy section. |
<DxpContent.Preview> |
A staged snapshot of live component markup, inert so a page may show many components without turning them into tab stops. Tune its inset with --preview-padding. Backs Overview’s preview and WhenToUseItem’s example. |
<DxpContent.WhenToUse> + <DxpContent.WhenToUseItem name href?> |
The “Choosing …” table of a group landing: component / usage prose / live example, one row per component. Stacks below 50em. The example goes in the example slot, the prose in the default slot. |
<DxpContent.TokenList name?, type?, numeric?> |
Vertical token swatch list. Canonical for the token-listing Fundamentals pages (fundamentals/{spacing,shape}.mdx and the Color / Typography Design tabs); don’t use it in a component’s design.mdx. |
The legacy helpers — <DxpContent.Guidance>, <DxpContent.Card> + <DxpContent.PropertyGroup> + <DxpContent.Property>, <DxpContent.TokenValue>, <DxpContent.VariablesTable> + <DxpContent.VariablesRow> + <DxpContent.VariablesTextStyleRow> — were removed in the consolidation that introduced these primitives. The old Figma.Grid and Figma.ImageBlock went with them.
For the longer reference (per-tab content shape, SubpageTabs internals), see playbook/CONTRIBUTING.md and the repo-level AGENTS.md.