DreamLake

Theming & CSS

Every color, radius, and font in the shell resolves through prefixed CSS custom properties declared in @dreamlake/dockit/styles.css. The --doc-template- prefix isolates the shell from the consuming app's own tokens; restyling the shell is overriding variables — no component edits.

The consumer stylesheet

A dockit site owns exactly one CSS file. This site's is the whole recipe:

styles/app.csscss
@import "tailwindcss";
@import "@dreamlake/dockit/styles.css";

/* Tell Tailwind where this site's own utility-class usage lives (the
   library's dist is covered by the @source inside its styles.css). */
@source "../pages";
@source "../examples";
@source "../renderer";

Line by line:

  • @import "tailwindcss" boots Tailwind v4 — no tailwind.config.js, everything is CSS-first.
  • @import "@dreamlake/dockit/styles.css" pulls in the shell's @theme token block, dark-mode overrides, and component styles. Order matters: the library must come after tailwindcss and before your own overrides.
  • Your @source lines point Tailwind at the directories where your markup uses utility classes, so those utilities get generated.
  • The library's stylesheet carries its own @source "./" at the top. Because @source resolves relative to the file it appears in, and the file ships as dist/styles.css, that line makes Tailwind scan the package's compiled JS in dist/ — classes used only inside dockit components are always emitted, without you listing the package yourself.

Color tokens

Declared in an @theme block, so each also mints Tailwind utilities (bg-doc-template-bg, text-doc-template-ink, …):

TokenLightRole
--color-doc-template-bg#fffefaPage background
--color-doc-template-panel#fcfbf7Cards, footer panels
--color-doc-template-rail#fcfbf7Sidebar / rail background
--color-doc-template-code#faf8f1Code block surface
--color-doc-template-ink#1a1a1aPrimary text
--color-doc-template-muted#6b6b6bSecondary text
--color-doc-template-faintrgb(0 0 0 / 0.08)Hairline borders
--color-doc-template-faint-strongrgb(0 0 0 / 0.16)Stronger borders
--color-doc-template-chiprgb(0 0 0 / 0.04)Chip fills
--color-doc-template-search#f5f4f0Search field fill
--color-doc-template-selected#d9e6f7Active sidebar link
--color-doc-template-accent#23aaffLinks, active TOC, highlights
--color-doc-template-accent-softrgb(35 170 255 / 0.09)Accent washes
--color-doc-template-warn#d68b3aWarnings, dev-mode dot
--color-doc-template-warn-softrgb(214 139 58 / 0.10)Warning washes
--color-doc-template-rail-stroke#d8d4c8TOC rail stroke

Radii: --radius-doc-template (10px) and --radius-doc-template-sm (6px).

Dark mode

A [data-theme='dark'] block re-declares the same custom properties — every utility and inline style swaps instantly, with zero re-render. The attribute is stamped on <html> by:

  1. the FOUC-safe inline script in the server-rendered head (first paint), and
  2. ThemeProvider afterwards (user choice, system tracking, cross-tab sync) — see ThemeToggle below.

A dark: Tailwind variant is wired to the same attribute for your own pages. Shiki code colors are dual-theme (github-light / github-dark) via CSS variables, so code follows along.

Fonts

TokenStackUsed for
--font-doc-template-uiInter Tight, ui-sans-serif, …Body, headings, links
--font-doc-template-monoFira Code, ui-monospace, …Code, chips, section labels, breadcrumbs

The server renderer emits a single Google Fonts <link> loading Inter Tight + Fira Code (the org-wide mono font).

Re-branding walkthrough

Restyling the shell is a stanza in your app.css, after the library import. Override the light values on :root and the dark values on [data-theme='dark']:

styles/app.csscss
@import "tailwindcss";
@import "@dreamlake/dockit/styles.css";

@source "../pages";
@source "../renderer";

/* Brand: violet accent, tighter radii, your own type. */
:root {
  --color-doc-template-accent: #7c5cff;
  --color-doc-template-accent-soft: rgb(124 92 255 / 0.09);
  --color-doc-template-selected: #e6dffb;
  --radius-doc-template: 6px;
  --font-doc-template-ui: "Instrument Sans", ui-sans-serif, sans-serif;
}

[data-theme='dark'] {
  --color-doc-template-accent: #a78bff;
  --color-doc-template-selected: #423a5c;
}

Every component — sidebar selection, TOC highlight, links, chips — re-resolves through the variables; there is nothing else to touch. If you swap a font stack, remember to also load the webfont (the renderer's default <link> only fetches Inter Tight and Fira Code).

ThemeToggle

The theme control comes in two variants, chosen by the themeToggle field in Site config:

  • 'cycle' (default) — a single button that steps light → system → dark, with a small spring animation on the icon swap.
  • 'segmented' — the three-button slider from the uikit/dreamlake docs: every theme visible at once, a pill indicator sliding under the active one, inactive icons leaning ±18°.
site.config.tsts
initDocs({
  site: { themeToggle: 'segmented' /* or 'cycle' */ },
  // …
})

This site is configured with 'segmented', so that is what the topbar shows. Here is another toggle, live — both stay in sync because they share the same ThemeProvider state:

Cycle light → dark → system:

<ThemeToggle /> takes no props — the variant is site-level config, not a per-instance choice. It must render inside the shell (or any ThemeProvider) — on its own it falls back to inert context defaults.

How theme state flows

  1. ThemeProvider (mounted by the Layout) keeps the choice in localStorage under doc:theme, synced across tabs and across every consumer of the hook.
  2. The provider stamps data-theme="light" | "dark" on <html>; every color token above re-resolves instantly.
  3. On first paint, a tiny inline script in the server-rendered <head> reads the same key before hydration, so a dark-mode reader never sees a light flash (FOUC-safe).

Reading the theme yourself

tsx
import { useTheme } from '@dreamlake/dockit'

function MyWidget() {
  const { theme, setTheme } = useTheme()   // 'light' | 'dark' | 'system'
  return <button onClick={() => setTheme('dark')}>Go dark ({theme})</button>
}