Foundations
Foundations shows the tokens of the project's design system, read from what the tests load, without stories or MDX. This page covers where the tokens come from, what the view shows, how it shows each design system and how it shows the dark variant.
Where the tokens come from #
After every run, the reporter writes foundations.json next to the manifest.
It reads four sources:
-
Design systems. The ones that
package.jsonandcomponents.jsonname, such as Tailwind, shadcn/ui, Material UI, Emotion and styled-components. -
Custom properties. The recorded stylesheets that declare custom
properties on a root-like selector, such as
:root,html.darkor[data-theme="dark"], and those with@font-face. -
The Tailwind 4 theme. The tokens of the project's own
@themeand Tailwind's defaults, read from the CSS file that loads Tailwind. -
A theme module. The tokens of a JS theme, such as a Material UI theme
or a plain object, when
foundations.themenames one. A Material UI theme is compared with Material UI's defaults.
foundations is the
option, and the output directory says how the file
is stored. Only CSS that the tests load counts. A token stylesheet that only the app entry
imports never reaches a frame, so its tokens are missing too.
When two sources name a token
A token shows once. This decides which source it shows from:
-
A theme module beats
--mui-*. WithcssVariables: true, Material UI also writes its theme as--mui-*custom properties. The same values show once, from the theme module. -
The Tailwind theme beats the variables Tailwind writes. Tailwind writes
its theme into the CSS as custom properties, parts such as
--text-xs--line-heightincluded. They show as tokens of the theme, not a second time as custom properties. -
Aliases give way. A color alias of
@theme inline, such as--color-primary: var(--primary), gives way to the custom property it points at.--primaryshows instead, read from the CSS with its light and dark values. - Missing from the CSS. Tailwind writes only the variables that utilities use. A token of the Tailwind theme that the CSS lacks paints with its theme value.
The view #
A Foundations entry sits in the sidebar of the viewer, above the test files. It shows when
the run found tokens, as
foundations in the
manifest counts them, or when the project uses Material UI. It names the design systems,
such as Tailwind · shadcn/ui, Material UI · Emotion or
styled-components, and custom properties when there is none.
The view has one section per token group, in this order: Colors, Typography, Spacing, Radius, Shadows, Breakpoints, Motion and Other. Only the sections with tokens show, each as a button with its count.
The specimens of the shown section render in one iframe, sandboxed with
allow-same-origin only, so no script runs, as in a
frame. It loads the project's own token and
font stylesheets inside @layer project, so fonts and var() are
real. The view's own CSS sits outside any layer, so it wins where both set the same
property.
Blocks
Inside a section, the tokens fall into blocks, such as Semantic colors,
Type scale or Elevation. A preset per design system forms them,
as Design systems describes. Each block has a title in readable
words, the name prefix its tokens share, such as --chart-* or
palette.primary.*, and its count. A note under the title says what the block
leaves out or how to change it. Within a section, the blocks come in this order, and an
empty block drops out:
- The tokens of the theme module.
-
The custom properties of the recorded stylesheets: the blocks of a preset, then blocks
by name prefix, then
Custom propertiesfor the rest. - The tokens of the Tailwind theme, with Tailwind's default palette last.
A framework default carries a default badge next to its name, so the
project's own tokens stand out. The defaults come from two sources: Tailwind's defaults,
and the tokens of a Material UI theme module that equal Material UI's own, as
Design systems describes. The compact grid of Tailwind's default
colors carries one badge on its title instead.
Specimens
| Section | Specimen |
|---|---|
| Colors |
a swatch, a large chip in the ramp of a color family, a small chip in a compact
palette with its name and value in the tooltip, or a pair tile:
Aa in the foreground on the background, with
their contrast
|
| Typography |
a sample line in the family, the weight, the size with its line height, the line
height or the letter spacing, in the whole style of a composite typography token,
or in a font shorthand. A line height's sample wraps over at least
two lines. The Tailwind theme's weights, tracking and leading paint their sample
in the theme's sans font
|
| Spacing | a bar of that length. A spacing scale adds a row per multiple, with its length in pixels |
| Radius | a box with that border radius |
| Shadows |
a card under a box or inset shadow, a shape under
filter: drop-shadow(), or Aa under a text shadow
|
| Breakpoints | a bar each, scaled to the widest of its block |
| Motion and Other | a list row each |
Each token shows its raw value. When the value refers to other tokens and resolves to
something else, it shows raw → resolved, such as
var(--radius) → 0.625rem or
calc(var(--radius) - 4px) → calc(0.625rem - 4px).
Filter
The field in the toolbar filters the tokens by name and value. Every word must occur, in
any case, in a token's name or in its raw light or dark value. So brand finds
--color-brand-500, and 0.7 0.18 285 finds the tokens whose name
or values hold all three. A pair shows whole when either of its tokens matches, and a
type style when the name of one of its custom properties does.
While the field holds text, each section button counts its matches, and a section without
one shows no token matches.
/ focuses the field while Foundations is open. Esc clears it and leaves the field. Enter leaves the field and, when the open section has no match, opens the first section with one. The filter is not part of the link. It starts empty each time Foundations opens and when a new run arrives.
The inspector
While Foundations is open, the inspector shows a foundations section. A row
that does not apply is left out.
- systems
-
The design systems with their versions. shadcn/ui adds its style and base color from
components.json. - Tailwind entry
- The CSS file that loads Tailwind, such as
src/index.css. - theme module
-
The module and its export, such as
src/theme.ts#theme, with its adapter. - stylesheets
- How many token and font sheets, such as
1 token sheet, 1 font sheet. - dark variant
-
What
darkapplies:class .dark,attribute [data-theme="dark"],prefers-color-scheme,theme modesornone(the dark variant). - collected
- How long the reporter took to collect the tokens, in milliseconds.
An errors section lists the problems the reporter met while collecting.
foundationsErrors
says what to do about them.
Links and hosting
The link keeps the section as foundations=<section>, which
Links describes with the rest of the address. Foundations
works on a static site from describe-me build and without Live. The viewer
fetches foundations.json only when the view opens.
See the colors of the shadcn/ui example,
its radius, where the project's own
tokens show raw → resolved, and the
typography of the Material UI example.
Design systems #
A preset per design system groups the tokens into blocks. Material UI goes first, then shadcn/ui and Tailwind, and a generic preset takes what is left. A token still shows once, in the section of its kind.
Tailwind
The tokens of the Tailwind 4 theme form these blocks:
- Colors
-
A ramp of large chips per color family of the project with at least three numeric steps,
such as
--color-brand-*. ThenTheme colorsfor the project's other colors, andTailwind palette: the default colors in a compact grid, one row per family. Its note says how to remove it: add--color-*: initialto@theme. - Typography
-
Font families,Type scalewith each size's line height,Font weights,Tracking,LeadingandBase font. - Spacing
-
Spacing scale:--spacing, then a row per multiple that utilities such asp-4use, from 1 to 96, each with its length in pixels. - Radius
Radius scale.- Shadows
-
Box shadows,Inset shadows,Drop shadowsandText shadows, each painted the way its utility paints it. - Breakpoints
BreakpointsandContainers.- Motion
Easing,AnimationsandTransitions.- Other
-
A block per namespace, such as
Blur,PerspectiveandAspect.
The project's tokens and Tailwind's defaults share a block, and each default carries the
default badge. See the
spacing scale and the
typography of the shadcn/ui example.
shadcn/ui
With shadcn/ui, its custom properties form four blocks ahead of the Tailwind ones:
Semantic colors-
The color custom properties outside the chart and the sidebar.
--primaryand--primary-foregroundform a pair, as does every--Xwith its--X-foreground, and so do--backgroundand--foreground. A pair shows as one tile:Aain the foreground on the background, both tokens with their values, and their contrast. The singles, such as--borderand--ring, follow as swatches. Chart--chart-*.Sidebar--sidebar-*, with pairs of its own.Radius-
--radius, then the Tailwind radius tokens derived from it, such as--radius-lg: var(--radius).
The contrast is the WCAG 2 ratio of the two colors as the frame paints them, for the mode
on screen, so bg=dark shows the ratio of the dark pair. A translucent color
counts over what lies under it. The level follows the ratio:
AAA- 7:1 and up.
AA- 4.5:1 and up, enough for body text.
AA large- 3:1 and up, enough for large text only.
fail- Below 3:1.
See the pairs of the shadcn/ui example and its radius.
Material UI
One preset, fed by the theme module that foundations.theme names or, without
one, by the --mui-* custom properties that cssVariables: true
writes. With both, the theme module wins. The blocks:
-
Palette.
Primary,Secondary,Error,Warning,InfoandSuccessshowmain,lightanddarkas swatches, andcontrastTextas a pair tile withmainbehind it. Then theGreyramp,Text,Background,Divider,CommonandAction. From--mui-*,Componentsholds the colors of components, such as--mui-palette-Alert-*. -
Typography.
Variantsshows the 13 variants,h1tooverline, each as a sample line in its whole style with its parts. From--mui-*, each sample paints itsfontshorthand. A theme module addsFont familyandFont weights. -
Spacing.
spacing(0)tospacing(12). From--mui-*,--mui-spacingwith a row per multiple. -
The rest.
Shape, the 25 levels ofElevation,Breakpoints,Duration,Easingandz-index. From--mui-*, alsoOpacityandOverlays.
Material UI writes most palette colors a second time as …Channel properties,
such as --mui-palette-primary-mainChannel: 91 75 219, for colors with alpha.
The preset leaves them out, and each block that had some says how many in its note.
Material UI 9.5 writes no breakpoints and no transitions as custom properties, so those
blocks show only with a theme module.
With a theme module, the reporter also builds Material UI's defaults with the project's
own createTheme(), from the same light and
dark color schemes and cssVariables as the theme and nothing
else. Each token with the same value carries the default badge. A typography
variant counts only when every part matches. When only the light or only the dark value
matches, the badge shows only while that mode is on screen, as the stage background picks
it. A derived value such as contrastText counts when it comes out the same.
Only a theme module marks defaults, never the --mui-* custom properties. When
@mui/material/styles does not import, every token stays the project's own and
the diagnostics get one
sentence. The Material UI example runs on CSS variables with its theme module, so its
--mui-* give way to the module. See its
palette, where only Primary and Secondary
are its own, and its typography.
When Foundations opens for a project that uses Material UI without a theme module or
--mui-*, it says what to set: “To show the Material UI theme here, name its
module in foundations: { theme: 'src/theme.ts' }, which also marks Material
UI's defaults, or create the theme with cssVariables: true.” Without any
token, the page holds only the heading and that sentence.
Other design systems
Custom properties that no preset takes form blocks by name prefix, within each section.
Names that share their first two parts, such as --chakra-colors-gray-50 and
--chakra-colors-bg, form a block when there are at least two. Its title and
prefix come from the longest prefix they share: Chakra colors,
--chakra-colors-*. Names of two parts group by the first. The rest show under
Custom properties. Chakra UI, Mantine and Radix Themes show this way.
The tokens of a plain theme module group by their parent path, so
colors.ink shows in a block Colors, colors.*, as in
the jsdom example.
Type styles
A custom property whose name holds font-family, font-weight,
font-size, line-height or letter-spacing shows in
Typography with a sample of that part. The part may sit anywhere in the name and be
written in camelCase, in the plural or as one word, as in --font-size-lg,
--heading-fontSize, --chakra-font-sizes-md or
--font-lineheight-1. It wins over the Tailwind prefixes, so
--font-size-lg is a size and not a font family.
Custom properties that end in a part and share the name before it form one type style, as
Style Dictionary and Tokens Studio write them. --type-title-font-size,
--type-title-line-height and --type-title-font-weight show as
one token, --type-title-*: a sample line in the whole style, the font size as
its value, and its parts under it. A style needs a font size and at least one other part,
so --link-font-weight and --link-letter-spacing stay two rows.
The sample paints through the custom properties, so the dark variant applies, and the
parts show their light values. Styles group by name prefix like any custom property, so
--type-title-* and --type-body-* form a block Type,
--type-*. Custom properties that a preset takes, such as
--mui-*, never form a style.
A plain theme module forms its type styles from objects. An object with a
fontSize and at least one more of fontFamily,
fontWeight, lineHeight, letterSpacing and
textTransform, such as typography.title, is one token with its
parts, in the block of its parent path, typography.*. A number is in px for a
size and a letter spacing, as React writes it. The object's other keys, such as a variant
nested in it, stay tokens of their own. An object whose fontSize is a scale,
such as { sm: '14px' }, is no style. A weight or a line height written as a
string, such as '600' or '1.5', counts as one when its path
names it, as in fontWeights.semibold.
The dark variant #
The stage's background switch sits at the top of the view too, with the same four swatches
as the viewport toolbar: auto,
light, dark and transparent. It is part of the link
as bg. dark applies the dark variant the project declares:
- A class.
.darkon the root. -
An attribute.
[data-*="dark"], such as[data-theme="dark"]. -
A media query. Rules under
prefers-color-scheme: dark. The iframe of the specimens takes the dark color scheme, so they match. -
light-dark(). Values written with it show their dark value. -
Theme modes. A theme module with a light and a dark mode, such as the
colorSchemesof a Material UI theme, shows its dark values.
light and auto show the light variant.
transparent shows it on the checkerboard. Under dark, when the
project has no dark variant and no theme modes, the view shows the hint
no dark variant found, except on a page that holds only the Material UI setup
hint. The inspector's dark variant row names the one the project declares.
The stage's switch uses the same result. Under dark, a replayed frame, a
thumbnail and the test in Live get the class or attribute that the dark variant row names
(background).
The viewer theme, the switch in the header, never changes a specimen. It colors only the panel around them.
Compare the dark variant of the shadcn/ui example, a .dark class, in its
colors, that of the
browser-mode example, prefers-color-scheme, in
its colors, and that
of the Material UI example, the prefers-color-scheme rule of its CSS
variables beside its theme modes, as its inspector row says, in
its colors.
Limits #
Foundations reads only the CSS that the tests load. The Tailwind 4 theme comes from an unstable API, the config of Tailwind 3 is not read, and Chakra UI, Mantine and Radix Themes show in generic blocks by name prefix. Limitations lists the workarounds.