vlist 2.8.0 → 3.0.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.github.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # vlist
2
2
 
3
- The virtual list library for every framework. Ultra efficient, batteries-included, and accessible with composable plugins — in 9.9 KB.
3
+ The virtual list library for every framework. Ultra efficient, batteries-included, and accessible with composable plugins.
4
4
 
5
- **v2.8.0** — [Changelog](./CHANGELOG.md) · autosize `remeasure(index?)`; framework adapters can select the `vlist/synthetic` entry via `VListConfig.factory`; `vlist/config` no longer warns about the scale stub; deprecation notices for 3.0 (`scroll.mode`, `scroll.runway`, native scrollbar values, old plugin hooks).
5
+ **v3.0.0-next.1** (prerelease on the npm `next` tag) Synthetic input by default, native input through `vlist/native`, and removal of the deprecated scroll configuration and plugin hooks. See the [changelog](https://github.com/floor/vlist/blob/next/CHANGELOG.md).
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/vlist.svg)](https://www.npmjs.com/package/vlist)
8
8
  [![bundle size](https://img.shields.io/bundlephobia/minzip/vlist)](https://bundlephobia.com/package/vlist)
@@ -11,7 +11,7 @@ The virtual list library for every framework. Ultra efficient, batteries-include
11
11
 
12
12
  - **Accessible** — WAI-ARIA, 2D keyboard navigation, focus recovery, screen-reader DOM ordering
13
13
  - **Zero dependencies** — framework-agnostic core with tiny adapters for Vue, Svelte, Solid, React
14
- - **9.9 KB gzipped** — composable plugins with perfect tree-shaking
14
+ - **11.4 KB gzipped (3.0 prerelease)** — composable plugins with perfect tree-shaking
15
15
  - **Constant memory** — ~0.1 MB overhead at any scale, from 10K to 1M+ items
16
16
  - **Tree, grid, masonry, carousel, table, groups, data, selection, search, sortable, transition** — all opt-in
17
17
  - **Axis-neutral** — vertical and horizontal scrolling through a single code path, all plugins work in both orientations
@@ -42,24 +42,24 @@ The virtual list library for every framework. Ultra efficient, batteries-include
42
42
 
43
43
  ```bash
44
44
  npm install vlist # vanilla JS
45
+ npm install vlist@next # 3.0 prerelease; latest stays on 2.8
45
46
  npm install vlist vlist-vue # or vlist-svelte / vlist-solidjs / vlist-react
46
47
  ```
47
48
 
48
- With vlist 2.8 and an adapter that forwards the `factory` option, opt into synthetic input explicitly:
49
+ Adapters use synthetic input by default. With an adapter that forwards `factory`, select native input explicitly:
49
50
 
50
51
  ```ts
51
52
  import { useVList } from "vlist-react";
52
- import { createVList } from "vlist/synthetic";
53
+ import { createVList } from "vlist/native";
53
54
 
54
55
  useVList({
55
56
  factory: createVList,
56
- scroll: { mode: "synthetic" },
57
57
  items,
58
58
  item: { height: 48, template: item => String(item.id) },
59
59
  });
60
60
  ```
61
61
 
62
- The same factory option is available to the other adapters. `vlist/config` keeps the synthetic driver out of its default bundle; importing the factory opts in. The factory is structural configuration: changing it requires recreating the list.
62
+ The same factory option is available to the other adapters. `vlist/config` defaults to synthetic input. The factory is structural configuration: changing it requires recreating the list.
63
63
 
64
64
  ## Quick Start
65
65
 
@@ -106,73 +106,84 @@ const list = createVList({
106
106
  ])
107
107
  ```
108
108
 
109
- ## Synthetic scroll input
110
-
111
- The opt-in `vlist/synthetic` entry adds `scroll.mode: 'synthetic'` alongside native and bounded modes. Native remains the default. Import the factory from this entry and plugins from `vlist`:
112
- Bounded mode note: on touch devices a long native fling can outrun the 2x runway and stall at its edge (measured at 145-226% of a 16x runway on an iPhone SE and a Pixel 8a). Prefer synthetic mode for touch-heavy lists; bounded remains the right choice for wheel and keyboard driven lists.
109
+ ## Scroll input
113
110
 
111
+ Synthetic scrolling is the default in 3.0. Import `createVList` and plugins from `vlist`; there is no `scroll.mode` option. The deprecated `vlist/synthetic` entry remains an alias of the same factory.
114
112
 
115
113
  ```typescript
116
- import { createVList } from 'vlist/synthetic'
117
- import { scrollbar } from 'vlist'
114
+ import { createVList, scrollbar } from 'vlist'
118
115
  import 'vlist/styles'
119
116
 
120
117
  const list = createVList({
121
118
  container: '#my-list',
122
119
  items: Array.from({ length: 1000 }, (_, id) => ({ id, name: `Row ${id}` })),
123
120
  item: { height: 48, template: item => `<div>${item.name}</div>` },
124
- scroll: { mode: 'synthetic' },
125
121
  }, [scrollbar()])
126
122
  ```
127
123
 
128
- Supported plugins are **table, groups, snapshots, scrollbar, autosize, transition, selection and a11y**. Existing plugin conflicts still apply; this list does not imply that all eight can be combined. `page()`, `carousel()` and `sortable()` throw when configured with synthetic mode. Carousel uses wrap scrolling, which this release does not support with synthetic input.
124
+ For native scrolling, import the factory from `vlist/native` and plugins from `vlist`. Native scrolling is required for `carousel()`, `sortable()`, and horizontal RTL lists; configuring these with the default entry throws. The native entry preserves carousel wrapping through a private runway implementation. Bounded scrolling is no longer a public mode.
125
+
126
+ ```typescript
127
+ import { createVList } from 'vlist/native'
128
+ import { carousel } from 'vlist'
129
+
130
+ const list = createVList({
131
+ container: '#slides',
132
+ orientation: 'horizontal',
133
+ items: slides,
134
+ item: { width: 320, template: renderSlide },
135
+ }, [carousel()])
136
+ ```
137
+
138
+ `page()` uses native document scrolling through an external source with either entry. Its content must fit the 16,777,216 px document element limit; creation throws above that limit when the size is known, and later growth warns once. A deferred custom renderer whose size is first committed during rendering also warns once. Use default viewport scrolling for larger lists. Native viewport lists warn once when content exceeds their browser-size safety limit.
129
139
 
130
140
  Known limitations:
131
141
 
132
- - RTL horizontal lists throw in synthetic mode in this release; use native mode. Vertical lists on RTL pages are supported. RTL support for the synthetic driver is planned as a non-breaking addition.
133
- - Same-axis touch stops at either boundary with no parent handoff, including gestures that start inside an edge-pinned list. Use native mode when touch gestures must scroll the parent page at a boundary.
134
- - The native main-axis scrollbar is absent. Provide a custom scrollbar, such as `scrollbar()` above. Its accessibility release gate remains open; synthetic mode is not a completed scrollbar-accessibility sign-off.
142
+ - Horizontal RTL lists require `vlist/native`. Vertical lists and tables support `dir="rtl"` on the container, including cross-axis wheel movement, aligned table headers and keyboard column navigation.
143
+ - Same-axis touch stops at either boundary with no parent handoff, including gestures starting inside an edge-pinned list. Use `vlist/native` when boundary gestures must scroll the parent page.
144
+ - The default entry has no native main-axis scrollbar. Add `scrollbar()` for an accessible custom scrollbar; native visibility options belong to `vlist/native`.
135
145
  - Inertia initializes its frame clock on the first frame after release, adding up to one frame of release latency.
136
146
  - Wheel input at an edge is left to the page when it cannot move the list. Native cross-axis scrolling remains available.
137
147
 
138
- Measurement corrections from autosize preserve ongoing motion. Synthetic input adds **2.6 KB gzipped** over the base entry (**12.5 KB** total before plugins); ordinary `vlist` imports exclude this driver. See [RFC-014](https://github.com/floor/vlist/discussions/127).
139
-
140
- ## Deprecated in 2.8, removed in 3.0
148
+ Measurement corrections from autosize preserve ongoing motion. Existing plugin conflicts still apply. See the [scroll input contract](https://vlist.io/docs/rfcs/RFC-014-Scroll-Input-Model).
141
149
 
142
- These notices prepare the 3.0 migration; 2.x behavior and defaults stay unchanged. `vlist/native` and `setScrollSource` are 3.0 replacements, not 2.8 APIs. Bounded mode remains supported in 2.x, including carousel and sortable; it emits no deprecation warning.
150
+ ## Migrating to 3.0
143
151
 
144
- | Option or API | Replacement | Since |
145
- |---|---|---|
146
- | `scroll.mode` | In 3.0, synthetic input in core; import `vlist/native` for native scrolling. Bounded is removed. | 2.8 |
147
- | `scroll.runway` | Remove it when moving to 3.0 synthetic core. | 2.8 |
148
- | `scroll.scrollbar: "native"` | In 3.0 use `vlist/native` for a browser scrollbar, or `scrollbar()` in synthetic core. | 2.8 |
149
- | `scroll.scrollbar: "none"` | In 3.0 synthetic core has no native main-axis scrollbar to hide; native hiding belongs to `vlist/native`. | 2.8 |
150
- | `PluginContext.setScrollFns`, `disableDefaultScroll` | Use `setScrollSource` when upgrading to 3.0. | 2.8 |
151
- | `scale()` | Use `scroll: { mode: "synthetic" }` from `vlist/synthetic`; bounded remains available in 2.x. | 2.4; guidance updated in 2.8 |
152
+ | Removed public API | Replacement |
153
+ |---|---|
154
+ | `scroll.mode` (all values, both entries) | Omit it. `vlist` provides synthetic input; import `vlist/native` for native scrolling. |
155
+ | `scroll.runway` (both entries) | Remove it. Default synthetic scrolling supports huge lists without a native runway. |
156
+ | Core `scroll.scrollbar: "native"` or `"none"` | Use `scrollbar()` with `vlist`, or select `vlist/native` to retain either string. Native types are exported as `NativeScrollConfig` and `NativeCreateVListConfig`. |
157
+ | `PluginContext.setScrollFns`, `disableDefaultScroll` | Use `setScrollSource` to supply an external position source and commit callback. |
158
+ | `scale()` and `ScalePluginConfig` | Remove it; the default entry supports the full logical range. `vlist/config` no longer installs a scale stub. |
152
159
 
153
- Only explicit `scale()` calls warn, once per process. The `vlist/config` compatibility stub is silent in 2.x and will no longer be installed in 3.0. Its scrollbar omission/options convenience remains supported and maps to `scrollbar()`; only the two string values above are deprecated. See the [RFC-014 migration contract](https://vlist.io/docs/rfcs/RFC-014-Scroll-Input-Model).
160
+ Removed scroll options throw a migration error before creating DOM. `vlist/config` retains its scrollbar omission/options convenience and its top-level `scrollbar: "none"` option; native visibility strings require an injected native factory. `baseOffset` remains private engine state for input providers; plugins continue to use `ctx.scroll.getRenderOrigin()`. No other public plugin hooks or adapter methods are removed.
154
161
 
155
162
  ## Plugins
156
163
 
157
- | Plugin | Size | Description |
158
- |--------|------|-------------|
159
- | **Base** | 9.9 KB | Virtualization, ARIA, keyboard nav, gap, padding, bounded scroll (1M+ items) |
160
- | `vlist/synthetic` entry | +2.6 KB | Opt-in synthetic scroll input (12.5 KB total before plugins) |
161
- | `data()` | +4.8 KB | Lazy loading with velocity-aware fetching |
162
- | `selection()` | +2.8 KB | Single/multiple selection with 2D keyboard nav |
163
- | `search()` | +3.2 KB | Search bar: filter/navigate modes, match highlighting |
164
- | `groups()` | +5.3 KB | Sticky/inline headers with grid + masonry + table + data integration |
165
- | `autosize()` | +1.0 KB | Auto-measure items via ResizeObserver |
166
- | `scrollbar()` | +2.0 KB | Custom scrollbar UI |
167
- | `grid()` | +2.5 KB | 2D grid layout |
168
- | `masonry()` | +4.1 KB | Pinterest-style masonry with lane-aware keyboard nav |
169
- | `carousel()` | +3.5 KB | Paged horizontal carousel with snap and keyboard nav |
170
- | `table()` | +5.8 KB | Data table with columns, resize, sort |
171
- | `tree()` | +5.0 KB | Collapsible tree with async loading and indent guides |
172
- | `page()` | +0.8 KB | Window-level scrolling |
173
- | `sortable()` | +3.0 KB | Drag-and-drop reordering with auto-scroll |
174
- | `snapshots()` | +1.1 KB | Scroll position save/restore |
175
- | `transition()` | +2.0 KB | FLIP-based enter/exit animations for insert & remove |
164
+ | Entry / export | Minified | Gzipped |
165
+ |---|---:|---:|
166
+ | **Base (`vlist`)** | 31.1 KB | 11.4 KB |
167
+ | `vlist/synthetic` (alias) | 31.1 KB | 11.4 KB |
168
+ | `vlist/native` | 28.1 KB | 10.2 KB |
169
+ | `a11y()` | 34.4 KB | 12.6 KB |
170
+ | `selection()` | 40.5 KB | 14.2 KB |
171
+ | `data()` | 44.8 KB | 16.2 KB |
172
+ | `scrollbar()` | 39.3 KB | 14.2 KB |
173
+ | `sortable()` | 40.6 KB | 14.3 KB |
174
+ | `groups()` | 47.1 KB | 16.7 KB |
175
+ | `page()` | 33.6 KB | 12.3 KB |
176
+ | `snapshots()` | 34.4 KB | 12.5 KB |
177
+ | `transition()` | 37.8 KB | 13.3 KB |
178
+ | `autosize()` | 34.2 KB | 12.4 KB |
179
+ | `grid()` | 38.2 KB | 13.8 KB |
180
+ | `table()` | 49.5 KB | 17.2 KB |
181
+ | `masonry()` | 42.5 KB | 15.5 KB |
182
+ | `tree()` | 46.4 KB | 16.4 KB |
183
+ | `search()` | 40.2 KB | 14.5 KB |
184
+ | `carousel()` | 41.0 KB | 14.9 KB |
185
+
186
+ Sizes are tree-shaken totals from `bun run size`, not additive plugin costs. Plugin rows measure the base factory plus that export for comparison across revisions; `carousel()` and `sortable()` must be used with the native factory at runtime. The base is **11,670 bytes gzipped** in this 3.0 work-in-progress build. The 9.9 KB target is not yet met; size optimization is deferred.
176
187
 
177
188
  ## Examples
178
189
 
@@ -297,6 +308,64 @@ const list = createVList({
297
308
  ])
298
309
  ```
299
310
 
311
+ ## Custom scrollbar (3.0 preview)
312
+
313
+ On `next`, `scrollbar()` remains a plugin. macOS and Android default to thin,
314
+ rounded, auto-hiding overlays; Windows defaults to a wider, square, always-visible
315
+ bar. Set `gutter: true` to reserve space. The same behavior works horizontally.
316
+
317
+ ```typescript
318
+ scrollbar({
319
+ platform: 'windows', // optional: macos | windows | android
320
+ width: 'thin', // optional: pixels (number) | auto | thin | none
321
+ radius: 4, // optional: thumb radius in pixels
322
+ thumbColor: '#666', // optional explicit colors
323
+ trackColor: '#eee',
324
+ gutter: true,
325
+ })
326
+ ```
327
+
328
+ Without explicit overrides, the plugin reads the container's standard
329
+ `scrollbar-width` and `scrollbar-color`. `auto` uses the platform width; `thin`
330
+ uses 6 px; `none` disables the track, hover target and gutter. Color order is
331
+ thumb then track. Numeric `width` and `radius` override the corresponding
332
+ `--vlist-custom-scrollbar-width` and `--vlist-custom-scrollbar-radius` variables
333
+ on the container; those variables override platform defaults. Without a custom
334
+ width variable, the width keywords retain their standard meaning (`none` always
335
+ disables the bar). Explicit colors override author standard colors. `autoHide`, `autoHideDelay` and `minThumbSize` remain available.
336
+ After changing author CSS, call `list.refreshScrollbar()` (or `refresh()` on a
337
+ standalone `Scrollbar` instance). Refresh rereads CSS; platform selection remains
338
+ fixed for that instance. Setting `enabled: false` disables the plugin's bar.
339
+
340
+ The focusable track exposes its controlled viewport, orientation, logical range,
341
+ and “Row N of M” value. Arrows move one row along the active axis, PageUp/PageDown
342
+ move a viewport, and Home/End reach the bounds. Focus keeps the bar visible.
343
+ Forced-color themes use system colors for the track, thumb and focus indicator.
344
+ The thumb has a minimum size even for millions of rows; drag positions continue
345
+ to use the full logical range. The real screen-reader acceptance pass is still
346
+ pending; this is not a completed accessibility sign-off.
347
+
348
+ ### Migrating WebKit scrollbar selectors
349
+
350
+ **The `::-webkit-scrollbar*` pseudo-elements are not mirrored.** They style
351
+ browser-owned scrollbars, not this plugin's DOM. Migrate each rule as follows
352
+ (the classes shown use the default `vlist` prefix):
353
+
354
+ | Existing selector | Plugin replacement |
355
+ | --- | --- |
356
+ | `::-webkit-scrollbar` | `.vlist-scrollbar`; `--vlist-custom-scrollbar-width` for thickness |
357
+ | `::-webkit-scrollbar-track` | `.vlist-scrollbar`; `--vlist-custom-scrollbar-track-color` |
358
+ | `::-webkit-scrollbar-thumb` | `.vlist-scrollbar__thumb`; `--vlist-custom-scrollbar-thumb-color`, `--vlist-custom-scrollbar-radius`, `--vlist-custom-scrollbar-min-thumb-size` |
359
+ | `::-webkit-scrollbar-thumb:hover` | `.vlist-scrollbar__thumb:hover`; `--vlist-custom-scrollbar-thumb-hover-color` |
360
+ | `::-webkit-scrollbar-corner` | No separate corner element. Reserved gutter space uses the `.vlist` background (`--vlist-bg`). A dedicated corner rule has no direct equivalent. |
361
+ | `::-webkit-scrollbar-button` | No arrow-button elements or direct styling equivalent. Use the scrollbar's row keys or track paging; custom buttons must be separate controls. |
362
+
363
+ For width and base thumb/track colors, prefer standard `scrollbar-width` and
364
+ `scrollbar-color` on the container, or plugin config. The plugin maps these onto
365
+ its custom properties at setup/refresh. Use the plugin classes and remaining
366
+ variables for radius, minimum thumb size and hover styling. High-contrast system
367
+ colors take priority while forced colors are active.
368
+
300
369
  ## Accessibility
301
370
 
302
371
  Every vlist is accessible by default following the [WAI-ARIA listbox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/listbox/):
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # vlist
2
2
 
3
- The virtual list library for every framework. Ultra efficient, batteries-included, and accessible with composable plugins — in 9.9 KB.
3
+ The virtual list library for every framework. Ultra efficient, batteries-included, and accessible with composable plugins.
4
4
 
5
- **v2.8.0** — [Changelog](https://github.com/floor/vlist/blob/main/CHANGELOG.md) · autosize `remeasure(index?)`; framework adapters can select the `vlist/synthetic` entry via `VListConfig.factory`; `vlist/config` no longer warns about the scale stub; deprecation notices for 3.0 (`scroll.mode`, `scroll.runway`, native scrollbar values, old plugin hooks).
5
+ **v3.0.0-next.1** (prerelease on the npm `next` tag) Synthetic input by default, native input through `vlist/native`, and removal of the deprecated scroll configuration and plugin hooks. See the [changelog](https://github.com/floor/vlist/blob/next/CHANGELOG.md).
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/vlist.svg)](https://www.npmjs.com/package/vlist)
8
8
  [![bundle size](https://img.shields.io/bundlephobia/minzip/vlist)](https://bundlephobia.com/package/vlist)
@@ -11,7 +11,7 @@ The virtual list library for every framework. Ultra efficient, batteries-include
11
11
 
12
12
  - **Accessible** — WAI-ARIA, 2D keyboard navigation, focus recovery, screen-reader DOM ordering
13
13
  - **Zero dependencies** — framework-agnostic core, tiny adapters for Vue, Svelte, Solid, React
14
- - **9.9 KB gzipped** — composable plugins with perfect tree-shaking
14
+ - **11.4 KB gzipped (3.0 prerelease)** — composable plugins with perfect tree-shaking
15
15
  - **Constant memory** — ~0.1 MB overhead at any scale, from 10K to 1M+ items
16
16
  - **Axis-neutral** — vertical and horizontal scrolling through a single code path, all plugins work in both orientations
17
17
 
@@ -19,6 +19,7 @@ The virtual list library for every framework. Ultra efficient, batteries-include
19
19
 
20
20
  ```bash
21
21
  npm install vlist
22
+ npm install vlist@next # 3.0 prerelease; latest stays on 2.8
22
23
  ```
23
24
 
24
25
  ## Quick Start
@@ -52,73 +53,84 @@ const list = createVList({ container: '#app', items, item: { height: 200, templa
52
53
  ])
53
54
  ```
54
55
 
55
- ## Synthetic scroll input
56
-
57
- The opt-in `vlist/synthetic` entry adds `scroll.mode: 'synthetic'` alongside native and bounded modes. Native remains the default. Import the factory from this entry and plugins from `vlist`:
58
- Bounded mode note: on touch devices a long native fling can outrun the 2x runway and stall at its edge (measured at 145-226% of a 16x runway on an iPhone SE and a Pixel 8a). Prefer synthetic mode for touch-heavy lists; bounded remains the right choice for wheel and keyboard driven lists.
56
+ ## Scroll input
59
57
 
58
+ Synthetic scrolling is the default in 3.0. Import `createVList` and plugins from `vlist`; there is no `scroll.mode` option. The deprecated `vlist/synthetic` entry remains an alias of the same factory.
60
59
 
61
60
  ```typescript
62
- import { createVList } from 'vlist/synthetic'
63
- import { scrollbar } from 'vlist'
61
+ import { createVList, scrollbar } from 'vlist'
64
62
  import 'vlist/styles'
65
63
 
66
64
  const list = createVList({
67
65
  container: '#my-list',
68
66
  items: Array.from({ length: 1000 }, (_, id) => ({ id, name: `Row ${id}` })),
69
67
  item: { height: 48, template: item => `<div>${item.name}</div>` },
70
- scroll: { mode: 'synthetic' },
71
68
  }, [scrollbar()])
72
69
  ```
73
70
 
74
- Supported plugins are **table, groups, snapshots, scrollbar, autosize, transition, selection and a11y**. Existing plugin conflicts still apply; this list does not imply that all eight can be combined. `page()`, `carousel()` and `sortable()` throw when configured with synthetic mode. Carousel uses wrap scrolling, which this release does not support with synthetic input.
71
+ For native scrolling, import the factory from `vlist/native` and plugins from `vlist`. Native scrolling is required for `carousel()`, `sortable()`, and horizontal RTL lists; configuring these with the default entry throws. The native entry preserves carousel wrapping through a private runway implementation. Bounded scrolling is no longer a public mode.
72
+
73
+ ```typescript
74
+ import { createVList } from 'vlist/native'
75
+ import { carousel } from 'vlist'
76
+
77
+ const list = createVList({
78
+ container: '#slides',
79
+ orientation: 'horizontal',
80
+ items: slides,
81
+ item: { width: 320, template: renderSlide },
82
+ }, [carousel()])
83
+ ```
84
+
85
+ `page()` uses native document scrolling through an external source with either entry. Its content must fit the 16,777,216 px document element limit; creation throws above that limit when the size is known, and later growth warns once. A deferred custom renderer whose size is first committed during rendering also warns once. Use default viewport scrolling for larger lists. Native viewport lists warn once when content exceeds their browser-size safety limit.
75
86
 
76
87
  Known limitations:
77
88
 
78
- - RTL horizontal lists throw in synthetic mode in this release; use native mode. Vertical lists on RTL pages are supported. RTL support for the synthetic driver is planned as a non-breaking addition.
79
- - Same-axis touch stops at either boundary with no parent handoff, including gestures that start inside an edge-pinned list. Use native mode when touch gestures must scroll the parent page at a boundary.
80
- - The native main-axis scrollbar is absent. Provide a custom scrollbar, such as `scrollbar()` above. Its accessibility release gate remains open; synthetic mode is not a completed scrollbar-accessibility sign-off.
89
+ - Horizontal RTL lists require `vlist/native`. Vertical lists and tables support `dir="rtl"` on the container, including cross-axis wheel movement, aligned table headers and keyboard column navigation.
90
+ - Same-axis touch stops at either boundary with no parent handoff, including gestures starting inside an edge-pinned list. Use `vlist/native` when boundary gestures must scroll the parent page.
91
+ - The default entry has no native main-axis scrollbar. Add `scrollbar()` for an accessible custom scrollbar; native visibility options belong to `vlist/native`.
81
92
  - Inertia initializes its frame clock on the first frame after release, adding up to one frame of release latency.
82
93
  - Wheel input at an edge is left to the page when it cannot move the list. Native cross-axis scrolling remains available.
83
94
 
84
- Measurement corrections from autosize preserve ongoing motion. Synthetic input adds **2.6 KB gzipped** over the base entry (**12.5 KB** total before plugins); ordinary `vlist` imports exclude this driver. See [RFC-014](https://github.com/floor/vlist/discussions/127).
85
-
86
- ## Deprecated in 2.8, removed in 3.0
95
+ Measurement corrections from autosize preserve ongoing motion. Existing plugin conflicts still apply. See the [scroll input contract](https://vlist.io/docs/rfcs/RFC-014-Scroll-Input-Model).
87
96
 
88
- These notices prepare the 3.0 migration; 2.x behavior and defaults stay unchanged. `vlist/native` and `setScrollSource` are 3.0 replacements, not 2.8 APIs. Bounded mode remains supported in 2.x, including carousel and sortable; it emits no deprecation warning.
97
+ ## Migrating to 3.0
89
98
 
90
- | Option or API | Replacement | Since |
91
- |---|---|---|
92
- | `scroll.mode` | In 3.0, synthetic input in core; import `vlist/native` for native scrolling. Bounded is removed. | 2.8 |
93
- | `scroll.runway` | Remove it when moving to 3.0 synthetic core. | 2.8 |
94
- | `scroll.scrollbar: "native"` | In 3.0 use `vlist/native` for a browser scrollbar, or `scrollbar()` in synthetic core. | 2.8 |
95
- | `scroll.scrollbar: "none"` | In 3.0 synthetic core has no native main-axis scrollbar to hide; native hiding belongs to `vlist/native`. | 2.8 |
96
- | `PluginContext.setScrollFns`, `disableDefaultScroll` | Use `setScrollSource` when upgrading to 3.0. | 2.8 |
97
- | `scale()` | Use `scroll: { mode: "synthetic" }` from `vlist/synthetic`; bounded remains available in 2.x. | 2.4; guidance updated in 2.8 |
99
+ | Removed public API | Replacement |
100
+ |---|---|
101
+ | `scroll.mode` (all values, both entries) | Omit it. `vlist` provides synthetic input; import `vlist/native` for native scrolling. |
102
+ | `scroll.runway` (both entries) | Remove it. Default synthetic scrolling supports huge lists without a native runway. |
103
+ | Core `scroll.scrollbar: "native"` or `"none"` | Use `scrollbar()` with `vlist`, or select `vlist/native` to retain either string. Native types are exported as `NativeScrollConfig` and `NativeCreateVListConfig`. |
104
+ | `PluginContext.setScrollFns`, `disableDefaultScroll` | Use `setScrollSource` to supply an external position source and commit callback. |
105
+ | `scale()` and `ScalePluginConfig` | Remove it; the default entry supports the full logical range. `vlist/config` no longer installs a scale stub. |
98
106
 
99
- Only explicit `scale()` calls warn, once per process. The `vlist/config` compatibility stub is silent in 2.x and will no longer be installed in 3.0. Its scrollbar omission/options convenience remains supported and maps to `scrollbar()`; only the two string values above are deprecated. See the [RFC-014 migration contract](https://vlist.io/docs/rfcs/RFC-014-Scroll-Input-Model).
107
+ Removed scroll options throw a migration error before creating DOM. `vlist/config` retains its scrollbar omission/options convenience and its top-level `scrollbar: "none"` option; native visibility strings require an injected native factory. `baseOffset` remains private engine state for input providers; plugins continue to use `ctx.scroll.getRenderOrigin()`. No other public plugin hooks or adapter methods are removed.
100
108
 
101
109
  ## Plugins
102
110
 
103
- | Plugin | Size | Description |
104
- |--------|------|-------------|
105
- | **Base** | 9.9 KB | Virtualization, ARIA, keyboard nav, gap, padding, bounded scroll (1M+ items) |
106
- | `vlist/synthetic` entry | +2.6 KB | Opt-in synthetic scroll input (12.5 KB total before plugins) |
107
- | `data()` | +4.8 KB | Lazy loading with velocity-aware fetching |
108
- | `selection()` | +2.8 KB | Single/multiple selection with 2D keyboard nav |
109
- | `search()` | +3.2 KB | Search bar: filter/navigate modes, match highlighting |
110
- | `groups()` | +5.3 KB | Sticky/inline headers with grid + masonry + table + data integration |
111
- | `autosize()` | +1.0 KB | Auto-measure items via ResizeObserver |
112
- | `scrollbar()` | +2.0 KB | Custom scrollbar UI |
113
- | `grid()` | +2.5 KB | 2D grid layout |
114
- | `masonry()` | +4.1 KB | Pinterest-style masonry with lane-aware keyboard nav |
115
- | `carousel()` | +3.5 KB | Paged horizontal carousel with snap and keyboard nav |
116
- | `table()` | +5.8 KB | Data table with columns, resize, sort |
117
- | `tree()` | +5.0 KB | Collapsible tree with async loading and indent guides |
118
- | `page()` | +0.8 KB | Window-level scrolling |
119
- | `sortable()` | +3.0 KB | Drag-and-drop reordering with auto-scroll |
120
- | `snapshots()` | +1.1 KB | Scroll position save/restore |
121
- | `transition()` | +2.0 KB | FLIP-based enter/exit animations for insert & remove |
111
+ | Entry / export | Minified | Gzipped |
112
+ |---|---:|---:|
113
+ | **Base (`vlist`)** | 31.1 KB | 11.4 KB |
114
+ | `vlist/synthetic` (alias) | 31.1 KB | 11.4 KB |
115
+ | `vlist/native` | 28.1 KB | 10.2 KB |
116
+ | `a11y()` | 34.4 KB | 12.6 KB |
117
+ | `selection()` | 40.5 KB | 14.2 KB |
118
+ | `data()` | 44.8 KB | 16.2 KB |
119
+ | `scrollbar()` | 39.3 KB | 14.2 KB |
120
+ | `sortable()` | 40.6 KB | 14.3 KB |
121
+ | `groups()` | 47.1 KB | 16.7 KB |
122
+ | `page()` | 33.6 KB | 12.3 KB |
123
+ | `snapshots()` | 34.4 KB | 12.5 KB |
124
+ | `transition()` | 37.8 KB | 13.3 KB |
125
+ | `autosize()` | 34.2 KB | 12.4 KB |
126
+ | `grid()` | 38.2 KB | 13.8 KB |
127
+ | `table()` | 49.5 KB | 17.2 KB |
128
+ | `masonry()` | 42.5 KB | 15.5 KB |
129
+ | `tree()` | 46.4 KB | 16.4 KB |
130
+ | `search()` | 40.2 KB | 14.5 KB |
131
+ | `carousel()` | 41.0 KB | 14.9 KB |
132
+
133
+ Sizes are tree-shaken totals from `bun run size`, not additive plugin costs. Plugin rows measure the base factory plus that export for comparison across revisions; `carousel()` and `sortable()` must be used with the native factory at runtime. The base is **11,670 bytes gzipped** in this 3.0 work-in-progress build. The 9.9 KB target is not yet met; size optimization is deferred.
122
134
 
123
135
  ## Framework Adapters
124
136
 
@@ -129,21 +141,20 @@ Only explicit `scale()` calls warn, once per process. The `vlist/config` compati
129
141
  | SolidJS | [`vlist-solidjs`](https://github.com/floor/vlist-solidjs) | 0.5 KB |
130
142
  | React | [`vlist-react`](https://github.com/floor/vlist-react) | 0.6 KB |
131
143
 
132
- With vlist 2.8 and an adapter that forwards the `factory` option, opt into synthetic input explicitly:
144
+ Adapters use synthetic input by default. With an adapter that forwards `factory`, select native input explicitly:
133
145
 
134
146
  ```ts
135
147
  import { useVList } from "vlist-react";
136
- import { createVList } from "vlist/synthetic";
148
+ import { createVList } from "vlist/native";
137
149
 
138
150
  useVList({
139
151
  factory: createVList,
140
- scroll: { mode: "synthetic" },
141
152
  items,
142
153
  item: { height: 48, template: item => String(item.id) },
143
154
  });
144
155
  ```
145
156
 
146
- The same factory option is available to the other adapters. `vlist/config` keeps the synthetic driver out of its default bundle; importing the factory opts in. The factory is structural configuration: changing it requires recreating the list.
157
+ The same factory option is available to the other adapters. `vlist/config` defaults to synthetic input. The factory is structural configuration: changing it requires recreating the list.
147
158
 
148
159
  ## Docs & Examples
149
160
 
package/dist/config.d.ts CHANGED
@@ -15,8 +15,9 @@
15
15
  * lean and tree-shakeable; only consumers that opt into the batteries-included
16
16
  * config (the adapters) pull in this module and, with it, every plugin it wires.
17
17
  */
18
- import type { VListItem, GroupsConfig, VListAdapter, ScrollConfig } from "./types";
18
+ import type { VListItem, GroupsConfig, VListAdapter } from "./types";
19
19
  import { createVList } from "./core/create";
20
+ import type { NativeScrollConfig } from "./native";
20
21
  import type { CreateVListConfig, VList, VListPlugin } from "./core/types";
21
22
  import type { DataPluginConfig } from "./plugins/data";
22
23
  import type { GridPluginConfig } from "./plugins/grid";
@@ -32,10 +33,8 @@ export type VListFactory<T extends VListItem = VListItem> = typeof createVList<T
32
33
  * translated into plugins by {@link resolvePlugins}.
33
34
  */
34
35
  export interface VListConfig<T extends VListItem = VListItem> extends Omit<CreateVListConfig<T>, "container" | "scroll"> {
35
- /** Synthetic mode requires a factory imported from vlist/synthetic. */
36
- scroll?: Omit<ScrollConfig, "mode"> & {
37
- mode?: ScrollConfig["mode"] | "synthetic";
38
- };
36
+ /** Input model; native scrolling requires a factory imported from vlist/native. */
37
+ scroll?: NativeScrollConfig;
39
38
  /** List factory; defaults to core createVList. Fixed for this instance. */
40
39
  factory?: VListFactory<T>;
41
40
  /** Layout mode. Wires the grid or masonry plugin from `grid`/`masonry`. */
@@ -65,7 +64,7 @@ export interface VListConfig<T extends VListItem = VListItem> extends Omit<Creat
65
64
  /**
66
65
  * Translate a {@link VListConfig} into the ordered plugin array that the core
67
66
  * `createVList` expects. Mirrors the adapters' historical behavior exactly:
68
- * `scale` and `snapshots` are always included, and `selection` is always
67
+ * `snapshots` is always included, and `selection` is always
69
68
  * present (in `"none"` mode when unset) so its API is available. Any user
70
69
  * `plugins` are appended last as an escape hatch.
71
70
  */