You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
5.5 KiB
144 lines
5.5 KiB
/*
|
|
* ScrollArea recipe — visual treatment for a custom-scrollbar
|
|
* container. The headless Soma layer owns the scroll math, the
|
|
* visibility timer (`type='hover' | 'scroll' | 'auto' | 'always'`)
|
|
* and the thumb position; Eidos paints the chrome.
|
|
*
|
|
* Architecture mirrors Radix Primitives / Bits UI:
|
|
*
|
|
* [data-scroll-area] → outer relative-positioned shell
|
|
* [data-scroll-area-viewport] → scroll container (overflow: auto)
|
|
* [data-scroll-area-scrollbar] → track (vertical / horizontal)
|
|
* [data-scroll-area-thumb] → draggable position indicator
|
|
* [data-scroll-area-corner] → square where both bars meet
|
|
*
|
|
* Tokens stay close to the canonical UIX scale: track and thumb
|
|
* borrow from the neutral palette; the corner tints with the same
|
|
* track color so a dual-axis container reads as a single unit.
|
|
*/
|
|
|
|
[data-scroll-area] {
|
|
position: relative;
|
|
overflow: hidden;
|
|
border-radius: var(--scroll-area-radius, var(--radius-md));
|
|
background: var(--scroll-area-bg, transparent);
|
|
}
|
|
|
|
[data-scroll-area-viewport] {
|
|
position: absolute;
|
|
inset: 0;
|
|
/* Trap wheel/touch scroll at the viewport's edges so reaching the top or
|
|
bottom of the list doesn't chain to the page (`<body>`) and scroll it.
|
|
Both viewport variants (fixed-fill above, autosize below) inherit this —
|
|
the autosize rule only overrides position/overflow, not this. Mirrors the
|
|
`overscroll-behavior: contain` every other scroll region already sets
|
|
(command / listbox / tree-view / table / virtual-list …). */
|
|
overscroll-behavior: contain;
|
|
}
|
|
|
|
/* Autosize mode — content-sized up to a max, then scroll, instead of the
|
|
default fixed-height fill. The viewport goes in-flow with a `max-block-size`
|
|
(the consumer sets `--scroll-area-auto-max-block`); the root content-sizes to
|
|
it and the overlay scrollbars span that box. Enables content-sized popups
|
|
(combobox / select / menu) — the case the default fixed-height model (and
|
|
Radix's ScrollArea, which can't take `max-height`) can't cover. Mirrors
|
|
Mantine's `ScrollArea.Autosize`. */
|
|
[data-scroll-area][data-autosize] [data-scroll-area-viewport] {
|
|
position: static;
|
|
inset: auto;
|
|
max-block-size: var(--scroll-area-auto-max-block, none);
|
|
overflow: auto;
|
|
}
|
|
|
|
/* The viewport scrolls; the scrollbars are positioned absolutely over
|
|
it (macOS-style overlay) so they don't displace content. The
|
|
soma-tracked thumb position drives the inline transform. */
|
|
|
|
[data-scroll-area-scrollbar] {
|
|
position: absolute;
|
|
display: flex;
|
|
user-select: none;
|
|
touch-action: none;
|
|
padding: var(--scroll-area-scrollbar-padding, var(--space-0-5));
|
|
background: var(--scroll-area-track-bg, color-mix(in srgb, var(--color-neutral-track) 60%, transparent));
|
|
transition:
|
|
background var(--scroll-area-transition-duration, var(--duration-fast))
|
|
var(--scroll-area-transition-ease, var(--ease-default)),
|
|
opacity var(--scroll-area-transition-duration, var(--duration-fast))
|
|
var(--scroll-area-transition-ease, var(--ease-default));
|
|
}
|
|
|
|
[data-scroll-area-scrollbar][data-orientation='vertical'] {
|
|
inset-block: 0;
|
|
inset-inline-end: 0;
|
|
inline-size: var(--scroll-area-scrollbar-size, var(--space-2-5));
|
|
flex-direction: column;
|
|
}
|
|
|
|
[data-scroll-area-scrollbar][data-orientation='horizontal'] {
|
|
inset-inline: 0;
|
|
inset-block-end: 0;
|
|
block-size: var(--scroll-area-scrollbar-size, var(--space-2-5));
|
|
flex-direction: row;
|
|
}
|
|
|
|
/* Reserve space on the corresponding edges when each scrollbar is mounted.
|
|
* Using `:has` lets us avoid an extra wrapper while still keeping the
|
|
* viewport's scroll content out from under the scrollbars. */
|
|
[data-scroll-area]:has([data-scroll-area-scrollbar][data-orientation='vertical'])
|
|
[data-scroll-area-scrollbar][data-orientation='horizontal'] {
|
|
inset-inline-end: var(--scroll-area-scrollbar-size, var(--space-2-5));
|
|
}
|
|
|
|
[data-scroll-area]:has([data-scroll-area-scrollbar][data-orientation='horizontal'])
|
|
[data-scroll-area-scrollbar][data-orientation='vertical'] {
|
|
inset-block-end: var(--scroll-area-scrollbar-size, var(--space-2-5));
|
|
}
|
|
|
|
[data-scroll-area-scrollbar][data-state='hidden'] {
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
[data-scroll-area-scrollbar][data-state='visible'] {
|
|
opacity: 1;
|
|
}
|
|
|
|
[data-scroll-area-scrollbar][data-hover],
|
|
[data-scroll-area-scrollbar][data-dragging] {
|
|
background: var(--scroll-area-track-bg-hover, color-mix(in srgb, var(--color-neutral-track) 84%, transparent));
|
|
}
|
|
|
|
[data-scroll-area-scrollbar]:focus-visible {
|
|
outline: var(--focus-ring-width) solid var(--focus-ring-color);
|
|
outline-offset: 0;
|
|
}
|
|
|
|
[data-scroll-area-thumb] {
|
|
flex: 1;
|
|
border-radius: var(--scroll-area-thumb-radius, var(--radius-full));
|
|
background: var(--scroll-area-thumb-bg, var(--color-content-muted));
|
|
transition: background var(--scroll-area-transition-duration, var(--duration-fast))
|
|
var(--scroll-area-transition-ease, var(--ease-default));
|
|
min-inline-size: var(--scroll-area-thumb-min-size, var(--space-3));
|
|
min-block-size: var(--scroll-area-thumb-min-size, var(--space-3));
|
|
}
|
|
|
|
[data-scroll-area-thumb]:hover,
|
|
[data-scroll-area-scrollbar][data-dragging] [data-scroll-area-thumb] {
|
|
background: var(--scroll-area-thumb-bg-hover, var(--color-content-secondary));
|
|
}
|
|
|
|
[data-scroll-area-corner] {
|
|
position: absolute;
|
|
inset-block-end: 0;
|
|
inset-inline-end: 0;
|
|
inline-size: var(--scroll-area-scrollbar-size, var(--space-2-5));
|
|
block-size: var(--scroll-area-scrollbar-size, var(--space-2-5));
|
|
background: var(--scroll-area-track-bg, color-mix(in srgb, var(--color-neutral-track) 60%, transparent));
|
|
}
|
|
|
|
[data-scroll-area-corner][data-state='hidden'] {
|
|
opacity: 0;
|
|
}
|