Starlight setup
@static-bloom/starlight is a Starlight plugin. It restyles every Starlight surface (header, search, sidebar, table of contents, content, asides, cards, tabs, steps, file trees, pagination, hero and code blocks), replaces the theme picker with a flavor picker, and generates code themes from the palette. This site uses it with the default options.
-
Install the plugin next to Starlight. It needs
astro7 and@astrojs/starlight0.42.Terminal window pnpm add @static-bloom/starlight -
Add it to Starlight’s
plugins.astro.config.mjs import { defineConfig } from 'astro/config'import starlight from '@astrojs/starlight'import staticBloomStarlight from '@static-bloom/starlight'export default defineConfig({integrations: [starlight({title: 'My docs',plugins: [staticBloomStarlight()],}),],})
When Starlight sets up, the plugin:
- puts
@static-bloom/starlight/styles.cssfirst incustomCss, so your own stylesheets load after it and win; - overrides the
ThemeProvidercomponent and, unlessflavorSelectis off, theThemeSelectcomponent, keeping every other override you configured; - sets the Expressive Code themes and style overrides;
- hands the configured flavor runtime to its
ThemeProviderthrough a Vite virtual module.
If you already override ThemeProvider or ThemeSelect, your component is kept and the plugin logs a warning. Static Bloom’s ThemeSelect needs Static Bloom’s ThemeProvider.
Options
Section titled “Options”staticBloomStarlight({ flavors: { dark: 'blackout', light: 'daybreak' }, accent: 'ion', flavorSelect: true, expressiveCode: true,})| Option | Values | Default |
|---|---|---|
flavors.dark |
'dusk', 'midnight', 'blackout' |
'midnight' |
flavors.light |
'daybreak' |
'daybreak' |
accent |
any of the 12 accents | 'plasma' |
flavorSelect |
true, false |
true |
expressiveCode |
true, false |
true |
flavors.dark is the flavor Auto uses on a dark system, and the one Starlight’s “Dark” choice maps to. accent re-points the primary color: links, the active sidebar entry, focus rings and glow. Invalid values or unknown keys throw a StaticBloomStarlightOptionsError when the plugin is created.
Flavor runtime and picker
Section titled “Flavor runtime and picker”The ThemeProvider override inlines the flavor runtime in <head>, so the stored flavor applies before the first paint. It sets data-flavor, data-theme (which Starlight and Expressive Code read), data-accent and color-scheme on <html>, keeps <meta name="theme-color"> on the active flavor, and exposes window.staticBloom. Astro pages and Vue islands on the same origin share the choice, stored under static-bloom:flavor.
The ThemeSelect override is Starlight’s own select with Auto and the four flavors; the header and mobile-menu copies stay in sync. Its labels come from Starlight’s UI strings, so you can rename it through the i18n collection. This site calls it “Select flavor”:
import { defineCollection } from 'astro:content'import { docsLoader, i18nLoader } from '@astrojs/starlight/loaders'import { docsSchema, i18nSchema } from '@astrojs/starlight/schema'
export const collections = { docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), i18n: defineCollection({ loader: i18nLoader(), schema: i18nSchema() }),}{ "themeSelect.accessibleLabel": "Select flavor"}themeSelect.auto renames the Auto option the same way.
With flavorSelect: false, Starlight’s dark, light and auto picker stays and keeps working: its choices map to flavors.dark, flavors.light and Auto. The plugin mirrors the flavor into Starlight’s starlight-theme storage key, and adopts a visitor’s earlier Starlight choice once, if no flavor was stored yet. window.StarlightThemeProvider.updatePickers() stays available for code that calls it.
Expressive Code
Section titled “Expressive Code”Unless expressiveCode is false on the plugin, or Expressive Code is turned off in Starlight, the plugin configures code blocks:
- Themes. A dark and a light VS Code theme generated from the palette’s syntax roles: keywords in ultraviolet, strings in acid, functions in ion, comments in italic
overlay2. The dark theme uses the accents all dark flavors share; the light theme uses Daybreak. - Style overrides. Code backgrounds, borders, frames, title bars, the copy button and text markers point at CSS variables (
var(--sb-canvas-sunken),var(--sb-line),var(--sb-panel), …), so every block follows the active flavor and accent without a rebuild. - Contrast.
minSyntaxHighlightingColorContrastis 4.5. The palette already meets WCAG AA on the code background, so token colors render as designed.
import type {} from '@static-bloom/astro'
window.staticBloom?.setFlavor('blackout') // persists and appliesYour own Expressive Code options win: themes replace the generated ones, and styleOverrides merge over the plugin’s, group by group for frames and textMarkers. The themes are exported for other Expressive Code or Shiki setups:
import { createExpressiveCodeTheme, staticBloomDark, staticBloomLight,} from '@static-bloom/starlight'
const blackoutTheme = createExpressiveCodeTheme('blackout')Tailwind in Starlight
Section titled “Tailwind in Starlight”To use the Tailwind theme and the Vue components on a Starlight site, add a stylesheet to customCss. The plugin’s stylesheet still loads first.
@layer base, starlight, theme, components, utilities;@import 'tailwindcss/theme.css' layer(theme);@import 'tailwindcss/utilities.css' layer(utilities);@import '@static-bloom/tailwind';@import '@static-bloom/vue/tailwind.css';
@layer base { *, ::before, ::after { border: 0 solid; }}starlight({ plugins: [staticBloomStarlight()], customCss: ['./src/styles/global.css'],})Tailwind’s preflight is left out because Starlight ships its own reset; the border reset is the one piece Tailwind’s border utilities need. Register @tailwindcss/vite in vite.plugins. @astrojs/starlight-tailwind is compatible but not required.
sb-* demos in content
Section titled “sb-* demos in content”These docs also render the framework-free CSS components inside pages. They import the component and utility layers after the lines above:
@import '@static-bloom/css/components.css';@import '@static-bloom/css/utilities.css';and wrap every live example in an element with the not-content class, which keeps Starlight’s content typography (margins, heading and link styles) out of it:
<div class="not-content"> <button class="sb-btn">Connect</button></div>Vue islands
Section titled “Vue islands”Add @astrojs/vue to the integrations and use the components as islands. Do not add the @static-bloom/astro integration to a Starlight site: the plugin’s ThemeProvider already installs the runtime.
Layers and overrides
Section titled “Layers and overrides”The plugin’s stylesheet declares the page’s layer order:
@layer base, starlight, theme, components, utilities;/* starlight: base, reset, core, content, components, utils, static-bloom */- Starlight’s color, shadow and font variables (
--sl-color-*,--sl-shadow-*,--sl-font,--sl-font-mono) are mapped to Static Bloom tokens in unlayered CSS. Override one in your own unlayered CSS, which loads later and wins. - The component restyles live in
@layer starlight.static-bloom. Any unlayered rule, or a rule in a later layer such as Tailwind’sutilities, beats them. - To recolor Starlight and the Static Bloom components together, change the tokens (
--sb-primary,--sb-canvas, …) rather than the--sl-*variables. - A nested
data-flavorordata-accentattribute restyles one region, as on the home page flavor strip. - Printing uses the Daybreak palette. Decorative effects respect
prefers-reduced-motion, and the background grain is removed in print and in forced-colors mode.