Styling and Theming
Using, customizing, and extending the Voice UI Kit styles
The Voice UI Kit ships with a minimal default theme that’s meant to be customized and extended. It’s not a “creative design library” with strong opinions; it’s a flexible foundation for building your own voice-first UIs.
Under the hood it uses Tailwind CSS v4 and is designed to play nicely with your existing styles or Tailwind setup.
@pipecat-ai/voice-ui-kit/styles
- The default theme@pipecat-ai/voice-ui-kit/styles.scoped
- The default theme, scoped to a.vkui-root
class- Dark mode support
- ShadUI compatibility (components registry coming soon!)
- Optional utilities:
@pipecat-ai/voice-ui-kit/utilities
for helpful extras (see below)
Setup
The Voice UI Kit CSS can be imported from the @pipecat-ai/voice-ui-kit/styles
package.
@import "@pipecat-ai/voice-ui-kit/styles";
:root {
/* etc */
}
If you're using Tailwind in your project, you can import it alongside your own custom styles:
@import "tailwindcss";
@import "tw-animate-css";
@import "@pipecat-ai/voice-ui-kit/styles";
:root {
/* etc */
}
Alternatively, you can include the styles in your application code:
import "@pipecat-ai/voice-ui-kit/styles";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
Customization
Developers can approach customization in a few different ways:
- Passing custom styles or class names through to the components.
- Overriding CSS variables, such as colors, fonts, etc.
- Installing individual components from the registry and extending them (ejecting.)
Theme Provider and useTheme
Wrap your app with ThemeProvider
to enable light/dark (or custom) themes and persistence.
import { ThemeProvider } from "@pipecat-ai/voice-ui-kit";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider defaultTheme="system" storageKey="voice-ui-kit-theme">
{children}
</ThemeProvider>
);
}
Props
Prop | Type | Default |
---|---|---|
children | React.ReactNode | - |
defaultTheme? | "system" | string | "system" |
storageKey? | string | "voice-ui-kit-theme" |
disableStorage? | boolean | false |
useTheme hook
Use useTheme
anywhere under the provider to read and set the theme.
import { useTheme } from "@pipecat-ai/voice-ui-kit";
export function ThemeToggle() {
const { theme, resolvedTheme, setTheme } = useTheme();
return (
<div>
<div>theme: {theme}</div>
<div>resolved: {resolvedTheme}</div>
<button onClick={() => setTheme("light")}>Light</button>
<button onClick={() => setTheme("dark")}>Dark</button>
<button onClick={() => setTheme("system")}>System</button>
</div>
);
}
Create a custom theme
@custom-variant mytheme (&:is(.mytheme *));
@theme {
/* Optional: local keyframes, shadows, etc. */
}
.mytheme {
--font-sans: system-ui, sans-serif;
--font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
--color-background: #000;
--color-foreground: #e5e7eb;
--color-primary: #22c55e;
--color-secondary: #14532d;
--color-border: #22c55e;
--color-input: #0f172a;
--color-accent: color-mix(in srgb, var(--color-primary) 10%, transparent);
--color-accent-foreground: var(--color-foreground);
}
Apply it by toggling a class on a root container (or html
if you prefer global):
import "@pipecat-ai/voice-ui-kit/styles";
import "./my-theme.css";
export function App() {
return (
<div className="mytheme">
{/* app */}
</div>
);
}
Switching themes at runtime
You can switch themes programmatically either globally (ThemeProvider on html
) or scoped to a container.
import { ThemeProvider, useTheme } from "@pipecat-ai/voice-ui-kit";
export default function App() {
return (
<ThemeProvider defaultTheme="system">
<Toolbar />
<YourApp />
</ThemeProvider>
);
}
function Toolbar() {
const { theme, resolvedTheme, setTheme } = useTheme();
return (
<div>
<span>current: {resolvedTheme}</span>
<button onClick={() => setTheme("light")}>Light</button>
<button onClick={() => setTheme("dark")}>Dark</button>
<button onClick={() => setTheme("system")}>System</button>
</div>
);
}
import "@pipecat-ai/voice-ui-kit/styles.scoped";
import { ThemeProvider, useTheme } from "@pipecat-ai/voice-ui-kit";
export default function App() {
return (
<ThemeProvider defaultTheme="dark">
<ScopedRoot />
</ThemeProvider>
);
}
function ScopedRoot() {
const { resolvedTheme } = useTheme();
// IMPORTANT: when using scoped styles, put the theme class on `.vkui-root`
return (
<div className={`vkui-root ${resolvedTheme}`}>
<YourApp />
</div>
);
}
If you're using the scoped import (@pipecat-ai/voice-ui-kit/styles.scoped
), set the theme class on the .vkui-root
element instead of html
.
Themes
The default theme is intentionally light-touch. If you want a quicker start, we also provide optional themes you can use as-is or adapt.
@import "@pipecat-ai/voice-ui-kit/styles";
@import "@pipecat-ai/voice-ui-kit/themes/terminal";
Theme files are primarily CSS variable overrides.
Utilities CSS
You can optionally import the utilities bundle for extra helpers used by the UI Kit.
@import "tailwindcss";
@import "@pipecat-ai/voice-ui-kit/styles";
@import "@pipecat-ai/voice-ui-kit/utilities";
Why import utilities
?
- Provides additional design tokens and helpers (e.g., scanlines, grid/stripe backgrounds, sizing shortcuts) used by components and examples
- Ensures custom compositions with UI Kit components merge correctly without conflicts
- Reduces duplication of one-off utility CSS in your app
Scoping
The Voice UI Kit components are intended to be placed in a developer's own application, inheriting from their own styles or Tailwind theme.
Sometimes, however, it's preferable to isolate styles from your own. The Voice UI Kit bundle includes a fully scoped CSS file that can be used instead:
@import "@pipecat-ai/voice-ui-kit/styles.scoped";
/* Your application CSS file */
The Voice UI Kit theme will now only apply when wrapped in a .vkui-root
class, and declarations from your own Tailwind theme will not be applied.
<div class="vkui-root">
<span class="vkui:flex">✅ Scoped correctly</span>
</div>
<div>
<span class="vkui:flex">❌ Not scoped</span>
</div>
Dark mode
The Voice UI Kit theme supports dark mode using a :root.dark
selector:
<html class="dark">
<body>
...
</body>
</html>
Class merging
The Voice UI Kit uses custom design tokens and utilities that aren't part of standard Tailwind CSS:
- Custom shadow classes:
shadow-xshort
,shadow-short
,shadow-long
,shadow-xlong
- Custom button sizing:
button-sm
,button-md
,button-lg
,button-xl
and their icon variants - Custom spacing tokens:
element-xs
,element-sm
,element-md
,element-lg
,element-xl
,element-2xl
These utilities support consistency across components and primitives, as well as reduce the amount of inline classes required.
Without proper merge handling, these custom classes would conflict with each other or not merge correctly when combined with standard Tailwind classes.
Usage
The cn
utility function is used throughout the Voice UI Kit components to merge class names intelligently:
import { cn } from "@pipecat-ai/voice-ui-kit";
// This will properly merge shadow classes
<Card className={cn("shadow-short", className)} />
// This will properly merge button sizing
<Button className={cn("button-lg", customClasses)} />
This ensures that when developers pass custom classes to components, they merge correctly with the Voice UI Kit's design system without conflicts.
Variable list
See available variables in the source index.css.