Getting started

# Styling & CSS

Each site builds its own Tailwind + daisyUI stylesheet so the chrome is themed to match — and code blocks restyle light↔dark with the switcher, CSS-only.

## The canonical build

docs-kit ships no compiled CSS — you build it.

docs-kit ships **no compiled CSS**. Each site builds its own with the Tailwind CLI (run via Bun), so the `@source` globs can see **both** your app **and** the gem's Phlex components. Without the gem in scope, every class the shared chrome uses would be tree-shaken away.

The bundled `bin/build-css` resolves the docs-kit (and daisyUI) gem paths and adds them as extra `@source` entries, so you never hand-write a gem's install location.

```shell
bun run build:css     # one-shot, for deploys
bun run watch:css     # rebuild on change, for development
```

## application.tailwind.css

Your Tailwind entry point wires up daisyUI, the themes, and the sources.

The `themes:` list here **must match** `c.themes` in your initializer — the CSS build ships exactly those themes and the `ThemeSwitcher` offers exactly those names. A theme in one list but not the other is either a dead switcher entry or an unreachable build. This is the single most important invariant on this page.

```css
@import "tailwindcss";

/* daisyUI — the theme list MUST match DocsKit.configuration.themes. */
@plugin "daisyui" {
  themes: dark --default, light --prefersdark, synthwave, retro,
    cyberpunk, dracula, night, nord, sunset;
}

/* Your app's views + components + the gem's Phlex chrome.
   bin/build-css resolves the gem paths, so you never hard-code them. */
@source "../../../app/views/**/*.{rb,erb,haml,html,slim}";
@source "../../../app/components/**/*.rb";
@import "./tailwind.sources.css";  /* gem @source lines, generated */
```

The `--default` modifier picks the theme applied on first paint and `--prefersdark` the one used when the OS asks for a dark scheme. That block above is this very site's — its nine themes are the nine in `c.themes`.

> **Warning:** Interpolated Tailwind class names get tree-shaken. Always write **literal** class strings — e.g. `class: "badge badge-primary"`, never `class: "badge badge-\#{color}"`. The scanner can't see the built name, so the style never ships. New render-time classes (like the Drawer) need an `@source inline(...)` line.

## Adding a theme

Two edits and a rebuild — CSS block, config, done.

Themes come from daisyUI. To add one, keep the two lists in step:

1. Add the name to the `@plugin "daisyui" { themes: ... }` block in `application.tailwind.css`.
2. Add the same name to `c.themes` in `config/initializers/docs_kit.rb`.
3. Rebuild the CSS (`bun run build:css`).

First entry in `c.themes` is the page default; override with `c.default_theme`. See [Configuration](https://docs-kit.zoolutions.llc/docs/configuration) for the full theme surface.

```ruby
DocsKit.configure do |c|
  c.themes = %w[dark light synthwave retro cyberpunk dracula night nord sunset]
end
```

## Code highlighting: one light theme, one dark

Rouge highlights code; two config knobs make it follow the switcher.

`DocsUI::Code` highlights with [Rouge](https://docs-kit.zoolutions.llc/docs/languages) and injects its **own** inline theme CSS — no separate stylesheet asset. Which theme that CSS uses is config:

- `c.code_theme` — the **base** Rouge theme, emitted **un-scoped** so it applies under every daisyUI theme. Default `Rouge::Themes::Monokai`.
- `c.code_theme_dark` — an **optional** second Rouge theme. When set, `Code` additionally emits that theme's CSS scoped under `[data-theme=X] .code-highlight` for each shipped dark theme. daisyUI's more-specific `[data-theme]` selector wins, so code blocks restyle when the switcher lands on a dark theme. **CSS-only — no JS, no flash.** Default `nil` (single-theme, byte-for-byte backwards compatible).
- `c.dark_themes` — which theme names count as dark for that scoping. Defaults to the built-in daisyUI dark themes and is intersected with `c.themes` at render time, so only **shipped** dark themes emit CSS. A custom/branded dark theme must be listed here or its code CSS won't scope — docs-kit can't inspect the compiled daisyUI CSS to detect darkness.

**This site sets both.** Its initializer picks a light base and a dark override, so every code block on the page you're reading restyles as you flip the theme switcher between a light theme (`light`, `retro`, `cyberpunk`, `nord`) and a dark one:

```ruby
DocsKit.configure do |c|
  c.code_theme      = "Rouge::Themes::Github"  # light themes
  c.code_theme_dark = "Rouge::Themes::Monokai" # dark themes
  # c.dark_themes defaults to daisyUI's dark set; override only
  # for a custom dark theme the built-in list doesn't know.
end
```

Try it: switch the theme in the topbar and watch this next block change palette. It's the same highlighter, two scoped stylesheets.

```ruby
class Doc
  extend DocsKit::Registry

  path_prefix    "/docs"
  view_namespace "Views::Docs::Pages"

  page "Overview",       group: "Getting started"
  page "Styling & CSS",  group: "Getting started"
end
```

For this site, the shipped dark themes (the intersection of `c.dark_themes` and `c.themes`) are **dark, synthwave, dracula, night, sunset** — those five each get a `[data-theme=…]`-scoped Monokai block; the four light themes fall through to the un-scoped GitHub base.

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `c.code_theme` | String or Class | Rouge::Themes::Monokai | Base (light) Rouge theme, emitted un-scoped. |
| `c.code_theme_dark` | String, Class, nil | nil | Optional dark override, scoped per shipped dark theme. nil = single-theme. |
| `c.dark_themes` | Array<String> | daisyUI dark set | Which theme names count as dark; intersected with c.themes at render. |

> **Note:** A String theme name is resolved to its Rouge constant. A typo'd or unloaded name **degrades gracefully** — the base theme falls back to the default and a bad `code_theme_dark` simply emits no dark CSS, so a mistake never crashes a code block.

## Custom styles

Plain CSS, @apply, @layer, or extra stylesheets.

Add your own CSS below the imports in `application.tailwind.css` — plain rules, `@apply`, or `@layer` all work.

To pull in additional, separately-built stylesheets (linked after the Tailwind build), list their logical names via `c.stylesheets` in your initializer. Default is `%w[application]` — the Bun/Tailwind build.

```ruby
DocsKit.configure do |c|
  c.stylesheets = %w[application announcements]
end
```

Next: see [Languages](https://docs-kit.zoolutions.llc/docs/languages) for the Rouge lexer surface, [Components](https://docs-kit.zoolutions.llc/docs/components) for the kit `Code` and `Example` render live, and [Configuration](https://docs-kit.zoolutions.llc/docs/configuration) for every config knob in one place.