Tailwind in a design

The Tailwind CSS v4 classes and theme of a design.

Knighn designs use Tailwind CSS v4. The file renders with the Tailwind browser build, and the editor uses the same compiler. Classes are the stored truth: the properties panel reads them and writes them. Write classes the way that a careful developer writes them.

Spacing

One spacing step is 4 px. The step number is the size divided by 4.

Class Size Class Size Class Size
p-px 1 px p-4 16 px p-16 64 px
p-0.5 2 px p-5 20 px p-20 80 px
p-1 4 px p-6 24 px p-24 96 px
p-1.5 6 px p-8 32 px p-32 128 px
p-2 8 px p-10 40 px p-40 160 px
p-3 12 px p-12 48 px p-64 256 px
  • The same steps work for m-*, gap-*, w-*, h-*, size-*, inset-*, space-* and leading-*.
  • Tailwind v4 accepts each whole step, not only the steps in the table: w-90 is 360 px, and h-13 is 52 px.
  • Keep to a rhythm: 4, 8, 12, 16, 24, 32, 48, 64, 96. Use gaps of 8 to 16 in a component, 24 to 48 between groups and 64 to 128 between sections.

Type

Class Size Line height
text-xs 12 px 16 px
text-sm 14 px 20 px
text-base 16 px 24 px
text-lg 18 px 28 px
text-xl 20 px 28 px
text-2xl 24 px 32 px
text-3xl 30 px 36 px
text-4xl 36 px 40 px
text-5xl 48 px 1
text-6xl 60 px 1
text-7xl 72 px 1
text-8xl 96 px 1
  • Weights: font-normal 400, font-medium 500, font-semibold 600, font-bold 700.
  • Line height: leading-none 1, leading-tight 1.25, leading-snug 1.375, leading-normal 1.5, leading-relaxed 1.625. A step also works: leading-7 is 28 px. text-lg/7 sets the size and the line height together.
  • Letter spacing: tracking-tighter -0.05em, tracking-tight -0.025em, tracking-wide 0.025em, tracking-wider 0.05em, tracking-widest 0.1em.
  • For headings, use tracking-tight and text-balance. For paragraphs, use text-pretty and a measure of 60 to 75 characters: max-w-prose or max-w-xl.
  • Body text is 16 px on desktop and mobile. Secondary text is 14 px. Do not use text smaller than 12 px.

Radius, shadow and colour

  • Radius: rounded-xs 2 px, rounded-sm 4 px, rounded-md 6 px, rounded-lg 8 px, rounded-xl 12 px, rounded-2xl 16 px, rounded-3xl 24 px, rounded-full. A style can change sm to xl with tokens.
  • Shadow: shadow-2xs, shadow-xs, shadow-sm, shadow-md, shadow-lg, shadow-xl, shadow-2xl. Use a small shadow or a ring, not both at full strength.
  • Borders and hairlines: border border-border, or ring-1 ring-border, which does not change the size of the box. Write ring-1, because ring alone is 1 px in v4 and 3 px in v3.
  • Colour: use tokens such as bg-surface and text-muted-foreground. See tokens.md. The default palette also works: bg-emerald-500, text-zinc-600. The opacity modifier works on both: bg-accent/10, bg-black/5.
  • Gradients: bg-linear-to-b from-accent/20 to-transparent. v4 names them bg-linear-*, bg-radial and bg-conic.

Arbitrary values

Knighn writes classes with the same rules when you edit in the properties panel. Follow them, so that the whole file has one style:

  • For a spacing or a size, use a step when the size divided by 4 is a whole number: w-280 is 1120 px, h-120 is 480 px, and max-w-160 is 640 px. Use px for 1 px.
  • Use an arbitrary value for a size that is not on the scale: h-[38px], w-[134px].
  • For radius, shadow, weight, line height and letter spacing, use the name only when the value is the same as the theme value. Use an arbitrary value for other values: rounded-[10px], leading-[1.05], tracking-[-0.02em].
  • For type sizes off the scale, use an arbitrary value: text-[56px].
  • For a print design, use print units: text-[10pt], leading-[14pt], pt-[22mm], w-[210mm]. Do not change them to px by hand. CSS draws 96 px for each inch, so 10pt is 13.33 px and 22mm is 83.15 px. Knighn keeps the unit.
  • For a colour that is not a token and that appears only one time, use an arbitrary value: bg-[#ff4f1f]. A colour that appears more than one time is a token.
  • For grid tracks, use an arbitrary value: grid-cols-[240px_1fr]. Use _ for a space.
  • For aspect ratios, use aspect-video (16 by 9), aspect-square, or an arbitrary value such as aspect-[4/3].

Do not write p-[16px] for p-4, or w-[1120px] for w-280.

A size class such as text-sm sets the font size and the line height. When you change only the size in the properties panel, Knighn writes an arbitrary value, such as text-[14px], so that the line height stays. Both are correct.

Layout

  • Stack: flex flex-col gap-4. Row: flex items-center gap-3.
  • Columns: grid grid-cols-3 gap-6. A card that spans two columns: col-span-2.
  • Content column: mx-auto w-full max-w-6xl px-6.
  • Push items apart: justify-between, or ml-auto on the last item.
  • Overlays: relative on the parent, absolute inset-0 on the overlay.
  • Long text in a row: min-w-0 on the text box and truncate on the text.
  • Images: size-full object-cover in a box that has a size.

Sizes map to classes like this:

Size In a row In a column
Fixed A step, such as w-80 (320 px), or w-[322px] off the scale A step, such as h-50 (200 px)
Hug no width class, or w-fit no height class
Fill flex-1, and min-w-0 when text can truncate self-stretch or w-full

Container variants

Each frame with a width is a container. Container variants follow the width of the frame, not the width of the window. So one component works in a 390 px frame and in a 1440 px frame.

  • Write the base classes for the narrow frame first. Add container variants for wider frames.
  • @3xl:px-16 applies when the frame is 768 px or wider. @max-3xl:hidden applies when it is narrower than 768 px.
  • Do not use viewport variants such as md:. See the table below.
Viewport variant Container variant Starts at
sm: @min-[40rem]: 640 px
md: @3xl: 768 px
lg: @5xl: 1024 px
xl: @7xl: 1280 px
2xl: @min-[96rem]: 1536 px
max-md: @max-3xl: below 768 px
max-lg: @max-5xl: below 1024 px

The container sizes, in px:

Variant Size Variant Size Variant Size
@3xs 256 @lg 512 @4xl 896
@2xs 288 @xl 576 @5xl 1024
@xs 320 @2xl 672 @6xl 1152
@sm 384 @3xl 768 @7xl 1280
@md 448

Be careful: @md: is 448 px, not 768 px. For the tablet change, use @3xl:.

What applies at the usual frame widths:

  • 390 px (mobile): the base classes, @xs: and @sm:.
  • 834 px (tablet): up to @3xl:.
  • 1440 px (desktop): up to @7xl:.
<section className="grid grid-cols-1 gap-6 px-6 py-16 @3xl:grid-cols-2 @3xl:px-12 @5xl:grid-cols-3 @5xl:py-24">
  <div className="rounded-xl bg-surface p-6 ring-1 ring-border">One</div>
  <div className="rounded-xl bg-surface p-6 ring-1 ring-border">Two</div>
  <div className="rounded-xl bg-surface p-6 ring-1 ring-border">Three</div>
</section>

Nested containers

@container on an element makes it the container for its descendants. Container variants inside it follow that element, not the frame. Use it for a card that changes with its own width.

<article className="@container rounded-xl bg-surface p-6 ring-1 ring-border">
  <div className="flex flex-col gap-4 @md:flex-row @md:items-center">
    <div className="aspect-video rounded-lg bg-muted @md:w-48" />
    <p className="text-muted-foreground">The layout of this card follows the width of the card.</p>
  </div>
</article>

Give a container a name when containers are nested: @container/card and @md/card:flex-row.

Things that break in frames

All frames share one document in the editor. So classes that follow the viewport follow the editor window, and Knighn warns about them.

  • Do not use h-screen, min-h-screen, w-screen or h-dvh, or vh and vw units. Fill the frame with size-full or min-h-full, and give the frame a height on the canvas.
  • Do not use fixed. Use absolute in a relative root, or keep the element in the flow.
  • Do not use sticky for a header. Put the header at the top of the flow.
  • For a width that follows the frame, use cqw: w-[50cqw].

States

The canvas shows one state of each screen. hover:, focus: and active: classes stay in the file and show in Preview mode only. To show a state in the design:

  • Hover, pressed or disabled on a component: add a variant prop, such as state?: 'default' | 'hover' | 'disabled' with AddVariant, and put the instances side by side.
  • A tooltip: await AddTooltip(id, 'Refresh data', { side: 'bottom' }) puts it next to the layer.
  • An open menu, a hover on a screen, or a confirmation step: AddState(screen, 'Delete confirm') copies the screen beside it as a new screen. Then change the copy: give changes such as { 'Delete button': { text: 'Confirm' } }, or Insert the menu or the dialog into its root as an absolute layer.
  • Keep the base screen as it is, and name each state for what it shows: Hover, Menu open, Delete confirm.

Dark mode

Dark mode is a mode of the theme mode set. [data-theme="dark"] in the theme block changes the tokens, and the dark: variant follows the same attribute:

@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
  • Use tokens for colours that change: bg-background text-foreground. They change with the mode, so you need no dark: classes.
  • Use dark: only for a small change that tokens do not cover: dark:invert on a black logo.
  • To show a frame in dark mode, give it modes: { theme: 'dark' }, or use Place(…, { modes: { theme: 'dark' } }).
  • To show one part of a screen in dark mode, put data-theme="dark" on its element.
<section data-theme="dark" className="flex flex-col gap-4 bg-background px-6 py-16 text-foreground">
  <h2 className="text-3xl font-semibold">Built for the night shift</h2>
  <p className="text-muted-foreground">This section is dark in a light screen.</p>
</section>

Class order

The order of classes is not important. Knighn sorts them in the Tailwind order when it saves. Knighn keeps classes that Tailwind does not know, and shows them as chips.