AZMARA PLATFORM
Guide

Tailwind CSS setup

What @azmr/ui needs from Tailwind CSS v4, how Tailwind finds the package's classes, and why class names are spelled out in full.

View as Markdown for coding assistants, or see llms.txt.

@azmr/ui is styled with Tailwind CSS v4. The package does not install Tailwind for you; your app provides it.

What you need

  • Tailwind CSS v4. The stylesheets use v4 directives (@theme inline and @source inline()), so v3 does not work.
  • A build step for Tailwind. With PostCSS (Next.js, for example), add Tailwind's plugin:
// postcss.config.mjs
export default {
  plugins: { "@tailwindcss/postcss": {} },
};

This setup, Next.js with PostCSS, is the one the package's own consumer test installs and builds. Tailwind's Vite plugin (@tailwindcss/vite) is its standard alternative; it is not covered by that test yet, and the getting-started guides will add it.

Import the stylesheet

In your global CSS, after Tailwind, import the stylesheet and the class lists for the components you use:

@import "tailwindcss";
@import "@azmr/ui/styles.css";
@import "@azmr/ui/classes/button.css";
@import "@azmr/ui/classes/input.css";

Use @azmr/ui/tokens.css instead of styles.css if your app already has global styles. The package page explains the difference, and the token reference lists everything the stylesheet defines.

How Tailwind finds the package's classes

Tailwind generates CSS only for the class names it finds in the files it scans. Your own files are scanned automatically, but the package's components are in node_modules, which Tailwind skips. So the package ships a class list for each entry point: @azmr/ui/classes/<entry>.css holds exactly the classes that entry uses, its shared code included, as one @source inline(...) rule. Import one for each entry you use, and Tailwind generates those classes and nothing else.

The lists are named after the entry points: @azmr/ui/button has @azmr/ui/classes/button.css, @azmr/ui/icons/lucide has @azmr/ui/classes/icons/lucide.css. To generate every component's classes, import the combined list instead:

@import "@azmr/ui/classes.css";

That is the simplest setup, but it costs CSS for components you never render. An app that uses Button and Input generates about 6 kB of CSS (gzip) with their two lists, against about 13 kB with the combined one.

The lists are generated when the package is built, from the same scanner Tailwind uses for @source, so they cannot fall out of step with the components. The package's build checks that the combined list generates exactly what a scan of all its JavaScript would, and that a Button and Input app stays within its CSS budget. Its consumer test installs the package into a fresh Next.js and Tailwind v4 app outside this repository, builds it, and confirms that classes which exist only inside the package's JavaScript are in the generated CSS. It also runs once with the class-list imports removed and expects that run to fail, so the check is known to detect the problem.

Classes in other packages of your own, such as a shared component library in a monorepo, need their own @source:

@source "../../packages/my-ui/src";

Spell class names out in full

Tailwind finds a class by reading it as text. A class built at runtime is never seen, so it silently produces no CSS:

// Not generated: "bg-success" never appears in the source as a whole.
const wrong = (tone: string) => `bg-${tone}`;

// Generated: every class appears in full.
const tones = {
  success: "bg-success",
  warning: "bg-warning",
  info: "bg-info",
} as const;

The package follows the same rule. Its responsive layout classes (Stack direction, Grid columns and spans) are tables of complete class strings, generated by a script, and a test recomputes the tables and fails if they drift. If you wrap a component and choose classes from a prop, use a lookup table like tones above.