# Theming

Make blog content look native to your site. The entire MD2 component system
reads **fifteen color tokens** (plus a corner radius) — set them once per
blog and every callout, chart, step guide, and badge follows.

## The theme editor

On the blog page, the **MD2 theme** card has grouped pickers:

| Group | Tokens |
|---|---|
| Accents | info · warning · danger · success · note |
| Tints | a soft background per accent (sits behind text) |
| Surfaces | background · soft surface · border |
| Text | text · muted |
| Radius | corner rounding for panels |

A live sample re-renders as you pick, so you see callouts, tints, and
borders on your palette before saving. **Save theme** applies it to your
live pages within a minute; **Remove theme** returns to the neutral
default. If your site has a light/dark toggle, flip **Dark variant** on to
maintain a palette per scheme — the "Sites with a light/dark toggle"
section below explains how the pair is served.

:::callout{severity=info title="Setting a token from your own CSS? Two names don't match the picker"}
Most tokens map directly to their CSS variable: `info` → `--md2-info`,
`warning` → `--md2-warning`, `success` → `--md2-success`, `bg` →
`--md2-bg`, `bg_soft` → `--md2-bg-soft`, `border` → `--md2-border`, `text`
→ `--md2-text`, `muted` → `--md2-muted`. Two accents don't: the picker's
**Danger** is `--md2-blocking`, and **Note** is `--md2-nit`. Every accent
also has a `-bg` tint variant (`--md2-blocking-bg`, `--md2-nit-bg`, …).
:::

## Propose from your website

Skip the manual work: **Propose from website** scans your site's real
design — the homepage, its stylesheets, declared theme color, and font
stacks — and has AI map your existing palette onto the fifteen tokens, with
a one-line rationale for the choices.

::::steps

:::step[Scan]
baas fetches your homepage and up to three of its stylesheets and
inventories the colors you actually use, ranked by frequency.
:::

:::step[Propose]
The model keeps your light-or-dark scheme, reuses your accent colors where
they fit, and builds readable tints — warning stays warm, danger reads as
danger, text stays high-contrast.
:::

:::step[Review and apply]
The proposal appears with color swatches. **Load into editor** fills the
pickers — tweak anything, then save. Nothing changes on your site until
you save.
:::

::::

## How it's delivered

The palette ships as a small scoped CSS block with your content — inlined
on server-rendered pages, injected once by the SDK. Colors are validated
hex values, so the block is inert beyond setting your tokens.

:::callout{severity=info title="Overriding tokens yourself: two rules"}
The tokens are plain CSS custom properties, so you can re-point any of them
from your own stylesheet. Two things decide whether your rule actually
takes effect.

**Beat (0,1,0).** Every block we inject declares its tokens at specificity
(0,1,0) on `.baas-wrap` and `[data-baas-blog]` — the same weight in every
colour scheme. But we inject at runtime, after your build-time CSS, so a
tie goes to us: your rule needs (0,2,0) or better. One ancestor plus the
hook does it — `.my-blog .baas-wrap`.

**Name the wrapper, not just your container.** A custom property declared
*on* an element beats one inherited from an ancestor, however specific the
ancestor's rule — and we declare ours on `.baas-wrap`. Setting `--md2-*` on
your own container, which is its parent, does nothing at all.
:::

## Sites with a light/dark toggle

A blog can store a **light/dark pair** instead of a single palette. Flip
**Dark variant** on in the theme editor: the pickers split into Light and
Dark tabs, both palettes preview side by side, and **Swap light ↔ dark**
trades the two sets (handy when your existing single palette was scanned
from your dark theme). With the toggle off, the single palette applies to
every visitor, exactly as before.

Which set a reader gets is decided in this order, first match winning:

1. **`data-baas-theme="light"` or `"dark"` on the blog container** — you
   telling us outright what this blog is, whatever the page around it does.
2. **Your own `data-theme="light"` / `"dark"`**, on `<html>`, on a wrapper,
   or on the container. This is the conventional way sites theme
   themselves, so a site with a normal theme toggle re-themes the blog for
   free, with no CSS of yours involved.
3. **The reader's system preference** (`prefers-color-scheme`).
4. Failing all of those, the light set.

**Propose from website** understands toggles too: when the scan finds
`prefers-color-scheme` media queries or `data-theme` rules in your CSS, it
inventories each scheme's colors separately and proposes both sets in one
pass. Review both tabs before saving, as with any proposal.

:::callout{severity=info title="We never write data-theme on your page"}
`data-theme` is the web's conventional theming hook, and sites that use it
usually declare a whole palette with a bare `[data-theme="light"] { … }`
rule — a selector that matches *any* element carrying the attribute. So we
never put it on the markup we inject: the attribute we set and read on our
own wrapper is `data-baas-theme`. Your theme rules cannot capture the blog
subtree, and you do not have to rewrite them.
:::

:::callout{severity=warning title="Toggles that don't use data-theme"}
If your site switches schemes another way — a `.dark` class on `<html>`,
a custom attribute — only the system preference is visible to the injected
CSS, so a forced toggle can drift out of sync. Bridge it from your own CSS.
Name the wrapper as well as the container, and lead with an ancestor so the
rule clears (0,1,0):

```css
/* Example: site toggles via a .dark class; re-point tokens yourself */
:root.dark .my-blog[data-baas-blog],
:root.dark .my-blog .baas-wrap {
  --md2-bg: #101720;
  --md2-bg-soft: #16202b;
  --md2-border: #2b3a4a;
  --md2-text: #e8eef4;
  --md2-muted: #93a1b0;
}
```

Re-point whichever of the 15 tokens your content uses (the `*-bg` washes
included); check both themes on a post with callouts and panels.
:::

## Letting your site own the palette

If your site already has a token layer, point ours at it once and the blog
re-themes with the site forever — no second palette to maintain, and no
scanning to redo when the brand moves. Set `data-baas-theme="inherit"` on
the container to stand the blog's configured palette down entirely, then
supply the tokens yourself:

```html
<div data-baas-blog data-baas-theme="inherit" data-key="pk_…" …></div>
```

```css
.my-blog[data-baas-blog],
.my-blog .baas-wrap {
  --md2-text: var(--text-body);
  --md2-muted: var(--text-muted);
  --md2-border: var(--border-neutral);
  --md2-bg: var(--surface-raised);
  --md2-bg-soft: var(--surface-card);
  --color-base-100: var(--surface-raised);   /* fallbacks MD2 also reads */
  --color-base-200: var(--surface-card);
  --color-base-300: var(--border-neutral);
  --color-base-content: var(--text-body);
}
```

Because your own variables do the work, both of your themes are covered by
one block. Surfaces and text are what need this treatment; the five accent
hues are mid-tone and read on either canvas, so leaving them to the blog's
palette is usually right.
