ScrollArea
A box with its own scrollbars and a height limit, fading at the edges where there is more to see.
Demo
0.18 — fixes and small improvements, release 1 of the list; the box fades at the edge where there is more.
0.17 — fixes and small improvements, release 2 of the list; the box fades at the edge where there is more.
0.16 — fixes and small improvements, release 3 of the list; the box fades at the edge where there is more.
0.15 — fixes and small improvements, release 4 of the list; the box fades at the edge where there is more.
0.14 — fixes and small improvements, release 5 of the list; the box fades at the edge where there is more.
0.13 — fixes and small improvements, release 6 of the list; the box fades at the edge where there is more.
0.12 — fixes and small improvements, release 7 of the list; the box fades at the edge where there is more.
0.11 — fixes and small improvements, release 8 of the list; the box fades at the edge where there is more.
0.10 — fixes and small improvements, release 9 of the list; the box fades at the edge where there is more.
0.9 — fixes and small improvements, release 10 of the list; the box fades at the edge where there is more.
0.8 — fixes and small improvements, release 11 of the list; the box fades at the edge where there is more.
0.7 — fixes and small improvements, release 12 of the list; the box fades at the edge where there is more.
0.6 — fixes and small improvements, release 13 of the list; the box fades at the edge where there is more.
0.5 — fixes and small improvements, release 14 of the list; the box fades at the edge where there is more.
0.4 — fixes and small improvements, release 15 of the list; the box fades at the edge where there is more.
0.3 — fixes and small improvements, release 16 of the list; the box fades at the edge where there is more.
0.2 — fixes and small improvements, release 17 of the list; the box fades at the edge where there is more.
0.1 — fixes and small improvements, release 18 of the list; the box fades at the edge where there is more.
Each box takes focus, so the arrow keys scroll it; screen readers hear its name.
Get it
It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.
$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/scroll_area.qweimport ui/theme.qwe use theme_cssuse ui/scroll_area.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
view ScrollAreaDemo() = <div.demo-stack> <ScrollArea id="scroll-area-demo-notes" height="220px" label="Release notes" pad={4}> {for v in range(1, 19) => <p style="margin: 0 0 10px"><b>0.{19 - v}</b> — fixes and small improvements, release {v} of the list; the box fades at the edge where there is more.</p>} </ScrollArea> <ScrollArea id="scroll-area-demo-steps" axis="x" height={null} label="Steps"> <div style="display: flex; gap: 12px; width: max-content; padding: 4px 0"> {for i in range(1, 13) => <div style="width: 160px; padding: 14px; border: 1px solid var(--ui-border); border-radius: var(--ui-radius-sm); background: var(--ui-subtle); font-size: 13px">Step {i}</div>} </div> </ScrollArea> <p.demo-note>Each box takes focus, so the arrow keys scroll it; screen readers hear its name.</p> </div>Source
// ScrollArea: a box with scrollbars of its own, no taller (or wider) than it is// told, whose content fades out at an edge where there is more to see. The fades// follow the scrolling in CSS alone (scroll-driven animations); browsers without// them show no fade. The box takes focus, so the keyboard scrolls it, and screen// readers hear it as a region with its name.//// <ScrollArea height="280px" label="Release notes"> {for r in releases => <p>...</p>} </ScrollArea>// <ScrollArea axis="x" label="Steps"> <div style="display: flex; gap: 12px"> ... </div> </ScrollArea>//// height: how tall it may be, as CSS ("280px"); width: how wide (none).// axis: "y" (down, the default), "x" (across) or "both".// label: its name for screen readers; without one it is a plain box.// fade: the fades at the edges (true). pad: room inside, in pixels (0).// thin: a slim scrollbar (true).import ui/theme.qwe use ui_clslet scroll_area_css = css`.ui-scroll { position: relative; max-height: var(--ui-scroll-h, none); max-width: var(--ui-scroll-w, none); min-width: 0; overflow: hidden auto; overscroll-behavior: contain; padding: var(--ui-scroll-pad, 0); outline: none; border-radius: var(--ui-radius-sm); color: var(--ui-fg); font: 15px/1.5 var(--ui-font);}.ui-scroll-x { overflow: auto hidden; }.ui-scroll-both { overflow: auto; }.ui-scroll-thin { scrollbar-width: thin; scrollbar-color: var(--ui-strong) transparent; }.ui-scroll:focus-visible { box-shadow: var(--ui-focus); }.ui-scroll-fade { position: sticky; z-index: 1; display: block; pointer-events: none; opacity: 0; }.ui-scroll-top, .ui-scroll-bottom { left: 0; right: 0; height: 0; }.ui-scroll-top { top: 0; }.ui-scroll-bottom { bottom: 0; }.ui-scroll-top::before, .ui-scroll-bottom::before { content: ""; position: absolute; left: 0; right: 0; height: 28px; }.ui-scroll-top::before { top: 0; background: linear-gradient(var(--ui-scroll-bg, var(--ui-bg)), transparent); }.ui-scroll-bottom::before { bottom: 0; background: linear-gradient(transparent, var(--ui-scroll-bg, var(--ui-bg))); }.ui-scroll-start, .ui-scroll-end { top: 0; bottom: 0; width: 0; height: 0; float: left; }.ui-scroll-start { left: 0; }.ui-scroll-end { right: 0; float: right; }.ui-scroll-start::before, .ui-scroll-end::before { content: ""; position: absolute; top: 0; bottom: 0; width: 28px; }.ui-scroll-start::before { left: 0; background: linear-gradient(to right, var(--ui-scroll-bg, var(--ui-bg)), transparent); }.ui-scroll-end::before { right: 0; background: linear-gradient(to left, var(--ui-scroll-bg, var(--ui-bg)), transparent); }@supports (animation-timeline: scroll()) { .ui-scroll-top { animation: ui-scroll-show linear both; animation-timeline: scroll(nearest block); animation-range: 0 40px; } .ui-scroll-bottom { animation: ui-scroll-hide linear both; animation-timeline: scroll(nearest block); animation-range: calc(100% - 40px) 100%; } .ui-scroll-start { animation: ui-scroll-show linear both; animation-timeline: scroll(nearest inline); animation-range: 0 40px; } .ui-scroll-end { animation: ui-scroll-hide linear both; animation-timeline: scroll(nearest inline); animation-range: calc(100% - 40px) 100%; }}@keyframes ui-scroll-show { from { opacity: 0; } to { opacity: 1; } }@keyframes ui-scroll-hide { from { opacity: 1; } to { opacity: 0; } }`view ScrollArea(height: str? = "280px", width: str? = null, axis: str = "y", label: str? = null, fade: bool = true, pad: int = 0, thin: bool = true, id: str? = null, class: str? = null, children) { style global scroll_area_css let across = axis == "x" || axis == "both" let down = axis == "y" || axis == "both" <div class={ui_cls(["ui-scroll", axis == "x" && "ui-scroll-x", axis == "both" && "ui-scroll-both", thin && "ui-scroll-thin", class])} id={id} tabindex={label != null ? "0" : null} role={label != null ? "region" : null} aria-label={label} style={`--ui-scroll-h: ${height ?? "none"}; --ui-scroll-w: ${width ?? "none"}; --ui-scroll-pad: ${pad}px`}> {fade && down && <span.ui-scroll-fade.ui-scroll-top aria-hidden="true"></span>} {fade && across && <span.ui-scroll-fade.ui-scroll-start aria-hidden="true"></span>} {children} {fade && across && <span.ui-scroll-fade.ui-scroll-end aria-hidden="true"></span>} {fade && down && <span.ui-scroll-fade.ui-scroll-bottom aria-hidden="true"></span>} </div>}Related
Thread 0 posts
On ScrollArea: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.