Skip to content

Themes

Like Vuetify, the components don’t use fixed grays but theme roles, so they work on light and dark pages. Switch these docs between light and dark (top right) — every example follows.

Role Used for
background / on-background page background and text
surface / on-surface cards, menus, fields
on-primary, on-secondary, on-success, … text on solid fills (flat buttons, selected chips, colored cards)

Muted text, borders, field backgrounds and hover states are on-surface at reduced opacity (text-on-surface/70, border-on-surface/38…), so they adapt to any theme by themselves. The roles are regular Tailwind colors — use them in your own markup too:

<body class="bg-background text-on-background">
<p class="text-on-surface/70">Muted text</p>
</body>

Set data-theme to light, dark or system (follows the operating system). On <html> it themes the whole page; on any element it themes just that part, and themes nest both ways — like Vuetify’s v-theme-provider:

<section data-theme="dark">
<Card title="Dark section" />
<Card data-theme="light" title="Light again" />
</section>
data-theme="dark"
Whatever the page theme.
City
data-theme="light"
Nested back to light.
info

An element with data-theme gets the theme’s background and text color. Without any data-theme, the page is light.

Put ThemeScript in <head>. It runs before the page paints and restores the saved theme, so a dark page doesn’t flash white on load:

src/layouts/Layout.astro
---
import { ThemeScript } from 'bws-astro-ui';
---
<html lang="pl">
<head>
<ThemeScript defaultTheme="system" />
</head>
<body class="bg-background text-on-background">
<slot />
</body>
</html>

Change the theme with the helpers from bws-astro-ui/theme:

<button data-theme-toggle>Toggle theme</button>
<script>
import { toggleTheme } from 'bws-astro-ui/theme';
document
.querySelector('[data-theme-toggle]')
?.addEventListener('click', toggleTheme);
</script>
Function What it does
setTheme(theme, { persist }) sets <html data-theme> and saves it (unless persist: false); fires theme:change on <html>
toggleTheme() switches between light and dark
getTheme() 'light', 'dark' or 'system'
getResolvedTheme() the theme shown — system resolved to 'light' or 'dark'
ThemeScript prop Default Description
defaultTheme 'light' theme until the visitor picks one: light · dark · system

Each role has a value per theme in --light-* and --dark-* variables. Override any of them:

src/styles/global.css
:root {
--dark-background: #0f1115;
--dark-surface: #1a1d24;
--light-success: #15803d;
}

Available: background, on-background, surface, on-surface, and success, warning, info, error with their on-* pairs. Status colors are dark with white text in the light theme and light with dark text in the dark one, so both meet WCAG AA contrast.

Brand colors come from your @theme and are the same in both themes. A dark primary is hard to read on a dark background, so give the dark theme its own shade with matching text — and restate the light one if light sections can sit inside dark ones:

src/styles/global.css
[data-theme='dark'] {
--color-primary: #a4c4f7;
--color-on-primary: #112443;
}
[data-theme='light'] {
--color-primary: #2b4570;
--color-on-primary: #ffffff;
}

These docs do exactly that.

The floating label of an outlined field covers the border with the background behind it — the page background by default, the card’s surface inside a Card. On another background, set it:

<div class="bg-primary/10 [--input-bg:var(--color-background)]">…</div>
Created byBanach Web SolutionsBanach Web Solutions