---
title: Styling a vzk site
description: 'Use this guide when changing how a vzk site looks: the classes its markup
  wears, the site''s stylesheet and tokens, and a file''s own `<style>`.'
sidebar:
  order: 12
---

Use this guide when changing how a vzk site looks: the classes its markup
wears, the site's stylesheet and tokens, and a file's own `<style>`. Start
from `describe_vocabulary topic=looks` for a whole stylesheet.

## Classes

Every class a file writes out is compiled into the site's stylesheet, so a
class name is one whole literal. A name joined from a value, such as
`"text-" + size` or `class="text-{size}"`, is refused.

```html
<article class={["card", lead && "card-lead"]}>
```

`class` takes a literal, or a list whose missing and `false` entries drop
out. Where a class varies with a value, choose between whole names with a
lookup:

```html
<h1 class={{ calm: "soft", bold: "loud" }[fields.tone]}>{fields.cta}</h1>
```

A `class` whose names cannot be read before a page is drawn (a row's value,
a function's answer) is a warning, `VZK0304`: no rule is made for those
names, so the element may be drawn unstyled. A visitor's text is never a
class.

A `class` given to a component lands on its root element:
`<Frame class="aspect-square rounded-surface" alt="Dan beside his van" />`.

## Tokens and component classes

The site's scale is its own `stylesheet.css`: read it with `read_files`.
Prefer its tokens to fixed numbers:

- spacing, used by utilities such as `gap-md` and `py-xl`;
- page rhythm such as `px-gutter`, `pt-band-top` and `pb-band-bottom`;
- measures such as `max-w-page`, `max-w-headline` and `max-w-lead`;
- colour roles such as `bg-surface`, `bg-raised`, `text-ink`, `text-body`
  and `text-muted`;
- type registers such as `text-display`, `text-title`, `text-lead`,
  `font-heading` and `font-strong`.

A component class is a decision the look owns: `btn-primary`, `btn-compact`,
`btn-outline`, `btn-block`, `link-underline`, `link-quiet`, `frame`,
`frame-empty` and `eyebrow`. A file never branches on which look is active:
a new look changes the stylesheet while the markup keeps the same tokens and
component classes. The primary responsive breakpoint is `md:`.

```html
<div id="prices" class="px-gutter md:px-gutter-lg pt-band-top md:pt-band-top-lg max-w-page mx-auto">
  <p class="eyebrow text-muted">Prices</p>
  <h1 class="font-heading font-strong text-display md:text-display-lg text-ink text-balance">What it costs, before I start.</h1>
</div>
```

## The stylesheet

`stylesheet.css` is one whole file, replaced by `write_files` on a branch:
font imports, tokens in `@theme`, look-owned components in `@utility`, base
rules in `@layer base`, and any site-wide rule utilities cannot express.
`@plugin`, `@config`, `@source`, `@reference` and non-URL imports are refused, and so is
Tailwind's built output. It must compile before the write is accepted.

## A file's own style

A file's `<style>` is CSS for the markup of that file and nothing else. It
comes after the markup:

```html
<script server>
  const { tone = "plain" } = props
</script>

<div class={["callout", tone == "warm" && "callout-warm"]}>
  <p class="callout-title"><slot name="title">Note</slot></p>
  <p><slot /></p>
</div>

<style>
  .callout {
    border-left: 4px solid currentColor;
    padding: 0.5rem 1rem;

    p {
      margin: 0;
    }
  }

  .callout-warm {
    border-color: #b45309;
  }

  .callout-title {
    font-weight: 700;
  }
</style>
```

- It is plain CSS, with nesting. A rule reaches only the markup written in
  its own file, wherever that is drawn, so two files may both style `.card`.
- A component's root is styled from the file that uses it by giving the
  component a class: `<Callout class="wide" />` and `.wide { ... }`.
- `:global(...)` is the way out. `.prose :global(p)` reaches what
  `markdown(...)` draws, and `.menu:global(.open)` a class a script adds.
- It may hold style rules, `@media`, `@supports`, `@container`, `@layer`
  with a block, `@starting-style`, `@keyframes`, and `@apply` inside a
  rule. `@import`, `@font-face` and `@property` belong in `stylesheet.css`.
- A selector that matches nothing in its file is a warning, and a style that
  cannot be read as CSS is an error.

Use a file's style for a rule one file alone owns, and the stylesheet for
what several files share. `style="width: {n}%"` on an element takes a number
or plain text as each value, for a visual value one element alone owns.
