Theming

Theming is server-driven (htmx + Alpine in production). The framework only owns the CSS contract — two attributes on <html> (or any container):

Built-in primaries

Each preset is its own stylesheet — load only the one in use. The default is bundled into bf.core.css; loading a preset sheet (eg bf.theme.mint.css) declares [data-bf-primary="mint"], then the host page sets the attribute.

Default
Mint
Crimson
Orchid
Ember

Note: these tiles only switch their own subtree because data-bf-primary is set on the wrapper. The shipped preset stylesheets must be loaded via <link> for the variables to apply at runtime — data-bf-primary="default" always works since it ships in core.

Custom (host page picks any color)

Load bf.theme.custom.css once, then bind --bf-custom-primary from your form. The 11-stop ramp is derived in CSS — no JS needed.

<html data-bf-primary="custom" style="--bf-custom-primary: #ec4899">
Live preview

Light / dark

light
Page surface
dark
Page surface

Rounding

The whole framework's radii multiply by --bf-radius-scale. Pick a named preset (square = 0, sharp = 0.5, default = 1, soft = 1.5, round = 2.25) or set the scale directly.

Custom value:

Switching presets affects everything that uses a radius token — buttons, inputs, cards, modals, badges, sidebar items. Try a preset, then revisit the other example pages and watch the framework re-shape itself.

Server-rendered switcher (htmx + Alpine)

<!-- 1. attribute on <html> is rendered by the server from the user's settings -->
<html lang="en" data-bf-theme="dark" data-bf-primary="mint">

<!-- 2. user picks a new color in a settings form; the form posts to /me/theme -->
<form
  hx-post="/me/theme"
  hx-trigger="change"
  hx-swap="none"
  x-data="{ primary: 'mint' }"
>
  <input type="radio" name="primary" value="default" x-model="primary"> Default
  <input type="radio" name="primary" value="mint"    x-model="primary"> Mint
  <input type="radio" name="primary" value="crimson" x-model="primary"> Crimson
</form>

<!-- 3. server response sets the attribute via hx-swap-oob or returns
        an updated <html> root via boost -->