Migrating to v0.14.0

Migrating to v0.14.0

v0.14.0 is the foundation milestone for refrakt v1.0. It lands the typed design tokens contract (SPEC-048), the new prism brand mark (SPEC-050), the tint vocabulary alignment (SPEC-053), and the Lumina palette refresh (SPEC-051).

This page covers the breaking changes a user-facing site or a plugin author needs to address when upgrading.

Lumina ships a new neutral default palette (SPEC-051)

The biggest visible change. Lumina's defaults moved from cream-and-navy to a quiet warm-neutral palette designed to disappear behind your content. Body background is now #f6f4ef (warm off-white) with #1c1a17 text; status, syntax, and surface tokens are all retuned to muted earthy values. Typography switched from Outfit to Inter (body) and JetBrains Mono (code). See the neutral default docs for the full new palette.

If your site was visually tuned around the cream-and-navy look, opt into the new tideline preset to restore it:

{
  "site": {
    "theme": {
      "package": "@refrakt-md/lumina",
      "presets": ["@refrakt-md/lumina/presets/tideline"]
    }
  }
}

Tideline preserves the previous Lumina colours and upgrades typography to IBM Plex Sans / Plex Mono as part of the v1.0 cleanup. If you specifically depended on the Outfit font, pin it back:

{
  "site": {
    "theme": {
      "package": "@refrakt-md/lumina",
      "presets": ["@refrakt-md/lumina/presets/tideline"],
      "tokens": {
        "font": {
          "sans": "'Outfit', system-ui, sans-serif"
        }
      }
    }
  }
}

Sites using theme as a string

If your refrakt.config.json uses the legacy string shorthand ("theme": "@refrakt-md/lumina"), it continues to work — you just get the new neutral default. Upgrading to the object form is only required if you want to add presets or token overrides.

Sites using a custom theme package

If you author a custom theme that built on Lumina's old --rf-* token values, audit the new values in packages/lumina/src/tokens.ts and update accordingly. The variable names are unchanged; only the values moved.

Font loading changes

The refrakt site's app.html (and any site that mirrors it) previously loaded Outfit from Google Fonts. After v0.14.0:

-<link href="https://fonts.googleapis.com/css2?family=Outfit:[email protected]&display=swap" rel="stylesheet" />
+<link href="https://fonts.googleapis.com/css2?family=Inter:[email protected]&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet" />

If your site has its own document-head template, update accordingly. Sites that opt into tideline should additionally load Plex Sans / Plex Mono.

Tint shape changes (SPEC-053)

The shape of TintDefinition and TintTokens (renamed from TintTokenSet) was realigned to match the rest of the design token contract. Field names that previously swapped meaning against the contract — most notably primary → text and accent → primary — are now consistent. The three-valued mode field was simplified to a two-state lockMode.

The user-facing surfaces are unchanged: the {% rune tint="warm" %} attribute, the data-tint HTML attribute, and the tint-mode rune attribute all continue to work exactly as before. Only the authoring shape for new tint definitions has moved. Most sites have no inline tint definitions and need no changes.

Field rename map

Old fieldNew fieldMaps to
backgroundbg--rf-color-bg
surfacesurface--rf-color-surface (unchanged)
primarytext--rf-color-text (formerly named primary — confusing collision with the contract's color.primary)
secondarymuted--rf-color-muted
accentprimary--rf-color-primary (formerly named accent — see above)
borderborder--rf-color-border (unchanged)
mode: 'auto' | 'dark' | 'light'lockMode: 'dark' | 'light' (absent = inherit)
(new)extends: '<other-tint>'

CSS custom property renames (theme authors only)

If you have hand-written CSS that reads the engine-emitted --tint-* properties directly, rename:

OldNew
--tint-background--tint-bg
--tint-primary--tint-text
--tint-secondary--tint-muted
--tint-accent--tint-primary
--tint-dark-background--tint-dark-bg
--tint-dark-primary--tint-dark-text
--tint-dark-secondary--tint-dark-muted
--tint-dark-accent--tint-dark-primary
--tint-surface, --tint-borderunchanged
--tint-dark-surface, --tint-dark-borderunchanged

Before and after

A typical Lumina warm tint, before:

warm: {
  light: {
    background: 'var(--rf-color-surface-active)',
    primary: 'var(--rf-color-text)',
    accent: 'var(--rf-color-warning)',
    border: 'var(--rf-color-border)',
  },
  dark: {
    background: '#2a2018',
    primary: 'var(--rf-color-text)',
    accent: 'var(--rf-color-warning)',
    border: '#4a3f33',
  },
}

After:

warm: {
  light: {
    bg: 'var(--rf-color-surface-active)',
    text: 'var(--rf-color-text)',
    primary: 'var(--rf-color-warning)',
    border: 'var(--rf-color-border)',
  },
  dark: {
    bg: '#2a2018',
    text: 'var(--rf-color-text)',
    primary: 'var(--rf-color-warning)',
    border: '#4a3f33',
  },
}

A mode-locked tint, before:

dark: {
  mode: 'dark',
  dark: {
    background: 'var(--rf-color-primary-700)',
    primary: 'var(--rf-color-primary-50)',
    accent: 'var(--rf-color-danger)',
  },
}

After:

dark: {
  lockMode: 'dark',
  dark: {
    bg: 'var(--rf-color-primary-700)',
    text: 'var(--rf-color-primary-50)',
    primary: 'var(--rf-color-danger)',
  },
}

Search-and-replace recipe

If you have a refrakt config or a custom theme with inline tints, the rename is a small set of regex / sed substitutions. Apply them only inside tints: { … } blocks — the same field names exist in other contexts and shouldn't be rewritten globally.

# Inside files that define tints (theme/config/plugin sources):

sed -i 's/\bbackground:/bg:/g'  path/to/file.ts
sed -i 's/\bsecondary:/muted:/g' path/to/file.ts

# The primary/accent swap is order-sensitive — rename `primary` to `text` first,
# then `accent` to `primary`, so we don't overwrite the new `primary` in step 2:
sed -i 's/\bprimary:/text:/g'   path/to/file.ts
sed -i 's/\baccent:/primary:/g' path/to/file.ts

# mode → lockMode
sed -i "s/\bmode: *'dark'/lockMode: 'dark'/g"   path/to/file.ts
sed -i "s/\bmode: *'light'/lockMode: 'light'/g" path/to/file.ts
# `mode: 'auto'` entries should be deleted entirely (auto is now the absence of lockMode).

Eyeball the diff afterward to catch any false positives — \b word-boundary regexes are mostly reliable but not perfect, especially around accent and primary which appear in many contexts.

extends for tint variants

New in v0.14.0: a tint can extend another tint and layer overrides on top.

tints: {
  warm: {
    light: { bg: 'var(--rf-color-surface-active)', text: '#1c1a17', primary: '#9c5a18' },
  },
  'warm-strong': {
    extends: 'warm',
    light: { primary: '#7c3aed' },
  },
}

The base tint is fully expanded first; the child's light / dark / lockMode layer on top per leaf. Circular chains are rejected at config-merge time with a clear error.

RefraktConfig.tints removed

The deprecated top-level tints field in refrakt.config.json (legacy flat-shape shorthand for sites.default.tints) is removed in v0.14.0. Move tint definitions inside the per-site config:

// Before (flat shape — no longer accepted at the top level)
{
  "contentDir": "./content",
  "theme": "@refrakt-md/lumina",
  "tints": { /* … */ }
}

// After
{
  "site": {
    "contentDir": "./content",
    "theme": "@refrakt-md/lumina",
    "tints": { /* … */ }
  }
}

Multi-site configs already use sites.<name>.tints and are unaffected.

Highlighter token renames (SPEC-048)

Custom CSS that reads the Shiki integration's CSS variables directly should rename --shiki-* to --rf-syntax-*:

- color: var(--shiki-token-keyword);
+ color: var(--rf-syntax-token-keyword);

This was a Chunk 1 change (already shipped in v0.14.0-alpha). Themes that consume only the contract surface (--rf-syntax-keyword and friends) are unaffected — the contract was always abstract.