Utilities
ARC UI components own everything inside their shadow boundary and nothing outside it. The space between components is yours, and in practice everyone writes the same flex, gap and padding CSS to fill it, against 237 custom propertiesbase.css already publishes with no ergonomic way to use them.
272 generated, arc--prefixed classes, about 2.6 KB gzipped, covering spacing, layout and surface. It is a deliberate middle: enough to lay out a page around ARC components, not enough to be a framework. This site loads the stylesheet, so every demo on this page is the classes themselves, live.
Why not just use Tailwind
You can, and for most utility work you should. What Tailwind cannot do is know whatarc-card uses for its own padding.
These classes are generated from the same tokens.js the components compile against, so .arc-p-md and a card's internal padding resolve to the same value, and overriding one base token moves both:
:root { --space-md: 20px; }
/* .arc-p-md and every component's internal md spacing move together */
Every declaration reads a var() rather than a literal, which is what keeps that link intact. Use these where you want your layout to track the component system, and anything else for the rest.
Install
import '@arclux/arc-ui/base.css'; /* defines the tokens */
import '@arclux/arc-ui/utilities.css'; /* uses them */
Opt-in and standalone: nobody who only wants tokens pays for it. Every class isarc- prefixed, because consumers run Tailwind or Bootstrap and an unprefixed .flex would collide catastrophically. It costs the terseness that makes utilities pleasant, and there is no way around it for a library.
Spacing
Every step of the --space-* scale, on padding, margin and gap. Suffixes are logical, not physical: x/y for the inline and block axes,s/e for start and end, t/b for block start and end. A layout built from these mirrors under dir="rtl" without a second stylesheet. The same class works in both directions:
.arc-ps-xl.arc-ps-xl dir="rtl".arc-p-md | padding: var(--space-md) |
.arc-px-lg | padding-inline: var(--space-lg) |
.arc-py-sm | padding-block: var(--space-sm) |
.arc-ps-xl | padding-inline-start: var(--space-xl) |
.arc-m-0 | margin: 0 |
.arc-mx-auto | margin-inline: auto |
.arc-gap-md | gap: var(--space-md) |
.arc-gap-y-lg | row-gap: var(--space-lg) |
Steps are xs sm md lg xl 2xl 3xl 4xl, plus 0 everywhere andauto on margins.
Layout
.arc-flex.arc-items-center.arc-justify-between.arc-col-span-2.arc-grid-cols-3.arc-gap-sm.arc-col-span-full- Display:
.arc-flex,.arc-inline-flex,.arc-grid,.arc-block,.arc-contents,.arc-hidden. - Flex:
.arc-flex-col,.arc-flex-wrap,.arc-flex-1,.arc-grow,.arc-shrink-0. - Alignment:
.arc-items-center,.arc-justify-between,.arc-self-end. - Grid:
.arc-grid-cols-1through-6,.arc-col-span-*,.arc-col-span-full. - Size:
.arc-w-full,.arc-h-full,.arc-max-w-full, and.arc-min-w-0, which is the fix for a flex child that refuses to shrink and text that refuses to ellipsis.
Surface
.arc-bg-card .arc-border .arc-rounded-md .arc-shadow-lg.arc-text-secondary .arc-text-sm.arc-text-accent-primary .arc-font-mono.arc-text-primary | color: var(--text-primary) |
.arc-text-lg | font-size: var(--text-lg) |
.arc-bg-card | background-color: var(--bg-card) |
.arc-border | border: 1px solid var(--border-default) |
.arc-border-subtle | border-color: var(--border-subtle) |
.arc-rounded-md | border-radius: var(--radius-md) |
.arc-shadow-lg | box-shadow: var(--shadow-lg) |
.arc-font-mono | font-family: var(--font-mono) |
Colors use the token names directly, so .arc-text-primary is the foreground color and .arc-text-lg is a type size, the same split--text-primary and --text-lg already have. The generator fails the build if a future token ever makes those two collide.
Font classes name the five roles, not typefaces:.arc-font-body.arc-font-label.arc-font-mono.arc-font-display.arc-font-quote
Limits
- Light DOM only. A class cannot cross a shadow boundary. These style the space around ARC components, never anything inside one. Use
::part()and the CSS custom properties for that. - No responsive or state variants. No
sm:, nohover:. That is where a utility layer becomes a framework, and this one is not that. Write a media query. - No arbitrary values. The scale is the point: a class that resolves to an arbitrary pixel value is not tracking the token system.
- Requires the tokens. Every class reads a
var(), so loadbase.css, or define the same variables yourself.