Getting StartedComponentsDesign TokensThemingTheme SynthesizerFrameworksAccessibilityUtilitiesServer RenderingBrowser SupportContributingChangelogApp ShellAspect GridAuth ShellCenterContainerDashboard GridFloat BarInsetMasonryPage HeaderPage LayoutResizableResponsive SwitcherSectionSettings LayoutSplit PaneStatus BarStickyToolbarAnchor NavBottom NavBreadcrumbBreadcrumb MenuCommand BarDrawerFooterLinkMenubarNavigation MenuPage IndicatorPaginationRailScroll IndicatorScroll SpyScroll To TopSidebarSkip LinkStepper NavTabsTop BarTree ViewAccordionAspect RatioAvatarAvatar GroupCardCarouselCollapsibleColor SwatchCTA BannerDividerEmpty StateFeature CardIconImageImage CompareImage HotspotsInfinite ScrollLightboxMarqueeQR CodeScroll AreaSkeletonSpinnerStackVideoVirtual ListActivity HeatmapAnimated NumberBadgeBar ListChartClockComparisonCountdown TimerData GridDescription ListDiffGaugeJSON TreeKanbanLevel MeterListMeterSparklineStatStepperTagTimelineUptimeValue CardWaveformBlockquoteCode BlockCode GroupGradient TextHighlightKbdKeyboard MapMarkdownNumber FormatProseTerminalTextTime AgoTruncateTypewriterButtonButton GroupCalendarCheckboxChipColor PickerComboboxCopy ButtonDate PickerDate Range PickerField ListFieldsetFile UploadFormHotkeyIcon ButtonImage CropperInline EditInputInput GroupKnobLabelMasked InputMulti SelectNumber InputPassword InputPin InputRadio GroupRange SliderRatingSearchSegmented ControlSelectSignature PadSliderSortable ListSwitch GroupTag InputTextareaTheme ToggleTime PickerToggleTransfer ListTree SelectAlertAnnouncementBannerCommand PaletteConfirmConnection StatusContext MenuConversationDialogDropdown MenuHover CardLoading OverlayNotification PanelPopoverProgressSheetToastTooltip
ARC UIARC Radiant Components
v4.9DocsComponentsTokensSynthesizer
Getting StartedFrameworksServer RenderingDesign TokensThemingTheme SynthesizerTypographyUtilitiesAll ComponentsAccessibilityBrowser SupportChangelogContributingStatsApp ShellAspect GridAuth ShellCenterContainerDashboard GridFloat BarInsetMasonryPage HeaderPage LayoutResizableResponsive SwitcherSectionSettings LayoutSplit PaneStatus BarStickyToolbarAnchor NavBottom NavBreadcrumbBreadcrumb MenuCommand BarDrawerFooterLinkMenubarNavigation MenuPage IndicatorPaginationRailScroll IndicatorScroll SpyScroll To TopSidebarSkip LinkStepper NavTabsTop BarTree ViewAccordionAspect RatioAvatarAvatar GroupCardCarouselCollapsibleColor SwatchCTA BannerDividerEmpty StateFeature CardIconImageImage CompareImage HotspotsInfinite ScrollLightboxMarqueeQR CodeScroll AreaSkeletonSpinnerStackVideoVirtual ListActivity HeatmapAnimated NumberBadgeBar ListChartClockComparisonCountdown TimerData GridDescription ListDiffGaugeJSON TreeKanbanLevel MeterListMeterSparklineStatStepperTagTimelineUptimeValue CardWaveformBlockquoteCode BlockCode GroupGradient TextHighlightKbdKeyboard MapMarkdownNumber FormatProseTerminalTextTime AgoTruncateTypewriterButtonButton GroupCalendarCheckboxChipColor PickerComboboxCopy ButtonDate PickerDate Range PickerField ListFieldsetFile UploadFormHotkeyIcon ButtonImage CropperInline EditInputInput GroupKnobLabelMasked InputMulti SelectNumber InputPassword InputPin InputRadio GroupRange SliderRatingSearchSegmented ControlSelectSignature PadSliderSortable ListSwitch GroupTag InputTextareaTheme ToggleTime PickerToggleTransfer ListTree SelectAlertAnnouncementBannerCommand PaletteConfirmConnection StatusContext MenuConversationDialogDropdown MenuHover CardLoading OverlayNotification PanelPopoverProgressSheetToastTooltip

Typography

ARC UI ships no font files. It defines five typographic roles and names a reference face for each; you assign your own by overriding one token per role.

Roles, Not Typefaces

A design system that bundles its own typefaces makes two decisions on your behalf: which faces you use, and how they reach the browser. ARC UI makes neither. What it commits to is the roles: which parts of the interface are set in which voice, at what size, weight and tracking. Those relationships are the typographic design; the specific faces are yours.

Every role is four tokens: the family you assign, the fallback stack behind it, the weight the role is set at, and the composed value components actually consume:

Override only -family and everything downstream (the fallback, and every component using that role) follows. Nothing here needs you to retype a font stack.

The weight is a separate knob because a face you assign will not always ship the weight the role is set at. The Label role is set at 600 across some fifty components; point it at a face whose heaviest cut is 500 and the browser synthesises the difference, which looks like smeared type rather than a missing weight. Setting--font-label-weight once fixes all of them; derived tokens like--section-title-weight and --ui-accent-weight reference the role rather than restating a number.

The Roles

Text--font-body-family
Sphinx of black quartz, judge my vow.

Prose, inputs, descriptions, option lists, table cells. The default for everything not covered by another role.

Reference Host GroteskFallback system-ui, sans-serifWeight 500
Label--font-label-family
Section label

Form labels, table headers, eyebrows, dividers, segment labels. Rendered small, uppercase and tracked, so it wants a face that holds up at 12px in caps.

Reference TomorrowFallback ui-monospace, monospaceWeight 600
Mono--font-mono-family
1,024.7 ms · +12.4% · 0x2F

Code blocks, keyboard hints, command palette shortcuts, and tabular numerics: meter readouts, stat deltas, calendar day numbers.

Reference JetBrains MonoFallback ui-monospace, monospaceWeight 400
Display--font-display-family
Radiant interfaces

Large headings and hero type. Inherits the Text role until you assign it a face of its own, so a display typeface is opt-in rather than assumed. The weight does not follow Text; large type usually wants its own.

Reference follows TextFallback follows TextWeight 500
Quote--font-quote-family
“

The quotation glyph on arc-blockquote, the one place the system wants a serif.

Reference GeorgiaFallback serifWeight 200

--font-accent is kept as an alias of the Label role. It named that role before the slots were split out, and the name was misleading: it reads like a display face, but it has always driven form labels and table headers. Prefer--font-label in new work.

Assigning a Typeface

Set the family tokens on :root, after ARC UI's stylesheet. That is all. Components read the roles through the shadow boundary.

Assigning one role does not disturb the others. Leaving a role unassigned keeps its reference face, and if that face never loads, its fallback.

Using one typeface everywhere

Point the roles at the same family. The size, weight and tracking distinctions that separate them are defined independently, so the hierarchy survives.

Scoping to part of a page

Role tokens are ordinary custom properties, so they cascade. Assign them on any ancestor to rebrand a subtree.

Loading the Font Files

Assigning a role tells components which family to ask for. Getting that family into the browser is separate, and ARC UI stays out of it. Self-host, use a font CDN, or use a package like Fontsource. A self-hosted @font-face looks like this:

Declare a variable font once, with a weight range. Declaring the same file under several discrete weights is the most common way to get this wrong: a@font-face naming a single weight gives the browser no way to set thewght axis, so it renders the font's default instance for every one of them. Nothing errors, every weight resolves, and the hierarchy is flat: components in this library ask for weights from 200 to 800 and would all render identically.

Use the range the font actually has. Asking for a weight outside it gets the nearest end of the axis, so a face whose lightest cut is 300 cannot deliver the 200 some roles default to. Set --font-quote-weight and friends to something it can reach instead.

Preload the faces used above the fold, and only those:

Use the family name the package registers, not the one you expect

Whatever loads your font decides what the family is called, and the assignment has to use that exact string. This mistake fails quietly: an unknown family name is not an error; it is skipped, so the role falls through to its fallback and the page renders in system-ui, which looks close enough to a grotesque to read as "the font loaded" at a glance. Fontsource's variable packages are where this usually happens; they register the family with a Variable suffix:

The static Fontsource packages (@fontsource/host-grotesk) register the bare name, so the two differ by more than the import path. To check which you have, look at the font-family in the package's own CSS, or confirm in DevTools: the Computed panel names the face actually rendering, and Rendered Fonts names the file it came from. If it says system-ui, the name is wrong.

Fallbacks & Layout Shift

Each role carries a fallback stack so an unassigned or unloaded face degrades to something deliberate rather than to the browser default. You can replace a fallback on its own:

Fallbacks differ from webfonts in width and vertical metrics, so text reflows when the real face arrives. The fix is a metric-matched fallback declared against your font's actual metrics:

Tools such as Fontaine and next/font generate these overrides automatically. Pair with font-display: swap; with a metric-matched fallback the swap stops being visible as a jump.

The Reference Faces

The defaults name Host Grotesk, Tomorrow, JetBrains Mono and Georgia. The specimens above render them live (this documentation site loads them, and the components were designed against them), but ARC UI does not ship or load them. If you load nothing, each role renders its fallback, which is a supported configuration rather than a broken one.

Treat them as a reference rendering, not a requirement. The roles are the contract.