foundations
Star
Reference

Blocks

Every component outside typography and prose. They ship from two import subpaths, and each group names the one it comes from.

Content

@supertype.ai/foundations/blocks

Components that sit inside prose. Tabs are a client component built on Base UI. The rest render on the server.

Card

Pass title and description for the default layout, or compose the slots directly for full control. An href makes the whole card a link; an href with a scheme opens in a new tab.

Callout

Two variants. compact is sized to sit inside a panel or sidebar. editorial sets the body at reading size and adds an accent rail, for docs and long-form pages.

Rotate the key first

The old one stops working the moment the new one is issued.

This deletes the slot

An abandoned replication slot holds WAL until the disk fills.
Why this is safe
Replication slots are consumed in order. See slot hygiene.
With no tone set, a callout renders muted.

Button

Two independent axes. variant is how much ink the button spends, and tone is what the ink means. A tone sets three custom properties, a variant spends them, and the grid below comes to thirty-five pairs from twelve declarations. The seven tones belong to the package rather than to this component: Callout and TypographyLink take the same list.

Badge

The same two axes as Button, spelled the same way, minus link, which belongs to things you click. Both apps had grown a private list instead, one carrying warning and supertype for tones the package already named warn and brand.

Steps

A numbered sequence. Numbers come from a CSS counter, so reordering steps renumbers them automatically, and the digits are excluded from copied text and from the accessibility tree.

  1. Add the package
    Pin a tag, never #main.
  2. Import the CSS, in order
    Or run npx foundations initto write the imports for you.
  3. Bind the fonts
    With .variable, never .className.

Disclosure

The one way to show and hide a row, built on the native <details> element: no JavaScript, correct before hydration, and animated where the browser supports it. Set type to single to make a group single-open, which the browser handles for you.

Retries
Three attempts, then backoff.
Timeouts
30s, then the job is requeued.
Dead letters
Kept 14 days, then dropped.

Tabs

TabGroup takes the tabs as data, which is the shape to reach for. Its variant picks the box: default renders a boxed segmented track, line drops the surface and underlines the active tab, as in the preview and code switcher on this page. Its tone inks the marker alone, which on line means the underline and the active tab's icon. With no content on any tab it is a picker, the strip alone; size sm is the compact strip for a toolbar or a chart. The values' own type flows to value and onValueChange, so a picker can drive a union of values without a cast.

npx foundations doctor
The active icon takes the list's tone.

Bulletin and Colophon

Bulletin is the panel shell without any copy. It gives you a top accent, an eyebrow, a headline, a short intro, a two-column list, and a footer action. Colophon is the same structure with the package's own message built in. A standalone attribution link variant is also provided for footers that need compactness.

The default panel. This is just <Colophon />. Hover over any of the hues to expand it.

Colophon

Designed with intention and mathematical rigor.

Typography primitives, semantic tokens, the essay shell, and contrast checks in one package, enforced in CI.

  • Decisions live in one place

    Utility classes are for styling. The actual decision belongs in the design system. Every type style, tone, and divider on this page comes from one package, so a change is one diff instead of a search through the app.

  • Every colour is measured twice

    WCAG tells us whether a colour passes the audit. APCA's Lc tells us how it feels in context, since the same contrast can read differently on different backgrounds. Both checks run against the shipped stylesheet in light and dark mode, so CI catches anything that slips.

Built with FoundationsOpen source design system by Supertype. MIT licensed.

The compact row for a footer that only has room for one line.

Built with FoundationsColors, typography, and blocks, tested and measured.

Use children for your own copy. It appears under the rule, where you would usually place a short note about the site or the people behind it.

Colophon

Designed with intention and mathematical rigor.

Typography primitives, semantic tokens, the essay shell, and contrast checks in one package, enforced in CI.

  • Decisions live in one place

    Utility classes are for styling. The actual decision belongs in the design system. Every type style, tone, and divider on this page comes from one package, so a change is one diff instead of a search through the app.

  • Every colour is measured twice

    WCAG tells us whether a colour passes the audit. APCA's Lc tells us how it feels in context, since the same contrast can read differently on different backgrounds. Both checks run against the shipped stylesheet in light and dark mode, so CI catches anything that slips.

Built with FoundationsOpen source design system by Supertype. MIT licensed.
Set in Ubuntu Sans and Average. Open-source design system by Supertype.

Bulletin uses the same structure without the content. It works well for announcements, release notes, or status updates. Every slot is optional.

Release 4.2

Exports run on a schedule now.

Two things shipped this week, and one of them changes how you set up a recurring report.

  • Scheduled exports

    Pick a cron schedule and the report lands in your inbox on the hour. We run backfills from the same query, so a resend matches the original.

  • Faster cold starts

    The worker pool now warms up on deploy instead of waiting for the first request. The slowest page in the app dropped from 1.9s to 240ms at the median.

Read the notesRolling out to all workspaces through Friday.

BuiltWithFoundations is the standalone link. Use it when the link sits inside another layout, such as a footer row with other links.

Built with Foundations

Rail and RailLink

The primitive behind every index in the package: a list with a rule down its side. On RailLink, active highlights an item, nested indents a sub-heading, and render swaps the anchor for a router link. It runs without client hooks, so it works in a server component.

TableOfContents

A margin index that highlights the current section with a scroll spy. Pass sections as { id, label } matching the ids on your headings. label renames the rail heading; pass null to hide it. An empty list renders nothing at all. The examples below spy on this page, so they update as you scroll.

ReadingRail

TableOfContents plus a progress donut, for pages read start to finish. Takes TocHeading[], { depth, id, label }, the shape extractHeadings returns. Headings at depth 3 are indented.

ReadingProgressBar

A hairline progress indicator fixed to the top of the viewport, for the widths where the rail is hidden. Mount one per page, above your header. It shares a single scroll listener with ReadingRail, so using both costs one subscription. The preview is omitted here, since it would track this page instead of the example.

Editorial

@supertype.ai/foundations/essay

Layout, meta, and the shell that composes them. Use the individual pieces for MDX articles, and EssayDocument when the content is data.

EssayColumns

A three-track grid: margin, measure, and an aside column that usually stays empty. Reserving that third track keeps the prose aligned across pages with and without asides. The measure grows with each step, and the aside appears once the container reaches 72rem. This is a container query rather than a viewport one, so a shell in a narrow column drops the rail and centers the prose instead of leaving a margin that is too narrow for a label. The preview uses this page's column rather than forcing a width it cannot support, so it shows the centered case. The specimen runs it at page width, where the margin track appears.

Use EssayColumns when the margin holds something other than a rail. For articles, ReadingLayout composes these columns with the reading rail already in the aside.

scaled to the 72rem container this block asks for

ReadingLayout

ReadingLayout combines EssayColumns with a reading rail in the margin for articles whose body is supplied as prose or compiled MDX. It works like EssayLayout, with the same column and sticky offset, but uses a scroll-spied rail instead of the declared-section index. If there are no headings, the rail disappears and the layout stays centered. The preview uses that narrower layout for the same reason: it is given the column rather than a fixed width. Expand the container past 72rem, as in the specimen, and the rail reappears.

Use ReadingLayout when the body arrives as prose or MDX. Pass the headings extracted from the markdown, and the rail, sticky offset, and headingless case are handled for you.

scaled to the 72rem container this block asks for

EssayMovements

An ordered list for stages that feed into one another: a pipeline, a method, a sequence of phases. Reach for it over a card grid when the order carries meaning.

  1. 01

    Read the source

    extractHeadings walks the raw markdown, so the index is ready before the MDX is compiled.

  2. 02

    Slug every heading

    createSlugger follows the same algorithm as rehype-slug, so rail anchors match the ids on the headings.

  3. 03

    Hand them to the rail

    ReadingRail takes the list as returned. The depth on each heading is what drives the indent.

Post meta

The byline row under an article title. PostMetaRow lays out PostDate, ReadTime and TagPills, separated by MetaDot. Dates format in en-US on both server and client, which keeps hydration free of a locale mismatch. Use formatPostDate for the same output outside React, such as in an OG image or a feed.

7 min readinfrastructurepostgres
7 min read

The essay shell

EssayHeader, EssayLayout, EssaySection, EssayPullQuote and EssayFigure compose an article by hand. EssayDocument takes the sections as data instead and builds the margin index from them. See a rendered essay for what they look like at full page width.

Helpers

@supertype.ai/foundations/essay

The functions that produce heading data for the rails, and the two hooks they run on.

Headings from markdown

extractHeadings pulls h2 and h3 from raw markdown, ignoring fenced code. It runs on the string alone, so it works at build time. Ids come from createSlugger, which follows GitHub's algorithm and therefore matches the ids rehype-slug adds to your headings. readingTime skips code blocks, which keeps the estimate honest on technical posts. The output below was generated at build time.

extractHeadings(source)

  • h2 The queue #the-queue
  • h3 Draining it #draining-it
  • h2 The measure #the-measure

createSlugger()

  • The queue: #the-queue
  • The Queue!: #the-queue-1

readingTime(source)

1 min. The fenced block is excluded from the count.

Scroll hooks

The hooks behind TableOfContents and ReadingRail, exported so you can build your own. useScrollSpy tracks a list of heading ids with a single IntersectionObserver. useReadingProgress returns 0 to 1 from a shared store, so any number of callers on a page share one scroll listener.