Styling
A guide to styling Base UI components with your preferred styling engine.
Base UI components are unstyled, don’t bundle CSS, and are compatible with Tailwind, CSS Modules, CSS-in-JS, or any other styling solution you prefer. You retain total control of your styling layer.
Style hooks
CSS classes
Components that render an HTML element accept a class prop to style the element with CSS classes.
<Switch.Thumb class="SwitchThumb" />
The prop can also be passed a function that takes the component’s state as an argument.
<Switch.Thumb class={(state) => (state.checked ? 'checked' : 'unchecked')} />
Data attributes
Components provide data attributes designed for styling their states. For example, Switch can be styled using its [data-checked] and [data-unchecked] attributes, among others.
.SwitchThumb[data-checked] {
background-color: green;
}
CSS variables
Components expose CSS variables to aid in styling, often containing dynamic numeric values to be used in sizing or transform calculations. For example, Popover exposes CSS variables on its Popup component like --available-height and --anchor-width.
.Popup {
max-height: var(--available-height);
}
Check out each component’s API reference for a complete list of available data attributes and CSS variables.
Style prop
Components that render an HTML element accept a style prop to style the element with a CSS object.
<Switch.Thumb style={{ height: '100px' }} />
The prop also accepts a function that takes the component’s state as an argument.
<Switch.Thumb style={(state) => ({ color: state.checked ? 'red' : 'blue' })} />
Tailwind CSS
Apply Tailwind CSS classes to each part via the class prop.
import { Menu } from 'base-ui-solid/menu';
export default function ExampleMenu() {
return (
<Menu.Root>
<Menu.Trigger class="flex h-8 items-center justify-center rounded-md border border-neutral-950 bg-white px-3 text-sm text-neutral-950 select-none hover:bg-neutral-100 active:bg-neutral-200 data-pressed:bg-neutral-100 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-neutral-950 dark:border-white dark:bg-neutral-950 dark:text-white dark:hover:bg-neutral-800 dark:active:bg-neutral-700 dark:data-pressed:bg-neutral-800 dark:focus-visible:outline-white">
Song
</Menu.Trigger>
<Menu.Portal>
<Menu.Positioner class="outline-hidden" sideOffset={8}>
<Menu.Popup class="origin-(--transform-origin) border border-neutral-950 bg-white py-1 text-neutral-950 outline-hidden transition data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 dark:border-white dark:bg-neutral-950 dark:text-white">
<Menu.Item class="flex cursor-default py-2 pr-8 pl-4 text-sm leading-4 outline-hidden select-none data-highlighted:bg-neutral-950 data-highlighted:text-white dark:data-highlighted:bg-white dark:data-highlighted:text-neutral-950">
Add to Library
</Menu.Item>
<Menu.Item class="flex cursor-default py-2 pr-8 pl-4 text-sm leading-4 outline-hidden select-none data-highlighted:bg-neutral-950 data-highlighted:text-white dark:data-highlighted:bg-white dark:data-highlighted:text-neutral-950">
Add to Playlist
</Menu.Item>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
);
}
CSS Modules
Apply custom CSS classes to each part via the class prop.
Then style those classes in a CSS Modules file.
import { Menu } from 'base-ui-solid/menu';
import styles from './menu.module.css';
export default function ExampleMenu() {
return (
<Menu.Root>
<Menu.Trigger class={styles.Button}>Song</Menu.Trigger>
<Menu.Portal>
<Menu.Positioner class={styles.Positioner} sideOffset={8}>
<Menu.Popup class={styles.Popup}>
<Menu.Item class={styles.Item}>Add to Library</Menu.Item>
<Menu.Item class={styles.Item}>Add to Playlist</Menu.Item>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
);
}
CSS-in-JS
Wrap each component part and apply styles, then assemble your styled components.
Use a CSS-in-JS library that supports Solid 2.0, or plain wrapper components with reactive style callbacks as shown below.
import { Menu, MenuPositionerCssVariables } from 'base-ui-solid/menu';
import { omit } from 'solid-js';
function StyledMenuTrigger(props: Menu.Trigger.Props) {
return <Menu.Trigger {...props} />;
}
function StyledMenuPositioner(props: Menu.Positioner.Props) {
return <Menu.Positioner {...props} />;
}
function StyledMenuPopup(props: Menu.Popup.Props) {
return (
<Menu.Popup
{...omit(props, 'style')}
style={(state) => ({
'max-height': `var(${MenuPositionerCssVariables.availableHeight})`,
...(typeof props.style === 'function' ? props.style(state) : props.style),
})}
/>
);
}
function StyledMenuItem(props: Menu.Item.Props) {
return (
<Menu.Item
{...omit(props, 'style')}
style={(state) => ({
outline: state.highlighted ? '1px solid currentColor' : undefined,
...(typeof props.style === 'function' ? props.style(state) : props.style),
})}
/>
);
}
const MenuExample = () => (
<Menu.Root>
<StyledMenuTrigger>Song</StyledMenuTrigger>
<Menu.Portal>
<StyledMenuPositioner>
<StyledMenuPopup>
<StyledMenuItem>Add to Library</StyledMenuItem>
<StyledMenuItem>Add to Playlist</StyledMenuItem>
</StyledMenuPopup>
</StyledMenuPositioner>
</Menu.Portal>
</Menu.Root>
);
export default MenuExample;