Theming
Every color the React controls draw comes from a --listens-* CSS custom property. The kit ships a light and a dark value for each one and switches between them on its own, so you only set the tokens you want to change.
Change a color
Set the token on :root in your global stylesheet. The kit's own styles sit in a cascade layer, @layer listens, and plain CSS always beats a layer, so your value wins without !important.
index.css
:root {
/* Labels and content while the mic is open. */
--listens-accent: #7c3aed;
/* Focus, highlighted options, and checkbox, radio and slider fills. */
--listens-interactive: #7c3aed;
}
A value set this way applies in light and dark mode alike. To give dark mode its own value, see different colors in light and dark.
Light and dark mode
The controls, the listening bar and the popup follow the device's light or dark setting automatically. To switch them yourself, set data-listens-theme to "dark" or "light" on the <html> element, and remove it to follow the device again. The controls only read the attribute there, so it has no effect on an inner element.
document.documentElement.setAttribute('data-listens-theme', 'dark');
The React setup has a dark mode switch built from a ListensToggle, so people can tap it or say "dark mode".
Different colors in light and dark
Dark mode turns on in two ways: the device is set to dark, or your app sets data-listens-theme="dark". Cover both with the same selectors the kit uses:
index.css
:root {
--listens-accent: #7c3aed;
}
/* The device is dark, and the app hasn't forced light. */
@media (prefers-color-scheme: dark) {
:root:not([data-listens-theme='light']) {
--listens-accent: #a78bfa;
}
}
/* The app forced dark. */
:root[data-listens-theme='dark'] {
--listens-accent: #a78bfa;
}
Theme one part of a page
Custom properties are inherited, so a token set on any element reaches every control inside it. Give a section a class and set its tokens there:
index.css
.checkout {
--listens-accent: #0a7c42;
--listens-interactive: #0a7c42;
--listens-success: #0a7c42;
}
Checkout.tsx
export function Checkout() {
return (
<section className="checkout">
{/* …every control in here uses the green */}
</section>
);
}
This also keeps a section light on a dark page, or dark on a light one: set the whole palette on it, using the values in the token reference.
Style one control
Tokens restyle every control at once. To change just one, give it a className, which every control takes and merges onto its root element. Your class's rules beat the kit's layer, and because tokens are inherited, a class can also retheme that one control:
index.css
.danger-toggle {
--listens-success: #e53935;
}
<ListensToggle
className="danger-toggle"
label="Delete my data"
command="delete my data"
/>
Most controls also take classNames, which puts your classes on individual parts, such as a text field's label or input. Each control's page lists its classNames parts, CSS classes and the tokens it reads under Styling hooks, for example the label button's.
The listening bar and popup
The bar and the popup have their own stylesheet, with frosted light and dark backgrounds, so the tokens don't change them. They follow the device and data-listens-theme like the controls do. Their colors are props instead: waveColor on both, and backgroundColor and textColor on the popup. See the listening bar and popup page.
Token reference
The web kits share these tokens, so the same CSS restyles React and Angular.
| Token | Light | Dark |
|---|---|---|
| Listening signal | ||
--listens-accentLabels and content while the mic is open. The one color every control shares. | #ff0000 | #ff0000 |
| Text | ||
--listens-textPrimary control text. | #222222 | #ededed |
--listens-text-secondaryValues and secondary readouts. | #444444 | #c2c2c2 |
--listens-labelField labels. | #666666 | #a6a6a6 |
--listens-text-mutedIdle toggle button text and “no results” messages. | #888888 | #8f8f8f |
--listens-text-subtleIcons, empty states, delete buttons and disabled text. | #999999 | #7d7d7d |
--listens-text-strongThe strongest emphasis, such as the selected ring. | #000000 | #ffffff |
--listens-on-accentText and icons on an accent fill. | #ffffff | #ffffff |
--listens-placeholderInput placeholders. | #9a9a9a | #7d7d7d |
| Surfaces and borders | ||
--listens-surfaceInputs, menus, list items and cards. | #ffffff | #2a2a2a |
--listens-borderDefault borders and input outlines. | #d0d0d0 | #4a4a4a |
--listens-border-subtleFaint dividers. | #e0e0e0 | #3a3a3a |
--listens-button-borderThe soft edge around buttons. | rgba(0, 0, 0, 0.12) | rgba(255, 255, 255, 0.13) |
--listens-button-faceButton fill. | #efefef | #6b6b6b |
--listens-on-buttonText on a button fill. | #222222 | #ffffff |
--listens-menu-hairlineThe edge of the combobox menu. | rgba(0, 0, 0, 0.05) | rgba(255, 255, 255, 0.12) |
--listens-ringThe selection ring’s shadow. | rgba(0, 0, 0, 0.2) | rgba(255, 255, 255, 0.28) |
| Interactive | ||
--listens-interactiveFocus, highlighted options, and checkbox, radio and slider fills. | #007aff | #0a84ff |
--listens-successThe toggle’s track when on. | #4caf50 | #46c463 |
--listens-trackThe toggle’s track when off. | #dddddd | #555555 |
--listens-toggle-thumbThe toggle’s thumb. | #ffffff | #f0f0f0 |
--listens-segment-onA checked toggle button’s fill. | #64b5f6 | #0a84ff |
--listens-segment-on-borderA checked standalone toggle button’s border. | #2196f3 | #409cff |
--listens-dangerDestructive hovers, such as delete. | #e53935 | #ff6b6b |