Scroll Area
Free · MITHeadless, accessible, and styled entirely through the attribute surface below — bring your own CSS system, or start from the reference theme.
Install
Without a flag you get no styling at all — plain HTML carrying the attribute surface below. --theme copies the same theme in two dialects;--bridge lumos re-points either one onto Lumos for Astro's tokens (it needs a theme to re-point, and Lumos itself, already in your project).
Unstyled — behavior only, you write the CSS
npx astrocraft-ui add media/scroll-areaPlain-CSS theme — no build step
npx astrocraft-ui add media/scroll-area --theme cssTailwind theme — needs Tailwind v4
npx astrocraft-ui add media/scroll-area --theme tailwindPlain-CSS theme, re-pointed onto Lumos tokens
npx astrocraft-ui add media/scroll-area --theme css --bridge lumosTailwind theme, re-pointed onto Lumos tokens
npx astrocraft-ui add media/scroll-area --theme tailwind --bridge lumosLive demo
ScrollArea
One attribute is the whole primitive. Tab into the log below and scroll it with the arrow keys — then note that it contains nothing focusable, which is the case browsers now handle themselves, and that the second one contains links, which is the case they still do not. Both are reachable here because tabindex="0" is in the markup rather than left to a heuristic.
- 12:04:01 build started
- 12:04:03 resolving 231 modules
- 12:04:06 transformed src/components/ui/**
- 12:04:09 bundling client scripts
- 12:04:11 emitted dist/_astro/client.js
- 12:04:12 emitted dist/index.html
- 12:04:12 build finished in 11.4s
- 12:04:12 0 errors, 0 warnings
- 12:04:13 preview on http://localhost:4321
12:04:01 build started — details
12:04:03 resolving 231 modules — details
12:04:06 transformed src/components/ui/** — details
12:04:09 bundling client scripts — details
12:04:11 emitted dist/_astro/client.js — details
12:04:12 emitted dist/index.html — details
12:04:12 build finished in 11.4s — details
12:04:12 0 errors, 0 warnings — details
12:04:13 preview on http://localhost:4321 — details
Attribute surface
With no class-merging machinery, these attributes are the whole extension point. State is a data-attribute or a native attribute; visibility is the hiddenattribute. Generated from source by pnpm slots.
| Component | Slots | Variants | Runtime state | Native state |
|---|---|---|---|---|
| `ScrollArea.astro` | `scroll-area` | `data-orientation`: `vertical` · `horizontal` · `both` | — | — |
Source
What the command copies — 2 files, alias-free so the copy lands standing alone. This is the whole component, shown without its comments; the files you install keep them, with the reasoning behind every rule.
---
import "../../../../styles/structure.css";
import type { HTMLAttributes } from "astro/types";
type Props = HTMLAttributes<"div"> & {
orientation?: "vertical" | "horizontal" | "both";
label?: string;
};
const {
orientation = "vertical",
label = "Scrollable content",
class: className,
...rest
} = Astro.props;
---
<div
tabindex="0"
role="group"
aria-label={label}
data-slot="scroll-area"
data-orientation={orientation}
class={className}
{...rest}
>
<slot />
</div>
import ScrollArea from "./ScrollArea.astro";
export { ScrollArea };
export default ScrollArea;
What you get
The component source, copied into your project by npx astrocraft-ui add media/scroll-area — no package dependency, no CSS to fight. Whatever the paid blocks compose, this is it.