# Styling

> Global CSS, Tailwind, and static assets in Koze

Package: Koze
Canonical: https://kuratchi.dev/docs/koze/styling
Markdown: https://kuratchi.dev/docs/koze/styling.md

Koze is Vite-first. CSS uses plain Vite plugins — Tailwind, PostCSS, CSS Modules, Lightning CSS — you install them, wire them into `vite.config.ts`, and they run on the global stylesheet end-to-end. No Koze-specific CSS config.

## The two CSS paths

| You want... | Put it here | What happens |
| --- | --- | --- |
| A stylesheet Vite should **process** (Tailwind, PostCSS, CSS Modules, imports from `node_modules`…) | `src/app.css` | Koze registers it as a Rollup input. Every Vite CSS plugin runs on it. Emitted as a hashed asset. `<link>` auto-injected into the shell. |
| A static file Vite should **not touch** (prebuilt CSS, favicons, images, fonts, JSON, PDFs…) | `src/assets/` | Served verbatim by Wrangler's ASSETS binding. Never processed. Reference it with a plain absolute URL. |

One convention, zero config.

## `src/app.css` — global stylesheet

Drop a file at `src/app.css`. Koze wires it up automatically:

1. Registered as a client Rollup input, so every Vite CSS plugin processes it.
2. Emitted as a content-hashed asset (e.g. `/assets/koze-global-css-BLFtAlQt.css`) in production.
3. `<link rel="stylesheet" href={...}>` is injected right before `</head>` in your app shell. You don't write the `<link>` tag.

In development, the same file is served through Vite's module graph, so plugin transforms (Tailwind, PostCSS, CSS Modules, live reload) run on every edit.

```css
/* src/app.css */
body {
  margin: 0;
  font-family: system-ui, sans-serif;
  background: #0a0e17;
  color: #e6e9ef;
}
```

That's it. No build step, no Koze config.

> **Note:**
You can still author `<link rel="stylesheet">` tags in `src/app.koze` for third-party stylesheets (Google Fonts, CDN libraries). Koze only auto-injects the one pointing at `src/app.css`.

## Tailwind CSS

Tailwind v4 is a Vite plugin. Install it alongside any companion plugins:

```bash
npm install -D tailwindcss @tailwindcss/vite daisyui @tailwindcss/forms
```

Wire it into Vite:

```ts
// vite.config.ts
import { defineConfig } from 'vite';
import { koze } from '@kuratchi/koze/vite';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
  plugins: [koze(), tailwindcss()],
});
```

Declare Tailwind + plugins + theme in your global stylesheet:

```css
/* src/app.css */
@import "tailwindcss";
@plugin "daisyui";
@plugin "@tailwindcss/forms";

/* Tailwind v4 default source detection skips the .koze extension.
   Tell it explicitly to scan route files for utility usage. */
@source "./routes/**/*.koze";

@theme {
  --color-primary: #0ea5e9;
  --font-display: 'Inter', sans-serif;
}
```

Use utilities in any route:

```html
<button class="btn btn-primary">Click me</button>
```

That's the full integration. No `kuratchi.config.ts` entries, no CLI step, no watch process.

> **Warning:**
When scanning route files with Tailwind, the `@source "./routes/**/*.koze"` directive is required. Without it Tailwind won't see utility classes used in your templates and will purge them from the output.

## Static assets — `src/assets/`

Anything in `src/assets/` is served verbatim. It's Wrangler's ASSETS binding in production and Vite's publicDir in dev. No plugin ever transforms these files.

Use this for:

- Prebuilt CSS from a CDN-style library (`bootstrap.min.css`, `pico.min.css`)
- Favicons and app icons
- Static images, fonts, JSON, PDFs
- Anything you want served byte-for-byte

```text
src/
├─ app.css                 → processed by Vite
└─ assets/
   ├─ favicon.ico          → /favicon.ico
   ├─ hero.png             → /hero.png
   ├─ fonts/inter.woff2    → /fonts/inter.woff2
   └─ bootstrap.min.css    → /bootstrap.min.css  (served as-is)
```

Reference them with absolute URLs:

```html
<!-- src/app.koze -->
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="/bootstrap.min.css" />
```

> **Tip:**
Rule of thumb: **processed → `src/app.css`. Verbatim → `src/assets/`.** If Tailwind or PostCSS needs to see it, it goes in `src/app.css` (or is imported from there). If it's a finished file you want at `/<path>`, drop it in `src/assets/`.

## CSS Modules

Vite supports CSS Modules out of the box. Import them from server-side modules:

```css
/* src/server/styles/card.module.css */
.card {
  padding: 1rem;
  border-radius: 8px;
}
```

```ts
// src/server/card.ts
import styles from './styles/card.module.css';
export { styles };
```

## PostCSS

PostCSS runs automatically when a `postcss.config.js` exists at the project root:

```js
// postcss.config.js
export default {
  plugins: {
    autoprefixer: {},
  },
};
```

## Lightning CSS

Vite can use Lightning CSS as its CSS transformer:

```ts
// vite.config.ts
import { defineConfig } from 'vite';
import { koze } from '@kuratchi/koze/vite';

export default defineConfig({
  plugins: [koze()],
  css: {
    transformer: 'lightningcss',
  },
});
```

## CSS frameworks (Bootstrap, Pico, Bulma, …)

Two options. Pick one per library.

**Option A — process via `@import` (recommended when installed as a package):**

```css
/* src/app.css */
@import "bootstrap/dist/css/bootstrap.min.css";

.my-customizations { /* ... */ }
```

Vite resolves the import through `node_modules`. The final bundle is a single hashed CSS asset.

**Option B — drop prebuilt CSS in `src/assets/` (recommended when you want a stable URL):**

```bash
cp node_modules/@picocss/pico/css/pico.min.css src/assets/pico.css
```

```html
<!-- src/app.koze -->
<link rel="stylesheet" href="/pico.css" />
```

## Migrating from the old `css` config

Older Koze projects used a `css.tailwind` / `css.plugins` option in `kuratchi.config.ts`. That config file no longer exists. The migration:

1. Install `@tailwindcss/vite` and add it to `vite.config.ts`.
2. Move `src/assets/app.css` → `src/app.css`.
3. Delete `kuratchi.config.ts` (every block has a new home — see [Configuration](/docs/koze/configuration#migrating-from-kuratchi-config-ts)).
4. Remove `<link rel="stylesheet" href="/assets/app.css" />` from your shell — Koze injects it now.

## Best practices

**One global stylesheet**
    `src/app.css` is the single Vite-processed entry. Import everything else from it.
**Assets stay verbatim**
    `src/assets/` is for finished files served at stable URLs. Never put Vite-processed CSS here.
**Use CSS custom properties**
    Define colors, spacing, and typography as variables for theming and consistency.
**Add plugins at Vite layer**
    Tailwind, PostCSS, Lightning CSS — all plain Vite plugins. No framework config needed.
