Files
WRNexusJS/examples/component-showcase/app/pages/components/custom-scrollbar.wrn
T
ClintchizandClaude Opus 5 b56cb8cba5
Quality / quality (ubuntu-latest) (push) Failing after 6m8s
Quality / quality (windows-latest) (push) Canceled after 0s
feat(ui): build LayoutSplitter and CustomScrollbar for real
Both advertised behaviour they did not have. LayoutSplitter declared
resizeStart, resize and resizeEnd with no pointer handling whatsoever, so a
caller wired up @resize and received nothing, for ever, with no error, and its
props were columns, gap and maxWidth copied from a grid scaffold.
CustomScrollbar was the same shape with a scroll output.

The splitter now resizes. Dragging lives in the reactive runtime behind
data-wrn-splitter, because a pointermove fires far too often to route through
a client function and a state write made in that callback is dropped; the
resolved size is held on the container as a --wrn-split custom property and
the component grids from it. The handle is a real separator: arrow keys step
it, Home and End go to the bounds rather than to nothing, and it carries
aria-valuenow, aria-valuemin and aria-valuemax. minSize fixes both bounds so
neither pane can be dragged away and left unrecoverable.

CustomScrollbar is CSS rather than script -- scrollbar-width and
scrollbar-color with webkit rules for the engines that still need them -- and
its fake scroll output is removed rather than left unimplemented, since a
caller can listen for a plain scroll event.

The test harness needed a fix too: mount did not bind the window CustomEvent,
so the runtime built events from the host global and happy-dom listeners never
matched them, which made anything dispatched look silently lost.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 11:22:45 +05:30

108 lines
16 KiB
Plaintext

// Generated by scripts/generate-showcase.mjs. Do not edit directly.
page CustomScrollbarDetail {
layout = "showcase"
state playground_color = ctx.url.searchParams.get("pg_color") ?? "primary"
state playground_size = ctx.url.searchParams.get("pg_size") ?? "default"
state playground_axis = ctx.url.searchParams.get("pg_axis") ?? "vertical"
state playground_thickness = Number(ctx.url.searchParams.get("pg_thickness") ?? "8")
state playground_maxHeight = ctx.url.searchParams.get("pg_maxHeight") ?? "20rem"
state playground_radius = ctx.url.searchParams.get("pg_radius") ?? "999px"
state playground_class = ctx.url.searchParams.get("pg_class") ?? "showcase-instance showcase-instance--1"
seo {
title = "Custom Scrollbar"
description = "Theme-aware, responsive custom scrollbar component."
}
view {
<nav class="docs-breadcrumbs" aria-label="Breadcrumb">
<a href="/">Components</a><span aria-hidden="true">/</span><a href="/layout">Layout</a><span aria-hidden="true">/</span><span>Custom Scrollbar</span>
</nav>
<header class="detail-hero">
<div class="detail-hero-copy">
<span class="showcase-eyebrow">Layout component</span>
<h1>Custom Scrollbar</h1>
<p>Theme-aware, responsive custom scrollbar component.</p>
<div class="detail-badges"><span>7 props</span><span>1 slots</span><span>0 outputs</span><span>Theme ready</span><span>Responsive</span></div>
</div>
<div class="detail-hero-icon"><span class="icon-[lucide--component] size-10" aria-hidden="true"></span></div>
</header>
<section id="playground" class="detail-section playground-section" data-playground data-playground-component="CustomScrollbar" data-playground-public-tag="true" data-playground-has-slot="true" data-playground-events="">
<div class="detail-section-heading"><span>Interactive playground</span><h2>Configure Custom Scrollbar</h2><p>Change any prop and inspect the server-rendered component immediately.</p></div>
<div class="playground-workbench">
<div class="playground-preview">
<div class="demo-browser-chrome"><span></span><span></span><span></span><small>Live preview</small></div>
<div class="playground-preview-source" data-playground-preview><CustomScrollbar color='{playground_color}' size='{playground_size}' axis='{playground_axis}' thickness='{playground_thickness}' maxHeight='{playground_maxHeight}' radius='{playground_radius}' class='{playground_class}'><div class="showcase-scroll-demo"><p>Line 1. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 2. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 3. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 4. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 5. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 6. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 7. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 8. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 9. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 10. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 11. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 12. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p></div></CustomScrollbar></div>
<section class="playground-code" aria-label="Current component code">
<header><span><span class="icon-[lucide--code-2] size-4" aria-hidden="true"></span>Component code</span><button type="button" data-playground-copy><span class="icon-[lucide--copy] size-4" aria-hidden="true"></span>Copy</button></header>
<pre><code data-playground-code>&lt;CustomScrollbar /&gt;</code></pre>
</section>
<div class="playground-status" data-playground-status aria-live="polite"></div>
</div>
<form class="playground-controls" data-playground-form>
<header><div><span>Component props</span><strong>7 controls</strong></div><button type="reset"><span class="icon-[lucide--rotate-ccw] size-4" aria-hidden="true"></span>Reset</button></header>
<div class="playground-fields"><label class="playground-field" for="playground-custom-scrollbar-color"><span><strong>color</strong><small>string</small></span><select id="playground-custom-scrollbar-color" name="pg_color"><option value="primary" selected>primary</option><option value="secondary">secondary</option><option value="success">success</option><option value="warning">warning</option><option value="danger">danger</option><option value="info">info</option></select></label><label class="playground-field" for="playground-custom-scrollbar-size"><span><strong>size</strong><small>string</small></span><select id="playground-custom-scrollbar-size" name="pg_size"><option value="default" selected>default</option><option value="xs">xs</option><option value="sm">sm</option><option value="md">md</option><option value="lg">lg</option><option value="xl">xl</option><option value="icon">icon</option><option value="icon-xs">icon-xs</option><option value="icon-sm">icon-sm</option><option value="icon-lg">icon-lg</option></select></label><label class="playground-field" for="playground-custom-scrollbar-axis"><span><strong>axis</strong><small>string</small></span><input id="playground-custom-scrollbar-axis" name="pg_axis" type="text" value="vertical" /></label><label class="playground-field" for="playground-custom-scrollbar-thickness"><span><strong>thickness</strong><small>number</small></span><input id="playground-custom-scrollbar-thickness" name="pg_thickness" type="number" value="8" /></label><label class="playground-field" for="playground-custom-scrollbar-maxHeight"><span><strong>max Height</strong><small>string</small></span><input id="playground-custom-scrollbar-maxHeight" name="pg_maxHeight" type="text" value="20rem" /></label><label class="playground-field" for="playground-custom-scrollbar-radius"><span><strong>radius</strong><small>string</small></span><input id="playground-custom-scrollbar-radius" name="pg_radius" type="text" value="999px" /></label><label class="playground-field" for="playground-custom-scrollbar-class"><span><strong>class</strong><small>string</small></span><input id="playground-custom-scrollbar-class" name="pg_class" type="text" value="showcase-instance showcase-instance--1" /></label></div>
</form>
</div>
</section>
<div class="detail-layout">
<main class="detail-content">
<section class="detail-section"><div class="detail-section-heading"><span>Live examples</span><h2>Designed for real product surfaces</h2><p>Compare configurations and resize the browser to check responsive behavior.</p></div><div class="demo-list">
<article class="demo-case">
<header class="demo-case-header">
<div><span class="demo-case-eyebrow">Recommended</span><h2>Themed scrolling region</h2><p>Scrollbar appearance is CSS rather than script: scrollbar-width and scrollbar-color are the standard properties, with webkit rules for the engines that still need them. The thumb follows the component colour.</p></div>
<span class="demo-case-number">01</span>
</header>
<div class="demo-workbench">
<div id="custom-scrollbar-demo-1" class="demo-canvas demo-canvas--1">
<div class="demo-browser-chrome"><span></span><span></span><span></span><small>Live preview</small></div>
<div class="demo-render"><CustomScrollbar size="default" axis="vertical" thickness="8" maxHeight="14rem" radius="999px" class="showcase-instance showcase-instance--1"><div class="showcase-scroll-demo"><p>Line 1. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 2. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 3. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 4. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 5. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 6. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 7. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 8. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 9. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 10. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 11. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p><p>Line 12. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.</p></div></CustomScrollbar></div>
</div>
<section class="demo-code" aria-label="Themed scrolling region usage">
<header><span><span class="icon-[lucide--code-2] size-4" aria-hidden="true"></span>Component usage</span><small>.wrn</small></header>
<pre><code>&lt;CustomScrollbar
maxHeight="14rem"&gt;
&lt;div class="showcase-scroll-demo"&gt;&lt;p&gt;Line 1. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.&lt;/p&gt;&lt;p&gt;Line 2. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.&lt;/p&gt;&lt;p&gt;Line 3. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.&lt;/p&gt;&lt;p&gt;Line 4. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.&lt;/p&gt;&lt;p&gt;Line 5. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.&lt;/p&gt;&lt;p&gt;Line 6. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.&lt;/p&gt;&lt;p&gt;Line 7. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.&lt;/p&gt;&lt;p&gt;Line 8. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.&lt;/p&gt;&lt;p&gt;Line 9. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.&lt;/p&gt;&lt;p&gt;Line 10. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.&lt;/p&gt;&lt;p&gt;Line 11. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.&lt;/p&gt;&lt;p&gt;Line 12. A scrolling region only shows its scrollbar when the content actually overflows, so this demo ships enough of it.&lt;/p&gt;&lt;/div&gt;
&lt;/CustomScrollbar&gt;</code></pre>
</section>
</div>
</article>
<article class="demo-case">
<header class="demo-case-header">
<div><span class="demo-case-eyebrow">Advanced</span><h2>Horizontal, thicker track</h2><p>A horizontal region with a heavier track. thickness is clamped, since it arrives as an attribute and a scrollbar wider than its content helps nobody.</p></div>
<span class="demo-case-number">02</span>
</header>
<div class="demo-workbench">
<div id="custom-scrollbar-demo-2" class="demo-canvas demo-canvas--2">
<div class="demo-browser-chrome"><span></span><span></span><span></span><small>Live preview</small></div>
<div class="demo-render"><CustomScrollbar color="info" size="sm" axis="horizontal" thickness="14" maxHeight="8rem" radius="4px" class="showcase-instance showcase-instance--2"><div class="showcase-scroll-demo showcase-scroll-demo--wide"><span>Column 1</span><span>Column 2</span><span>Column 3</span><span>Column 4</span><span>Column 5</span><span>Column 6</span><span>Column 7</span><span>Column 8</span><span>Column 9</span><span>Column 10</span><span>Column 11</span><span>Column 12</span><span>Column 13</span><span>Column 14</span></div></CustomScrollbar></div>
</div>
<section class="demo-code" aria-label="Horizontal, thicker track usage">
<header><span><span class="icon-[lucide--code-2] size-4" aria-hidden="true"></span>Component usage</span><small>.wrn</small></header>
<pre><code>&lt;CustomScrollbar
color="info"
size="sm"
axis="horizontal"
thickness="14"
maxHeight="8rem"
radius="4px"&gt;
&lt;div class="showcase-scroll-demo showcase-scroll-demo--wide"&gt;&lt;span&gt;Column 1&lt;/span&gt;&lt;span&gt;Column 2&lt;/span&gt;&lt;span&gt;Column 3&lt;/span&gt;&lt;span&gt;Column 4&lt;/span&gt;&lt;span&gt;Column 5&lt;/span&gt;&lt;span&gt;Column 6&lt;/span&gt;&lt;span&gt;Column 7&lt;/span&gt;&lt;span&gt;Column 8&lt;/span&gt;&lt;span&gt;Column 9&lt;/span&gt;&lt;span&gt;Column 10&lt;/span&gt;&lt;span&gt;Column 11&lt;/span&gt;&lt;span&gt;Column 12&lt;/span&gt;&lt;span&gt;Column 13&lt;/span&gt;&lt;span&gt;Column 14&lt;/span&gt;&lt;/div&gt;
&lt;/CustomScrollbar&gt;</code></pre>
</section>
</div>
</article></div></section>
<section id="api" class="detail-section"><div class="detail-section-heading"><span>Component API</span><h2>Props and configuration</h2><p>All content and behavior shown above is supplied through these props and slots.</p></div><div class="docs-table-wrap"><table class="docs-table"><thead><tr><th>Prop</th><th>Type</th><th>Default</th><th>Required</th></tr></thead><tbody><tr><td><code>color</code></td><td>string</td><td><code>"primary"</code></td><td>No</td></tr><tr><td><code>size</code></td><td>string</td><td><code>"default"</code></td><td>No</td></tr><tr><td><code>axis</code></td><td>string</td><td><code>"vertical"</code></td><td>No</td></tr><tr><td><code>thickness</code></td><td>number</td><td><code>8</code></td><td>No</td></tr><tr><td><code>maxHeight</code></td><td>string</td><td><code>"20rem"</code></td><td>No</td></tr><tr><td><code>radius</code></td><td>string</td><td><code>"999px"</code></td><td>No</td></tr><tr><td><code>class</code></td><td>string</td><td><code>""</code></td><td>No</td></tr></tbody></table></div></section>
</main>
<aside class="detail-aside">
<div class="detail-toc"><strong>On this page</strong><a href="#playground">Playground</a><a href="#custom-scrollbar-demo-1">Themed scrolling region</a><a href="#custom-scrollbar-demo-2">Horizontal, thicker track</a><a href="#api">Props API</a></div>
</aside>
</div>
<nav class="detail-pagination">
<a href="/components/container"><small>Previous</small><strong>← Container</strong></a>
<a href="/components/divider"><small>Next</small><strong>Divider →</strong></a>
</nav>
}
}