Pricing

CSS files

How the global CSS is split into the theme, base styles and utilities, and what each item adds to each file.

slabui init splits your CSS into four files. The global CSS is the css file from slabui.json, wherever it is; the other three go to core/, in the folder of the styles alias, and the global CSS imports them by their relative path.

styles/
├── globals.css         ← the theme: :root, .dark and @theme inline
└── core/
    ├── fonts.css       ← typography: font families and the text-* scale
    ├── base.css        ← element styles (*, html, body) in @layer base
    └── utilities.css   ← @utility and @custom-variant
styles/globals.css
@import "tailwindcss";
@import "./core/fonts.css";
@import "./core/base.css";
@import "./core/utilities.css";
 
@custom-variant dark (&:is(.dark *));
 
@theme inline {
  /* radius, spacing and --color-* */
}
 
:root {
  --font-size: 1rem;
  /* light theme variables */
}
 
.dark {
  /* dark theme variables */
}

The global CSS holds only the theme, so it is the file the theme editor reads and writes. Styles that are not theme variables live in the other files, where update can change them without touching your colors.

Text size

Every text-* class is proportional to --font-size, the size of text-base. Change it in :root (or at runtime, on <html>) and all text grows or shrinks together; spacing and heights stay the same.

styles/core/fonts.css
@theme inline {
  --text-xs: calc(var(--font-size) * 0.75);
  --text-xs--line-height: calc(1 / 0.75);
  --text-sm: calc(var(--font-size) * 0.875);
  --text-sm--line-height: calc(1.25 / 0.875);
  --text-base: var(--font-size);
  /* … up to --text-9xl */
}

Line heights are unitless ratios, so they follow the font size on their own. Keep them unitless: a 1.25rem line height stays fixed while the text grows. Arbitrary sizes such as text-[0.625rem] do not go through the theme and do not follow --font-size.

--font-size is in rem, so it respects the font size set in the browser. Use it for a "text size" setting for your users, for example 1.125rem for larger text.

Where each thing goes

What it isFile
Theme variables, @theme inline, the dark variantglobals.css
Font families and the text-* scalefonts.css
Styles for elements in the whole app (*, body)base.css
Reusable @utility and @custom-variantutilities.css
CSS that exists only because of one componentstyles/ui/<component>.css, a file of the item
Choices of your app's layoutYour own CSS, not the registry

Keep scrollbar, * and other element styles inside @layer base. In Tailwind 4, CSS outside a layer always wins over the layers, utilities included: a * { scrollbar-width: thin } outside @layer base breaks no-scrollbar.

What add writes

Each item can bring, besides its files:

Item fieldWritten toMerged by
cssVarsglobals.css (--font-* and --text-* in fonts.css)Variable name
utilitiesutilities.cssUtility name
variantsutilities.cssVariant name
basebase.cssSelector and property

The theme variables an item of the SlabUI registry uses (bg-muted, var(--focus)) are added to globals.css with the default values when they are missing. Values you already have are never changed.

When utilities.css or base.css does not exist, add creates it in styles/core/ and adds the @import to the global CSS.

An existing global CSS

When the project already has a global CSS, such as the app/globals.css of Next.js or the src/index.css of Vite, init replaces its content with the SlabUI template and saves the original next to it, as globals.css.bak (or .bak.1, .bak.2… when a copy already exists). In an interactive terminal it asks first; answer No to keep the file and only add the missing theme variables. With --yes, it replaces the file.

The global CSS stays where it is, and the imports point to styles/core/:

app/globals.css
@import "tailwindcss";
@import "../styles/core/fonts.css";
@import "../styles/core/base.css";
@import "../styles/core/utilities.css";

Copy what you still need from the .bak file (custom classes, extra imports) and delete it.

A project set up by an earlier version, with these files next to the global CSS or directly in styles/, keeps using them where they are.

update merges utilities, variants and base declarations like the variables: new ones are added, the ones you did not change follow the new version, the ones you changed are kept with a warning, and the ones removed in the new version are kept unless you confirm.

In the registry

An item lists the utilities and variants it uses by name. The build reads their code from the author's global CSS (css in the entry) and the local files it imports, so each utility is written once:

registry/index.ts
{
  name: "sidebar",
  kind: "ui",
  files: [{ path: "ui/sidebar.tsx", kind: "ui" }],
  utilities: ["no-scrollbar"],
  variants: ["fixed"],
  // Only when there is no other way: it changes the whole app.
  base: { body: { overflow: "hidden" } },
}

A utility or variant that is not in the author's CSS stops the build with an error. The theme variables a utility uses (@apply bg-muted) count as tokens of the item.