# Updates

Released 15 Apr 2026. [Components 2.4.0](https://github.com/TicketSolutionsPtyLtd/roadie/blob/main/packages/components/CHANGELOG.md#240) and [Core 2.1.0](https://github.com/TicketSolutionsPtyLtd/roadie/blob/main/packages/core/CHANGELOG.md#210)

2 additions.

## New

- **Theming API improvements — controlled accent, validation, and pre-hydration bootstrap**

  Add a new declarative theming surface on `ThemeProvider` plus the pieces
  needed to eliminate the "flash of default accent" on static-export apps.

  **New exports from `@oztix/roadie-components`:**
  - `DEFAULT_ACCENT_COLOR` — the Oztix blue default, previously
    module-local.
  - `InvalidColorError` / `isValidHexColor` — validation primitives.
    Consumers can guard untrusted hex at the fetch boundary instead of
    reinventing a zod schema.
  - `getAccentStyleTagSync(hex)` — synchronous sibling of
    `getAccentStyleTag`. Returns a full `<style>` tag with
    `--accent-hue` and `--accent-chroma`, ready for framework-agnostic
    `<head>` injection. Uses the new sync sRGB→OKLCH converter in core.
  - `getAccentStyleSync(hex)` — returns just the inner CSS body
    (`:root{--accent-hue:...;--accent-chroma:...}`), for React consumers
    that want to wrap it in a real `<style>` element via
    `dangerouslySetInnerHTML`.
  - `getBootstrapScript(opts)` — re-exported from `@oztix/roadie-core`.
    Composes `getThemeScript` + an optional accent style tag into one
    head injection for apps that want to do the whole bootstrap in one
    line.

  **Controlled `accentColor` prop on `ThemeProvider`:**

  ```tsx
  <ThemeProvider accentColor={collection?.themeColour ?? null}>
    {children}
  </ThemeProvider>
  ```

  - Pass `undefined` (or omit the prop) to stay uncontrolled — the old
    `defaultAccentColor`-seeded behaviour is unchanged.
  - Pass a hex string to take control: the prop overrides internal state
    on every render, imperative `setAccentColor` calls become no-ops with
    a dev warning, and there's no effect sync or cleanup to wire up.
  - Pass `null` to opt into controlled mode while falling back to
    `defaultAccentColor` (ideal for `collection?.themeColour ?? null`).
  - Invalid hex input in a controlled prop logs a dev warning and falls
    back to the default — the provider never renders with a broken
    theme.

  **`setAccentColor` now throws synchronously** with `InvalidColorError`
  when the argument isn't a valid hex. Previously the call "succeeded"
  and threw inside the async accent effect with no handler path. If your
  app validates at the boundary (or uses the new `isValidHexColor`
  helper), there's nothing to change.

  **Why this matters.** Consumer apps that theme from async data
  (per-tenant branding, promoter-coloured collection pages, feature
  flags) can now drop their bespoke effect-based accent sync, their
  hex validator, and their hardcoded default constant. The imperative
  API remains for simple cases like in-app colour pickers.
- **Sync sRGB→OKLCH converter + unified bootstrap script + extended radius scale**

  **New sync colour utilities in `@oztix/roadie-core/colors`:**
  - `hexToOklch(hex)` — convert a hex string to `{ l, c, h }` using
    Björn Ottosson's reference sRGB→Oklab→OKLCH pipeline. Zero
    dependencies (the existing async helpers still use `colorjs.io`).
  - `getOklchHueSync(hex)` / `getOklchChromaSync(hex)` — synchronous
    siblings of the existing async helpers. Match the async output to
    four decimal places across a 20-hex representative palette.
  - `Oklch` type export.

  These unblock pre-hydration accent bootstrap: the hot path that the
  `ThemeProvider` accent effect uses (setting just `--accent-hue` and
  `--accent-chroma`) no longer needs an async `colorjs.io` import, so
  consumers can inject the accent style tag before the first paint.
  The existing async `generateAccentScale` / `generateNeutralScale`
  pipeline is unchanged — those still use `colorjs.io` for the full
  14-step hex fallback output.

  **New `getBootstrapScript` helper in `@oztix/roadie-core/theme`:**

  ```ts
  import { getBootstrapScript } from '@oztix/roadie-core/theme'

  const html = getBootstrapScript({
    followSystem: true,
    accentColor: collection?.themeColour // optional
  })
  ```

  Returns a single HTML string combining the theme script (dark-mode
  flash prevention) and an optional accent style tag. Framework-agnostic
  — drop it into `<head>` via `dangerouslySetInnerHTML`, Astro's
  `set:html`, or a plain HTML template. When `accentColor` is omitted
  or `null`, only the theme script is emitted. Invalid hex input throws
  synchronously with a clear error message.

  **Extended radius scale:**

  Tailwind v4's default radius scale stops at `rounded-4xl` (2rem), so
  `rounded-5xl` and beyond silently resolved to `0px`. Roadie now adds
  three extended tiers via `@theme inline` in `tokens.css`:
  - `--radius-5xl: 2.5rem` (40px) — hero cards, collection headers
  - `--radius-6xl: 3rem` (48px) — feature banners
  - `--radius-7xl: 3.5rem` (56px) — edge-to-edge promotional layouts

  See the [shape foundation](/foundations/shape) page for usage
  guidance and the new `foundations/theming` page for the full
  dynamic-theming walkthrough.
