Default scrollbars are loud and ignore your theme. Bones ships an opt-in stylesheet that makes every scrollbar thin, theme-aware, and invisible until you hover whatever is scrolling. For one scrolling pane rather than the whole page, use the Scroll area component.

Opt in

One import, applied page-wide. It's separate from index.css on purpose: restyling every scrollbar is a whole-page opinion your app should choose deliberately.

import "@usebones/tokens/scrollbars.css";

See it

This box scrolls; this site has the stylesheet enabled. The scrollbar fades in when your cursor is over the box and hides again when it leaves, picking up --ub-border-strong so it follows the theme. The gutter stays reserved, so content never shifts.

09:41 Deployed v0.2.1 to production

09:38 Smoke tests passed on preview

09:36 Built 47 pages in 3.6s

09:12 Merged: matrix full theme

08:57 Deployed v0.2.0 to production

08:55 Tagged v0.2.0

08:40 Merged: consistency pass

08:31 Merged: rename Input wrapper anatomy

08:02 Merged: autocomplete

07:48 Merged: combobox

07:30 Merged: navigation menu

07:15 Deployed v0.1.0 to production

How it works

Just the standard CSS scrollbar properties, colored by tokens. No JavaScript, no wrapper component, nothing to keep in sync:

* {
  scrollbar-width: thin;
  /* the visible color at zero alpha, so a fade moves opacity only */
  scrollbar-color: oklch(from var(--ub-border-strong) l c h / 0) transparent;
}

*:hover {
  scrollbar-color: var(--ub-border-strong) transparent;
}

Fading the reveal

The stylesheet deliberately ships no transition: transitions on broad selectors animate things that were never meant to move and make components that wait for transitions linger. To fade the reveal, declare it on the scrollables you own:

.my-scroll-area {
  transition: scrollbar-color var(--ub-duration-base) var(--ub-ease-out);
}