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.
svelte-kit-vice/src/uix/eidos/components/scroll-area
dev b8637976a0
feat(uix): elevation archetype (A1) + coherence audit + popover/dialog/option-list polish
4 months ago
..
README.md feat(layout): Layout Batch 3 — scroll-area, separator, splitter + splitter sema rewire 5 months ago
index.ts feat(scroll-area): add size/radius/scrollbars props + fix scroll-type race 5 months ago
scroll-area-corner.svelte feat(layout): Layout Batch 3 — scroll-area, separator, splitter + splitter sema rewire 5 months ago
scroll-area-scrollbar.svelte feat(layout): Layout Batch 3 — scroll-area, separator, splitter + splitter sema rewire 5 months ago
scroll-area-thumb.svelte feat(layout): Layout Batch 3 — scroll-area, separator, splitter + splitter sema rewire 5 months ago
scroll-area-viewport.svelte feat(layout): Layout Batch 3 — scroll-area, separator, splitter + splitter sema rewire 5 months ago
scroll-area.css feat(uix): elevation archetype (A1) + coherence audit + popover/dialog/option-list polish 4 months ago
scroll-area.svelte feat(uix): elevation archetype (A1) + coherence audit + popover/dialog/option-list polish 4 months ago
types.ts feat(uix): elevation archetype (A1) + coherence audit + popover/dialog/option-list polish 4 months ago

README.md

Eidos ScrollArea

Custom-scrollbar container. Hides the native scrollbar and renders a custom-painted track + thumb on top of a scrollable viewport. The headless Soma layer owns the scroll math, the visibility timer (type='hover' | 'scroll' | 'auto' | 'always') and the thumb position. Eidos paints chrome only — track, thumb, corner, hover and drag states.

Superficie

<ScrollArea type="hover" scrollHideDelay={600}>
  <ScrollArea.Viewport>
    <!-- scrollable content goes here -->
  </ScrollArea.Viewport>
  <ScrollArea.Scrollbar orientation="vertical">
    <ScrollArea.Thumb />
  </ScrollArea.Scrollbar>
  <ScrollArea.Scrollbar orientation="horizontal">
    <ScrollArea.Thumb />
  </ScrollArea.Scrollbar>
  <ScrollArea.Corner />
</ScrollArea>

The compound shape mirrors Radix Primitives and Bits UI 1:1: the consumer composes every part explicitly so a vertical-only ScrollArea omits the horizontal scrollbar (and the Corner) without prop gymnastics on the root.

Baseline

Origin: air/components/layout/scroll-area (branch morfo-runtime), which itself wrapped the legacy Terra scroll-area implementation.

Adjustments applied during the port:

  • Drop the air-scroll-area-* class namespace. Eidos targets [data-scroll-area] / [data-scroll-area-viewport] / etc., emitted by the Soma provider so the recipe never depends on consumer classes.
  • Replace the --air-scroll-area-* token namespace with bare --scroll-area-* names per the eidos naming convention. The identifiers map to canonical UIX primitives:
    • --scroll-area-track-bg → color-mix of --color-neutral-track
    • --scroll-area-thumb-bg → --color-content-muted
    • --scroll-area-scrollbar-size → --space-2-5
    • --scroll-area-thumb-min-size → --space-3
  • Soma is the source of truth. The eidos wrappers are pure pass-through with no additional ARIA, no additional data-attrs, no extra props. The morfo (src/uix/morfo/components/scroll-area.ts) declares the contract — Provider / Viewport / Scrollbar / Thumb / Corner — and the Soma provider emits the markers; eidos just styles them.
  • Hover and drag states use the morfo's data-hover / data-dragging presence flags rather than CSS pseudo-classes, matching the rest of the UIX runtime.

Comparativa

Capability UIX (eidos) Radix Primitives Bits UI react-aria-components
Compound API (Provider/Viewport/Scrollbar/Thumb/Corner) Yes Yes Yes Partial — single ScrollArea + virtual list
Scrollbar visibility modes hover / scroll / auto / always (soma) Same four Same four always / auto
Horizontal + vertical scrollbars composed independently Yes Yes Yes Yes
Corner element Yes Yes Yes No
Auto-hide delay configurable Yes (scrollHideDelay on soma) Yes (scrollHideDelay) Yes No
RTL support Yes (via soma dir) Yes Yes Yes
Touch action discipline touch-action: none on bar (recipe) Same Same Same
Drag state styling Yes (data-dragging) Yes (data-state) Yes n/a
Custom thumb min size Yes (--scroll-area-thumb-min-size) CSS variable CSS variable n/a

Decisiones

  • Soma is the source of state and ARIA. Eidos adds no evaluative props — neither size nor variant. Scrollbar visibility timing belongs to soma's type / scrollHideDelay pair; the chrome's color and thickness live in CSS tokens.
  • Compound API only. No flat <ScrollArea> shortcut. The consumer composes Viewport + Scrollbar(s) + Corner explicitly so a single-axis container doesn't pay for the unused parts. Mirrors Radix Primitives.
  • Thumb always sits inside Scrollbar. The morfo declares it as an optional child (no compositional gymnastics needed): the Thumb's position and ARIA are wired through the parent Scrollbar's provider context, so the consumer can omit the Thumb to render a track-only scrollbar (rare but legal).
  • No prop on the eidos root. The headless type / scrollHideDelay / dir go through pass-through; the demo's controls bind directly to those soma props.

Eventos Sema

ScrollArea declares 0 events. Scrolling is continuous and non-evaluative — there is no commit, no emerge, no contact. Soma emits the data-state="visible" | "hidden" markers as the scrollbar visibility transitions; if a consumer needs a sema signature on visibility change, it composes ScrollArea inside a larger primitive (e.g. a Drawer or Popover) that owns the relevant verb.

The Scrollbar drag does not commit a sema event either: scroll position is a viewport concern, not a value the surrounding form should react to. Slider / NumberField / Splitter are the right primitives when a drag commits a value.

Gaps

Gap Disposition Detail
viewport style escape for inner padding implementar Today the consumer applies padding inside the viewport's child. Adding a padding prop on Viewport (passed through to soma) would centralise the API.
Sema event on scroll-to-edge (commit-end) diferir Some apps need to know when the viewport hits top / bottom / start / end. Soma can emit data-at-top / data-at-bottom already; promoting that to a sema event is a v2 decision.
intersectionThreshold for sticky headers diferir Out of scope for the layout-primitive sweep. Belongs to a future <Sticky> primitive on top of ScrollArea.
Theme variant subtle / strong for track descartar Token overrides cover this already — var(--scroll-area-track-bg: …) is enough; a typed variant would manufacture an unused surface.
Auto-handle hover targeting via padding-box descartar Air shipped the padding directly on the bar; modern UAs honour the existing approach.

Referencias

Passive justification

The morfo declares scope: ['soma'] (the headless layer owns hover-timer state, drag math and scroll position), but no semantic events. Scrolling itself is continuous and non-evaluative — the user moves the viewport, the surrounding application reads the scroll position; there is no commit / emerge / contact verb to project onto a perceptual channel.

The Scrollbar's data-state (visible / hidden) IS a transition, but it is a CSS visibility transition driven by the soma timer rather than a user-authored intent. Promoting it to a sema event would either:

  • fire on every show / hide of the scrollbar (high-frequency pollution, equivalent to firing on every frame of a drag), or
  • require an arbitrary debounce that the consumer would have to reverse-engineer.

For consumers that need to react to scroll position (sticky headers, lazy-load), data-at-top / data-at-bottom / data-at-left / data-at-right on the Viewport are sufficient and do not require a sema layer.

Powered by TurnKey Linux.