# Theming > Override tokens, ship a brand logo, swap the layout variant, register a custom stylesheet. How to make a ShellDocs site look like yours. Source: https://shelldocs.shellui.dev/docs/theming ShellDocs is designed to be re-skinnable via CSS variables : the shipped `tokens.css` sets every color, radius, spacing, and font stack, and the entire chrome cascades from there. Override the tokens, and every primitive follows. ## The token file `ShellDocs.Tokens` ships one CSS file : `_content/ShellDocs.Tokens/tokens.css`. It declares two `:root` blocks : the light-mode defaults and the `:root.dark` overrides that kick in when the theme toggle is set to dark. > **Where the tokens come from** > > Each variable value is set via `hsl(...)` or `oklch(...)` for perceptual uniformity. The shadcn-compatible naming (`--background`, `--foreground`, `--primary`, `--muted`, …) means any component library that speaks the same vocabulary drops in without extra mapping. ## Overriding tokens Add a stylesheet **after** `tokens.css` in your `App.razor`: ```html ``` Then override any token in `brand.css`: ```css :root { --primary: oklch(0.55 0.22 240); --radius: 0.75rem; --font-sans: "Söhne", ui-sans-serif, system-ui, sans-serif; } :root.dark { --primary: oklch(0.75 0.18 240); } ``` The cascade takes care of the rest : buttons, links, focus rings, the sidebar highlight, and every callout all pull from `--primary` and update immediately. ## The full token surface | Name | Type | Default | Description | |------|------|---------|-------------| | `--background` | `color` | | Page background. | | `--foreground` | `color` | | Default text color. | | `--card` | `color` | | Card / elevated surface background. | | `--card-foreground` | `color` | | Text on card surfaces. | | `--popover` | `color` | | Popover / floating menu background. | | `--popover-foreground` | `color` | | Text inside popovers. | | `--primary` | `color` | | Primary action color : buttons, links, highlights. | | `--primary-foreground` | `color` | | Text on primary surfaces. | | `--secondary` | `color` | | Secondary surface / tag background. | | `--secondary-foreground` | `color` | | Text on secondary surfaces. | | `--muted` | `color` | | Subtle background : hover states, code blocks. | | `--muted-foreground` | `color` | | Secondary text, hints, timestamps. | | `--accent` | `color` | | Focus rings, hover accents. Semi-transparent. | | `--accent-foreground` | `color` | | Text on accent surfaces. | | `--border` | `color` | | Default border color. | | `--border-strong` | `color` | | Prominent border : dividers between sections. | | `--input` | `color` | | Form input background. | | `--ring` | `color` | | Focus-ring outline color. | | `--info` | `color` | | Info callout accent. | | `--warning` | `color` | | Warning callout accent. | | `--error` | `color` | | Error / danger callout accent. | | `--success` | `color` | | Success / tip callout accent. | | `--radius` | `length` | | Base border-radius. Chrome uses calc(var(--radius) + N) for varied roundness. | | `--sidebar-width` | `length` | | Sidebar column width. | | `--toc-width` | `length` | | Right-rail table-of-contents width. | | `--header-height` | `length` | | Top header height (TopNav variant). | | `--font-sans` | `stack` | | Sans-serif stack for prose + UI. | | `--font-mono` | `stack` | | Monospace stack for code + inline code. | ## Dark mode Dark mode is the `dark` class on the `html` element. The theme toggle flips it and saves the choice in `localStorage` under the key `shelldocs-theme`. The toggle is handled by `shelldocs.js`, so it works on a static export. The class is the source of truth. If something else flips it, such as a component library's own theme switch, `shelldocs.js` notices, saves the new value, and keeps the toggle icon in step. An inline script in `App.razor` reads that key on page load and applies the class **before first paint** : this is what prevents the light-mode flash on hard refresh. The script is added by `shelldocs init` automatically. To only override dark values, scope your rules: ```css :root.dark { --primary: oklch(0.75 0.18 240); --background: hsl(220, 15%, 8%); } ``` ## Brand logo Set via `ShellDocsOptions.LogoSvg` (inline SVG, uses `currentColor` : recolors with the theme) or `LogoLight` / `LogoDark` (URL pair for pre-rasterized bitmaps or full-color SVGs). An inline SVG is sized to `LogoHeight` with its width following its `viewBox`, so the file needs no `width` or `height` of its own. ```csharp // Inline SVG that recolors with theme o.LogoSvg = File.ReadAllText(Path.Combine(builder.Environment.WebRootPath, "brand/logo.svg")); // Or theme-specific images o.SetLogo("/img/logo-light.svg", "/img/logo-dark.svg", alt: "MyBrand"); ``` > **Prefer inline SVG** > > An inline `LogoSvg` with `stroke="currentColor"` swaps between light and dark for free : one file, both themes. Reach for `SetLogo` only when your brand needs specific hex colors that shouldn't shift. Before `0.1.17-alpha` the rule that sizes an inline logo never matched, and an SVG without a size of its own could render 0px wide. A site that added its own `.brand-logo-svg > svg` rule for that can remove it. ## Layout variant `ShellDocsOptions.LayoutVariant` picks between the two chrome layouts: | Name | Type | Default | Description | |------|------|---------|-------------| | `TopNav` | `default` | | Classic header spanning the full width, sidebar below. Best for wide catalog-style docs. | | `Sidebar` | `alt` | | Floating sidebar card with brand + search + collapse baked in. No top header. Best for narrower, chapter-style docs. This site uses it. | ```csharp o.LayoutVariant = DocsLayoutVariant.Sidebar; ``` Both layouts share the same token surface : switching is a one-line change. ## Custom fonts `--font-sans` and `--font-mono` are the two font tokens. Load your webfont via `@font-face` in a stylesheet loaded after `tokens.css`, then override the token: ```css @font-face { font-family: "MySansFont"; src: url("/fonts/my-sans.woff2") format("woff2"); font-weight: 400 900; font-display: swap; } :root { --font-sans: "MySansFont", ui-sans-serif, system-ui, sans-serif; } ``` Every ShellDocs primitive picks it up automatically : no per-component override needed. ## The `--theme` flag `shelldocs init --theme` and `ShellDocsOptions.Theme` accept `shadcn`, `fuma` and `nextra`, but have no effect in this release. Every site starts from the same `tokens.css`. Restyle with token overrides. ## Keeping page typography out Page content is styled through the `shelldocs-prose` class: paragraph spacing, list padding, heading sizes, link underlines. Those rules skip any element with the `not-prose` class and everything inside it. Preview frames carry it already, so components in examples are not restyled. Add it to your own markup when you drop custom HTML into a page and want it left alone: ```html