Getting Started
Typography
The type system uses Inter 300 for all UI text and Source Code Pro for monospace content. Both fonts are loaded at the app level via Next.js next/font and exposed as CSS variables that feed into the @theme inline token bridge.
Font families
| Token | Font | Usage |
|---|---|---|
--font-sans | Inter 300 | All body text, labels, headings, UI copy |
--font-mono | Source Code Pro | Code blocks, inline code, terminal output, token names |
Inter is loaded from Google Fonts via next/font/google at weight 300. Source Code Pro is also loaded from Google Fonts. Both use the variable option to emit CSS custom properties that feed into the Tailwind token bridge. All text uses letter-spacing: 0.001em (0.1% text spacing) for improved readability.
How fonts are loaded
// apps/docs/app/layout.tsx
import { Inter, Source_Code_Pro } from "next/font/google";
const inter = Inter({
variable: "--font-inter",
subsets: ["latin"],
weight: "300",
display: "swap",
});
const sourceCodePro = Source_Code_Pro({
variable: "--font-source-code-pro",
subsets: ["latin"],
weight: ["400", "500", "600", "700"],
});
// Applied to <body>:
// className={`${inter.variable} ${sourceCodePro.variable} font-sans antialiased`}The variable option on each font emits a CSS custom property (--font-inter, --font-source-code-pro) scoped to the element the class is applied to. The @theme inline block then maps these to the Tailwind-facing tokens:
/* globals.css @theme inline block */
@theme inline {
--font-sans: var(--font-inter);
--font-mono: var(--font-source-code-pro);
}Using font-sans in any Tailwind class therefore resolves to Inter, and font-mono resolves to Source Code Pro. Each app can point these tokens at different fonts by overriding the mapping in its own globals.css.
Type scale
The design system uses Tailwind's default type scale with no custom additions. The scale is grid-aligned: every size and its corresponding line height are multiples of 4px.
| Class | Size | Line height | Usage |
|---|---|---|---|
text-xs | 12px | 16px | Captions, timestamps, section labels, badge text |
text-sm | 14px | 20px | UI body text, table cells, nav items, helper text |
text-base | 16px | 24px | Default prose body (set on <body>) |
text-lg | 18px | 28px | Lead paragraphs, sub-section titles |
text-xl | 20px | 28px | Section headings (h3 in UI contexts) |
text-2xl | 24px | 32px | Page sub-headings (h2) |
text-3xl | 30px | 36px | Page titles (h1) |
Heading examples
These headings are rendered by the prose layer. In UI components, apply the same sizing and weight via Tailwind utilities directly rather than relying on the prose plugin.
Heading 1 — page title
Heading 2 — section
Heading 3 — sub-section
Heading 4 — detail
Prose rendering
Long-form documentation content is wrapped in a prose prose-sm container from the @tailwindcss/typography plugin. The plugin's CSS variables are remapped to our design system tokens in globals.css so that prose content inherits the correct colors automatically.
| Prose variable | Mapped to token |
|---|---|
--tw-prose-body | --foreground-light |
--tw-prose-headings | --foreground-default |
--tw-prose-lead | --foreground-lighter |
--tw-prose-links | --brand-link |
--tw-prose-bold | --foreground-default |
--tw-prose-bullets | --brand-default |
--tw-prose-code | --brand-300 |
--tw-prose-pre-bg | --background-surface-75 |
--tw-prose-th-borders | --border-default |
--tw-prose-td-borders | --border-muted |
The prose container max-width in this docs site is set to max-w-[57.6rem] with mx-auto centering, matching Supabase's documentation layout proportions.
Code
Use inline code for variable names, file paths, token names, and short snippets. Use fenced code blocks for multi-line examples. Inline code renders with a subtle surface background and border. Block code uses the same surface background with a rounded border and full monospace font.
// Example: cn() utility for class merging
import { cn } from "@repo/ui/utils";
function StatusBadge({
status,
className,
}: {
status: "todo" | "in_progress" | "done" | "blocked";
className?: string;
}) {
return (
<span
className={cn(
"inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium",
{
"bg-surface-200 text-foreground-lighter": status === "todo",
"bg-brand-400 text-brand-600": status === "in_progress",
"bg-brand-500 text-brand": status === "done",
"bg-destructive-300 text-destructive-600": status === "blocked",
},
className
)}
>
{statusLabel[status]}
</span>
);
}