# Tailwind

> Use Tailwind freely together with LO-TEK, with the same values and nothing fighting over the order.

Tailwind is not required in LO-TEK, and it does not get in the way either. If you use it, you should be able to write `p-6` on an `.lt-button` and expect it to work, and build your own surfaces with LO-TEK’s colours without looking up a single variable name.

The package has no dependency on Tailwind. The theme is a plain CSS file, so it is the version in your app that reads it, and two versions cannot conflict.

## Setup

```css title="app.css"
@layer theme, base, lotek, lotek-theme, components, utilities;


@import "tailwindcss";
@import "@lo-tek/interfaces/tokens.css";
@import "@lo-tek/interfaces/tailwind.css";


/* and the stylesheets for the components you use */
@import "@lo-tek/interfaces/button.css";
```

The first line is the only thing you have to remember. Without it, Tailwind’s Preflight resets the components, or the components beat your classes. `lotek-theme` is the layer a [generated theme](/your-own-theme.md) goes in, and it must be there even if you do not have a theme yet. [Layer order](#layer-order) explains why.

That is all. No `tailwind.config.ts`, no preset to import in JavaScript.

### When LO-TEK and Tailwind are two stylesheets

If LO-TEK comes in as a `<link>`, and Tailwind from somewhere else, there is no shared entry file to put the line at the top of. Then it belongs in a `<style>` that comes first in `<head>`, before both:

```html
<head>
  <style>@layer theme, base, lotek, lotek-theme, components, utilities;</style>


  <link rel="stylesheet" href="/static/tailwind-built.css" />
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.36.0/dist/lotek.css" />
</head>
```

A `@layer` statement only creates layers that do not exist yet, and does not move those already declared. Tailwind declares `theme, base, components, utilities` on the first line of its own CSS, and LO-TEK’s `tokens.css` opens `@layer lotek`. The stylesheet that loads first therefore decides the order, and both orders are listed as wrong in the table below. The statement in `<head>` decides it before either file gets to declare anything, and then it does not matter which of the two stylesheets loads first. A [generated theme](/your-own-theme.md) you load as a `<link>` is such a stylesheet: it declares `@layer lotek, lotek-theme;` itself, so if it comes before the bundled CSS, the line belongs in `<head>` there too.

The theme with the `lt-` classes, `tailwind.css`, is a `@theme` block Tailwind’s compiler must read, and cannot be linked. If there is no compiler involved, you get the components and the tokens, but not `text-lt-l` and `bg-lt-neutral-canvas`.

## Overriding a component with ordinary classes

The components are in `@layer lotek`, and the utility classes come after. A Tailwind class therefore wins over the component’s own value, without `!important` and without beating specificity:

**Code**

```html
<button class="lt-button">Submit application</button>
<button class="lt-button w-full justify-center rounded-full">Submit application</button>
<button class="lt-button" data-variant="secondary">Save draft</button>
<button class="lt-button px-8 py-4 text-lt-l" data-variant="secondary">Save draft</button>
```

The button keeps its colours, focus indicator and disabled state. You change only what you point at.

## Your own surfaces with LO-TEK’s values

Everything the theme adds is called `lt-`, like the other classes in the system. That way you always know which values are LO-TEK’s, and Tailwind’s own are untouched: `bg-neutral-100` still means the same.

**Code**

```html
<article class="rounded-xl border border-lt-neutral-border-subtle bg-lt-neutral-canvas p-4 shadow-lt-overlay">
<div class="mb-2 flex items-center justify-between gap-2">
  <h3 class="text-lt-l font-semibold text-lt-neutral-text-strong">Housing benefit application</h3>
  <span class="lt-badge" data-color="success">Approved</span>
</div>
<p class="text-lt-neutral-text-subtle">Sent 4 March. Case number 2026-0481.</p>
<a class="lt-link mt-3 inline-block" href="#">View the decision</a>
</article>
```

The example above is not a drawing. It is compiled by a real Tailwind when the documentation is built, from exactly the markup in the code tab.

## What the theme gives you

The colours follow the matrix: every cell becomes a class, and the name is `<utility>-lt-<family>-<role>`.

| Classes | Value |
| - | - |
| `bg-lt-accent-fill`, `text-lt-accent-content` | Filled surface, and the text on it |
| `text-lt-accent-text`, `bg-lt-accent-surface` | Links, and a tinted accent surface |
| `border-lt-neutral-border`, `border-lt-neutral-border-subtle` | Borders and rules |
| `bg-lt-neutral-canvas`, `text-lt-neutral-text-strong`, `text-lt-neutral-text-subtle` | The page, body text and muted text |
| `bg-lt-success-surface`, `text-lt-success-text` | Status. The same for `warning`, `danger` and `visited` |
| `bg-lt-brand1-surface` | The brand colours, when the meaning does not exist yet |
| `text-lt-xxs` to `text-lt-mega` | Text sizes |
| `max-w-lt-aside`, `max-w-lt-wide-content` | Widths for page layout |
| `shadow-lt-overlay` | The shadow under surfaces that sit above the page |

The colours, text sizes and shadow point to the `--lt-` variables, so the colours switch with the theme. `bg-lt-neutral-canvas` is white in the light theme and nearly black in the dark one, without you writing `dark:`.

The widths are the exception. `max-w-lt-aside`, `max-w-lt-wide-content` and the other widths are fixed values, the same as `Containers` in `@lo-tek/interfaces/tokens`, because there is no `--lt-` variable for them. If you want other widths, add them to your own `@theme`.

### Spacing is the same

Tailwind computes `p-4` as `calc(var(--spacing) * 4)`, and the theme sets `--spacing` to `--lt-spacing-1`. `p-4` is therefore four times the base unit, which is `--lt-spacing-4`, the same value the components use.

The steps in the scale are separate values, not computed from the unit. If you set only `--lt-spacing-1`, the Tailwind classes follow, and so does whatever in the components uses the unit directly, such as the space between list items, while `--lt-spacing-4` and the rest of the components stay where they were. Then `p-4` is no longer the same as a button’s padding, and the components drift apart from each other. If you change the scale, rewrite all of it:

```css
:root {
  --lt-spacing-0-5: 0.15rem;
  --lt-spacing-1: 0.3rem;
  --lt-spacing-2: 0.6rem;
  --lt-spacing-3: 0.9rem;
  --lt-spacing-4: 1.2rem;
  --lt-spacing-5: 1.5rem;
  --lt-spacing-6: 1.8rem;
  --lt-spacing-7: 2.1rem;
  --lt-spacing-8: 2.4rem;
  --lt-spacing-10: 3rem;
  --lt-spacing-12: 3.6rem;
  --lt-spacing-16: 4.8rem;
}
```

Then both the components and the Tailwind classes follow. `--lt-spacing-px` is the fixed pixel and is outside the scale.

The breakpoints are deliberately left out of the theme. LO-TEK’s `sm` to `2xl` already have Tailwind’s values, so `md:` works as before.

## Layer order

Layers count in the order they are first mentioned, and the last one wins regardless of specificity. That is why the order is on the first line:

| Order | What happens |
| - | - |
| `lotek` before `base` | Preflight resets the components. The button loses its background. |
| `lotek` after `utilities` | The components beat your classes. `p-6` does nothing. |
| `lotek-theme` after `utilities` | A generated theme beats your classes. `font-mono` on `<html>` does nothing. |
| `base`, `lotek`, `lotek-theme`, `utilities` | Everything works |

`lotek-theme` is in the line even if you have no theme. A generated theme only declares `@layer lotek, lotek-theme;`, and a `@layer` statement does not move layers already declared. Without a place in the line, the theme therefore ends up last, after `utilities`, the day you add it.

The failure cases are tested in `customising.browser.test.ts`, which builds each of them in a separate document, the first one and the list test with Tailwind’s own Preflight, and reads what the button, the list and the root element actually get. The documentation site you are reading now uses the same layer order, but without Preflight: Starlight has its own base in the layer `starlight`, and the site loads only `theme.css` and `utilities.css` from Tailwind.

## Three things worth knowing

**No component uses shadow DOM.** Everything is in plain DOM, so utility classes and your own CSS reach everywhere.

**`@apply` works, but is rarely needed.** If you want to collect a repetition, an ordinary class in your own CSS is easier to read, and it can use `var(--lt-color-…)` directly.

**Preflight is Tailwind’s choice, not ours.** LO-TEK’s components reset what they need themselves, including `box-sizing`, so they work as well without Preflight as with it.

## Without Tailwind

None of this is required. `var(--lt-color-accent-fill)` and `class="lt-button"` work just as well in plain CSS, and in every other tool that writes CSS. See [Customising](/customising.md).
