Changelog
What's shipping across @vyui/core and @vyui/kit — one rolling timeline, newest first.
- @vyui/kitv0.3.7
@vyui/kit v0.3.7
Patch Changes
- Drive every gesture surface from mouse events on Lynx web, and fix Sortable's (#161)
drop.
Lynx web dispatches raw mouse events and never synthesizes touch from them, so touch-only worklets left every drag surface inert in a desktop browser. Each gesture core now takes plain coordinates, with thin touch and mouse wrappers over it:Draggable,Slider,Swiper,SwipeAction,Sheet,Toastswipe,Sortable,FeedListpull-to-refresh, andScrollView's custom bounce. Release is explicit-buttons-only, a 500ms guard swallows the compatibility mousedown a touch browser replays after a tap, and nomouseleaveis bound (it doesn't bubble, so per-element delivery is unreliable on the Lynx dispatch path). The pull and the bounce only engage at an edge, so binding mouse on those two doesn't disturb ordinary wheel scrolling.ScrollView's bounce also moved offlynx.querySelector('#id')ontomain-thread-refhandles. That selector exists only on the native main thread — web-core's main-threadlynxobject has noquerySelector— so on web every call threw and took the whole bounce worklet down with it.
Sortable fixes on top of that:- The lifted row gets
zIndexfrom its background-side dragging state instead of painting in DOM order, so dragging down no longer slides it under the rows it passes — where they swallowed the pointer and the gesture's own move/up worklets stopped arriving. - Release settles the row into its target slot and re-runs the sibling shift for the final velocity-adjusted target; the transforms are cleared from the background once the reorder has rendered. Clearing them on the main thread at release repainted the pre-drag order for the frames the commit took to round-trip, which read as the list snapping back.
longPressMsdefaults to 150 (was 250).
- The lifted row gets
- Repo-wide over-engineering cleanup. Migrate all 38
@vyui/kitcomponents off the internaldefineThemeBuilderhelper onto the existinguseStyledComponentcomposable and delete the helper. Remove dead code from@vyui/core(theuseWarningno-op, and the unusedareEqual/findValuesBetween/mtsLog/get/nooputils) and simplifyhandleAndDispatchCustomEvent. In@vyui/cli, drop the fuzzy "did you mean" suggestion from the unknown-component error (which already lists every available component) and simplify dependency resolution. No public component API changes. (#158) - Make Slider's main-thread drag the only drag implementation, and fix it so it (#159)
works. It previously filled its thumb registry and every
*MTmirror with background-thread writes toMainThreadRef.current, which vue-lynx silently no-ops — the main thread saw an empty handle list and bailed out of_paintActiveThumbon every frame, while the commit read a stale array and landed asnext[0] ?? 0.SliderRoot's min/max/step/disabled/values mirrors hop throughrunOnMainThreadsetter worklets, the shapeSheetuses.SliderImplMTSresolves the thumb and range elements itself from the track viaquerySelectorAll, removing the BG→MT registration and its mount-time race.SliderThumbImplno longer touches aMainThreadRefat all.update:modelValuenow fires per frame during the drag, so a value rendered next to the slider tracks the gesture instead of jumping on release.valueCommitstays once per gesture, compared against the value the gesture started from.- The thumb and the filled range are painted from the worklets by writing the same anchor offsets the background style computes, so neither thread's write can double-count the other's.
- Values are re-sorted and the active thumb re-tracked every frame, and
minStepsBetweenThumbsis enforced per frame rather than at commit time — a thumb stops at the limit instead of springing back on release. - Touch offsets are read from
touches[0].x/.y, which are already relative to the bound element. Rebuilding them frompageY - layoutchange.topmixed viewport and page coordinates and drifted by the scroll offset. - Fixes the thumb sitting half its own width off-centre on right-anchored (inverted / RTL) sliders: the centring transform flips with the anchoring edge on the vertical axis but did not on the horizontal one.
- Fixes the
mdthumb rendering 0×0 —size-4.5is not a Tailwind v3 utility, so it compiled to no CSS at all.
Removed: the background drag implementation (SliderImpl), themainThreadDragprop, and keyboard stepping. The keyboard handlers never fired on Lynx native, which has no key events to bind. - Fix the default (
md) Slider thumb rendering 0×0. Its size wassize-4.5, and (#159) Tailwind v3's spacing scale has no 4.5 step, so the class compiled to no CSS — safelisted, no build warning. It is nowsize-[18px], and the preset test suite fails on any theme class using a fractional spacing step v3 cannot generate. - Updated dependencies [
949edd3,949edd3,f9f0946,949edd3,949edd3,01415de,949edd3,949edd3]:- @vyui/core@0.2.7
- Drive every gesture surface from mouse events on Lynx web, and fix Sortable's (#161)
drop.
- @vyui/corev0.2.7
@vyui/core v0.2.7
Patch Changes
- Drive every gesture surface from mouse events on Lynx web, and fix Sortable's (#161)
drop.
Lynx web dispatches raw mouse events and never synthesizes touch from them, so touch-only worklets left every drag surface inert in a desktop browser. Each gesture core now takes plain coordinates, with thin touch and mouse wrappers over it:Draggable,Slider,Swiper,SwipeAction,Sheet,Toastswipe,Sortable,FeedListpull-to-refresh, andScrollView's custom bounce. Release is explicit-buttons-only, a 500ms guard swallows the compatibility mousedown a touch browser replays after a tap, and nomouseleaveis bound (it doesn't bubble, so per-element delivery is unreliable on the Lynx dispatch path). The pull and the bounce only engage at an edge, so binding mouse on those two doesn't disturb ordinary wheel scrolling.ScrollView's bounce also moved offlynx.querySelector('#id')ontomain-thread-refhandles. That selector exists only on the native main thread — web-core's main-threadlynxobject has noquerySelector— so on web every call threw and took the whole bounce worklet down with it.
Sortable fixes on top of that:- The lifted row gets
zIndexfrom its background-side dragging state instead of painting in DOM order, so dragging down no longer slides it under the rows it passes — where they swallowed the pointer and the gesture's own move/up worklets stopped arriving. - Release settles the row into its target slot and re-runs the sibling shift for the final velocity-adjusted target; the transforms are cleared from the background once the reorder has rendered. Clearing them on the main thread at release repainted the pre-drag order for the frames the commit took to round-trip, which read as the list snapping back.
longPressMsdefaults to 150 (was 250).
- The lifted row gets
- Fix
List.scrollIntoIdthrowing on Lynx web, and restore its bottom/middle (#161) alignment everywhere.
The worklet reached forlynx.querySelector('#id')three times. That global selector API exists only on the native main thread — web-core's MTlynxobject has noquerySelector— so the first call raised aTypeErrorand took the whole worklet with it, and the list scrolled nowhere.
Two of those lookups were for the<list>itself, which the component owns, so they now go through amain-thread-refinstead. That works on both platforms: web-elements'<x-list>implementsscrollToPositionas a real DOM method, which is what__InvokeUIMethoddispatches to. The third targets an arbitrary consumer-owned child, where the global selector is the only route, so it is feature-checked. When it is absent the scroll keeps its step-1 landing and still honoursoffset— identical to whatalignTo: 'none'already produces — rather than aborting. Web'sboundingClientRectignoresrelativeToanyway, so there is nothing meaningful to measure against there.
Separately, the list'slayoutchangeworklet was bound as:main-thread:bindlayoutchange. vue-lynx only recognises themain-thread-prefix, so the colon form parsed as an ordinary prop and the handler never attached —listHeightMT/listWidthMTsat at 0 on every platform, which silently brokealignTo: 'bottom'andalignTo: 'middle'. Now bound with the hyphen form, with a test that fails on anymain-thread:binding in core. - Repo-wide over-engineering cleanup. Migrate all 38
@vyui/kitcomponents off the internaldefineThemeBuilderhelper onto the existinguseStyledComponentcomposable and delete the helper. Remove dead code from@vyui/core(theuseWarningno-op, and the unusedareEqual/findValuesBetween/mtsLog/get/nooputils) and simplifyhandleAndDispatchCustomEvent. In@vyui/cli, drop the fuzzy "did you mean" suggestion from the unknown-component error (which already lists every available component) and simplify dependency resolution. No public component API changes. (#158) - Fix
<Presence>wedging permanently on web when an animation end/cancel event (#161) is lost, and restore parity with upstreamlynx-family/lynx-uion the reopen-during-close path.handleKFStart/handleTransitionStartno longer bump the entering/leaving loop ids. Bumping killed the frame watchdog the instant any animation started, leaving the machine trusting an end event that web does not reliably deliver — a child unmounted mid-transition never fires one, and DOM bubbling feeds child animation events into these handlers to begin with. A stuckLeavingkept the invisible backdrop mounted, swallowing every tap under it.- Both watchdogs now poll through in-flight animations and force-resolve past a
MAX_STUCK_MS(3s) wall-clock ceiling. Wall-clock rather than frames because rAF cadence varies across environments. showflipping back totrueduringLeavingnow remounts viarestartShow()instead of tearing down. Previously a reopen racingLeaving→Leftstrandedshow=truewith nothing mounted and the trigger went permanently dead.onOpen/onCloseare deduped throughhasNotifiedOpen, so a reopen-during-close that routes back throughEntereddoesn't double-fire.- A close that races the enter animation no longer cuts straight to
Leaving. The exit keyframe starts from the element's underlying (fully-open) value, so swapping mid-enter snapped the element open and played the exit from there — the action sheet flashing up before sliding back out. The dismiss is deferred until the enter resolves, which the existinghandleAnimationEndsafeguard and entering watchdog already route toLeaving.
- Fix the sheet replaying its exit animation after a drag-dismiss. (#161)
Releasing a drag past the dismiss threshold drove the close twice: the MT release transition slid the panel off from where the finger left it, and Presence's.ui-leavingkeyframe then ran the same close again — starting from the fully-open underlying value, so the panel snapped back up and played the exit a second time. The scrim did the same throughvyui-fade-out, which keeps an explicitfrom { opacity: 1 }.
The inlineanimation: 'none'the release worklet paints was meant to suppress that keyframe, but a class-driven animation outranks it on the Lynx style path.SheetRootnow carries adragClosingflag, set by the release worklet before it emits the close, that dropsui-leavingfrom the panel and backdrop for that path — the MT transition owns the close and@transitionendstill advances Presence toLeft. Non-drag closes keep the keyframe unchanged. - Make Slider's main-thread drag the only drag implementation, and fix it so it (#159)
works. It previously filled its thumb registry and every
*MTmirror with background-thread writes toMainThreadRef.current, which vue-lynx silently no-ops — the main thread saw an empty handle list and bailed out of_paintActiveThumbon every frame, while the commit read a stale array and landed asnext[0] ?? 0.SliderRoot's min/max/step/disabled/values mirrors hop throughrunOnMainThreadsetter worklets, the shapeSheetuses.SliderImplMTSresolves the thumb and range elements itself from the track viaquerySelectorAll, removing the BG→MT registration and its mount-time race.SliderThumbImplno longer touches aMainThreadRefat all.update:modelValuenow fires per frame during the drag, so a value rendered next to the slider tracks the gesture instead of jumping on release.valueCommitstays once per gesture, compared against the value the gesture started from.- The thumb and the filled range are painted from the worklets by writing the same anchor offsets the background style computes, so neither thread's write can double-count the other's.
- Values are re-sorted and the active thumb re-tracked every frame, and
minStepsBetweenThumbsis enforced per frame rather than at commit time — a thumb stops at the limit instead of springing back on release. - Touch offsets are read from
touches[0].x/.y, which are already relative to the bound element. Rebuilding them frompageY - layoutchange.topmixed viewport and page coordinates and drifted by the scroll offset. - Fixes the thumb sitting half its own width off-centre on right-anchored (inverted / RTL) sliders: the centring transform flips with the anchoring edge on the vertical axis but did not on the horizontal one.
- Fixes the
mdthumb rendering 0×0 —size-4.5is not a Tailwind v3 utility, so it compiled to no CSS at all.
Removed: the background drag implementation (SliderImpl), themainThreadDragprop, and keyboard stepping. The keyboard handlers never fired on Lynx native, which has no key events to bind. - Fix the slider never moving on Lynx web. (#161)
Two independent bails, both before the gesture emitted anything, which is why the value sat frozen at whatever it mounted with while native felt fine.
The one that actually stranded it:_dragStartaborted when it could not resolve the thumb elements to paint. That lookup goes through the main-threadquerySelectorAllwrapper, which calls a__QuerySelectorAllPAPI that Lynx web does not expose to the main-thread realm —invokeresolves there, this throwsReferenceError, and atypeofcheck on the wrapper method cannot see it coming. The query is now guarded, and no longer gates the drag: those elements only drive the main-thread paint, which is a latency optimisation over the background's own render, so losing them costs smoothness rather than correctness. The background still receives every value and renders the thumb from its own style.
The second: the track's extent was the last piece of geometry sourced from a background@layoutchangepushed across withrunOnMainThread._beginAtalready fetchesinvoke('boundingClientRect')once per gesture for the origin, and that response carrieswidth/heighttoo, so the extent comes from it as well. One measurement, one frame, no thread hop — and re-read per gesture, which also fixes a track that resized while its tab was hidden.draggingMTis wired up while here:SliderImplMTSwas setting a local ref of its own, so the root's gate against echoing a liveupdate:modelValueback into the main thread's values was never armed. - Map Slider's touch and mouse gestures through one pointer coordinate frame. (#161)
The touch path readtouches[0].x/.y, which Lynx reports element-relative on native but does not provide on web at all —createCrossThreadEventbuilds web touches from raw DOMTouchobjects, which carry nox/y, so every offset came through asNaNand the thumb never moved on a touchscreen browser.
Both paths now readclientX/clientY, the only pointer field Lynx reports on native and web alike, and share a single_beginAtthat captures the track's origin frominvoke('boundingClientRect')once per gesture and subtracts it. The origin still never comes fromlayoutchange, which reports page-relative coordinates that drift from the pointer's viewport frame by the scroll offset.
- Drive every gesture surface from mouse events on Lynx web, and fix Sortable's (#161)
drop.
- @vyui/corev0.2.6
@vyui/core v0.2.6
Patch Changes
- Animate Collapsible/Accordion height on open/close by tweening a measured px height (Tray recipe); kept-mounted content morphs both directions. (#156)
- Scale the Sheet settle/dismiss duration by release velocity so a hard flick settles quicker while a slow release keeps the current feel. (#156)
- Cache the TabsIndicator list rect and re-measure it only on layout/orientation/dir changes, so a tab switch costs one
boundingClientRectround-trip instead of two. (#156)
- @vyui/kitv0.3.5
@vyui/kit v0.3.5
Patch Changes
- Fix every Sheet-backed surface rendering white in dark mode.
@vyui/corewas shipping color it can't ship. (#154).vyui-sheet__contenthardcodedbackground-color: #fff, which beat the consumer'sbg-defaulton source order and pinned drawer, tray, action sheet, select, combobox and popover to white in both color modes.SheetBackdropImplandSwipeActionsetbackgroundColorinline, which no class can outrank at all — so a theme's dim or row color on those elements was dead on arrival. It only read as a dark-mode bug because white-on-white is invisible.
All three now ship no color, matching the "No defaults — passbackgroundColorhere" contract every sibling overlay already documents.
Breaking for bare@vyui/coreconsumers:SheetContent,SheetBackdropandSwipeActionno longer paint a background. Supply one (@vyui/kitalready does on every slot). TheSwipeActionrow in particular must stay opaque or the actions behind it show through.
Also in@vyui/kit:actionSheet'scontentslot gainsbg-default. It was the one Sheet theme silently relying on core's white.
The reason it stayed hidden:SheetContentandSheetBackdropwere dropping the consumer'sclassentirely.OverlayPortalrenders nothing in place — it registers its slot forOverlayRootto paint elsewhere — so there is no root element forclass/styleto fall through to, and Vue discards them without a warning. Every kit theme class on a sheet panel (bg-default, borders, radius) was being thrown away, and core's hardcoded#fff/position: fixed/z-indexstood in for them. Both now forwarduseAttrs()onto the impl withinheritAttrs: false, matchingDialogContentModal.
Guarded bycomponents/headless-color.test.ts(fails if any core component declares a literal background color —var()is allowed,story/is exempt) and a case inOverlayRoot.test.tspinning the attr-forwarding contract forOverlayPortalconsumers.SheetHandlealso stops hardcoding thebg-accentedclass — a@vyui/kittoken utility, meaningless in a headless package without kit's Tailwind preset, and redundant since all three kit themes already put it on theirhandleslot. The guard now covers that direction too. - Add the
lunarisregistry style, and fix four token-layer bugs the audit turned up. (#154)lunaris— the LUNA design system (from lynx-ui, Apache-2.0) ported to the token layer: pure-grey canvas/paper/content chrome, rose accent, and the five.luna-gradient-*classes. A token-only overlay, so every base.vueandtheme/*.tsis reused unchanged. LUNA's neutral variant shares this chrome byte-for-byte and differs only in accent, so it is documented as a delta in the file header rather than shipped as a second style.- Fix:
shadcnandroundedheld nestedvar(--ui-color-*)refs on the mode tier, which collapse on Lynx native (one level ofvar()only), so consumer-writtenbg-primary/text-errorpainted nothing on device. - Fix:
shadcn'sprimarynow follows--base-colorlikeneutraldoes. It was pinned tozincwhile surfaces tracked the chosen gray, producing combinations shadcn/ui can't express (zinc accent on stone surfaces). - Fix:
resolveColorHexnow resolves shade-less colors (black,white). They are single strings intailwindcss/colors, so indexing them by shade fell through to the slate-500 fallback — a monochrome accent painted black surfaces with slate-blue baked SVG icons on them. - Fix:
initwarns when a chosen--base-colorcan't apply because the style ships its own palette, instead of silently ignoring it. - Guards: registry tests now assert every shipped style declares the full 120-token surface, declares no nested
var()value, and thatroundeddiffers from the base in--ui-radiusalone.
Install withvyui init --style lunaris. - Fix the overlay dim:
modal,drawer,trayandactionSheetall painted no scrim at all. (#154)
Theiroverlayslot used an alpha modifier on a semantic color (bg-neutral-900/50,bg-neutral-900/40). The Tailwind preset maps semantic colors to rawvar()strings with no<alpha-value>placeholder, so Tailwind 3 skips generating those utilities entirely — the class resolved to no CSS and the element painted nothing. All four now usebg-black/50, which parses to rgb so the modifier applies.
Most visible in dark mode, where abg-defaultpanel over an undimmedbg-defaultpage had nothing separating it.
Guarded bytheme/alpha-modifier.test.ts, which fails if any theme file reintroduces an alpha modifier on a semantic color. - Updated dependencies [
3298de9,9d82f23]:- @vyui/core@0.2.5
- Fix every Sheet-backed surface rendering white in dark mode.
- @vyui/corev0.2.5
@vyui/core v0.2.5
Patch Changes
- Fix every Sheet-backed surface rendering white in dark mode.
@vyui/corewas shipping color it can't ship. (#154).vyui-sheet__contenthardcodedbackground-color: #fff, which beat the consumer'sbg-defaulton source order and pinned drawer, tray, action sheet, select, combobox and popover to white in both color modes.SheetBackdropImplandSwipeActionsetbackgroundColorinline, which no class can outrank at all — so a theme's dim or row color on those elements was dead on arrival. It only read as a dark-mode bug because white-on-white is invisible.
All three now ship no color, matching the "No defaults — passbackgroundColorhere" contract every sibling overlay already documents.
Breaking for bare@vyui/coreconsumers:SheetContent,SheetBackdropandSwipeActionno longer paint a background. Supply one (@vyui/kitalready does on every slot). TheSwipeActionrow in particular must stay opaque or the actions behind it show through.
Also in@vyui/kit:actionSheet'scontentslot gainsbg-default. It was the one Sheet theme silently relying on core's white.
The reason it stayed hidden:SheetContentandSheetBackdropwere dropping the consumer'sclassentirely.OverlayPortalrenders nothing in place — it registers its slot forOverlayRootto paint elsewhere — so there is no root element forclass/styleto fall through to, and Vue discards them without a warning. Every kit theme class on a sheet panel (bg-default, borders, radius) was being thrown away, and core's hardcoded#fff/position: fixed/z-indexstood in for them. Both now forwarduseAttrs()onto the impl withinheritAttrs: false, matchingDialogContentModal.
Guarded bycomponents/headless-color.test.ts(fails if any core component declares a literal background color —var()is allowed,story/is exempt) and a case inOverlayRoot.test.tspinning the attr-forwarding contract forOverlayPortalconsumers.SheetHandlealso stops hardcoding thebg-accentedclass — a@vyui/kittoken utility, meaningless in a headless package without kit's Tailwind preset, and redundant since all three kit themes already put it on theirhandleslot. The guard now covers that direction too. - Sheet now paints through the app-root
<OverlayRoot>so it escapes ancestoroverflow: hiddenon Lynx native (#12). (#152)SheetContentandSheetBackdropwrap their impls in a new<OverlayPortal>(exported from@vyui/core), matching how Dialog, DropdownMenu, Combobox and Toast already portal. Presence is unchanged — the portal mounts inside it, so enter/leave animations still run to completion before unmount.
Consumers must have an<OverlayRoot />at the app root;<VyApp>mounts one by default.
- Fix every Sheet-backed surface rendering white in dark mode.
- @vyui/kitv0.3.4
@vyui/kit v0.3.4
Patch Changes
- KeyboardAware lift fixes: measure the margin against the LynxView viewport via
selectRoot()instead of the screen (Explorer chrome no longer shortens the lift), flipoffsetto its documented extra-clearance meaning (it was pushing fields INTO the keyboard), let a wrapping Trigger's registration win over the input's self-registration, and register kit VyInput/VyTextarea's styled field (via an internal as-child Trigger) so the field's bottom chrome clears too. Also adds a library-leveluseSafeArea/provideSafeAreaInsets(elk-style normalization of Sparkling/Explorer global props with OS gating); Sheet panels now pad their docked edges by the container's safe-area insets, andVyAppprovides these insets app-wide with asafeAreaprop to tune them (falseopts the whole app out with zeros; a partial{ top, bottom }overrides specific edges). Input/Textarea (core and kit) gainavoidKeyboard/avoidKeyboardSpacingpassthroughs to Lynx's nativeavoid-keyboardroot-view shift — a zero-JS alternative for simple forms. (#148) - Select/Combobox selection tick now bakes its fill via the Icon
colorprop (accent ramp) instead of a class the rasterized svg can't receive — it rendered black and vanished in dark mode. Combobox's "No results" text also carriestext-mutedon the<text>itself so it flips with the theme. (#148) - Separator label now uses
text-highlightedon the<text>itself, so its color flips with the theme (Lynx doesn't inherit the container's color into the text — the label rendered black in dark mode). (#148) - Bump vue-lynx to 0.5.1 (widens peer range to
^0.4.2 || ^0.5.1), with a local patch making the worklet-loader-mt registration scan comment-aware until upstream #287 ships (#148) - Updated dependencies [
73ed402,73ed402,73ed402,73ed402,73ed402]:- @vyui/core@0.2.4
- KeyboardAware lift fixes: measure the margin against the LynxView viewport via
- @vyui/corev0.2.4
@vyui/core v0.2.4
Patch Changes
- KeyboardAware lift fixes: measure the margin against the LynxView viewport via
selectRoot()instead of the screen (Explorer chrome no longer shortens the lift), flipoffsetto its documented extra-clearance meaning (it was pushing fields INTO the keyboard), let a wrapping Trigger's registration win over the input's self-registration, and register kit VyInput/VyTextarea's styled field (via an internal as-child Trigger) so the field's bottom chrome clears too. Also adds a library-leveluseSafeArea/provideSafeAreaInsets(elk-style normalization of Sparkling/Explorer global props with OS gating); Sheet panels now pad their docked edges by the container's safe-area insets, andVyAppprovides these insets app-wide with asafeAreaprop to tune them (falseopts the whole app out with zeros; a partial{ top, bottom }overrides specific edges). Input/Textarea (core and kit) gainavoidKeyboard/avoidKeyboardSpacingpassthroughs to Lynx's nativeavoid-keyboardroot-view shift — a zero-JS alternative for simple forms. (#148) - Sheet handle now uses the mode-aware
bg-accentedtoken instead of a hardcoded translucent black, so the drag pill stays visible in dark mode. (#148) - Tabs indicator no longer animates growing in from zero width on mount / CSS reload — the transition now arms one tick after the first measurement, so only genuine tab switches slide. (#148)
- Toast viewport now pads its docked edge by the container safe-area insets (via
useSafeArea), so bottom toasts clear the iPhone home indicator and top toasts clear the notch — the same insets the Sheet panels already honor. (#148) - Bump vue-lynx to 0.5.1 (widens peer range to
^0.4.2 || ^0.5.1), with a local patch making the worklet-loader-mt registration scan comment-aware until upstream #287 ships (#148)
- KeyboardAware lift fixes: measure the margin against the LynxView viewport via
- @vyui/kitv0.3.3
@vyui/kit v0.3.3
Patch Changes
- Overlay cleanup ahead of the shared-core refactor. (#143)
Breaking (pre-1.0 patch by repo policy): removes the deadvelocityThresholdprop fromSheetRoot(documented as unused/reserved — release logic uses the coast projection) and the consumerlessuseSheetBehavior()reactive wrapper,progressFor,pickSnap/PickSnapOpts(never adopted by Sheet;pickReleaseis the one release spec), and their types from@vyui/core. The pure spec helpers (pickRelease,directionAxis,viewportSnapsToPositions, …) remain.- kit
VyModal: wire the declared-but-deaddismissibleprop — backdrop taps are now blocked whenfalseand emitclose:prevent(mirrorsVyPopover) - sheet enter/exit keyframes now take their duration from the
durationprop (inlineanimation-durationlonghand) instead of a hardcoded 280ms, fixing the enter/settle desync for consumers likeVyTraythat passduration: 300
- kit
- Require
vue-lynx@^0.4.2and drop the local worklet-loader patch: upstream #190 now follows aliased and package worklet imports, withincludeWorkletPackagesfornode_modulesconsumers. NPM consumers must setpluginVueLynx({ includeWorkletPackages: ['@vyui/core', '@vyui/kit'] })— documented in the installation guide. (#142) - Updated dependencies [
53c027b,2e98112,df383f5,758a231,251b586,21ec23a]:- @vyui/core@0.2.3
- Overlay cleanup ahead of the shared-core refactor. (#143)
- @vyui/corev0.2.3
@vyui/core v0.2.3
Patch Changes
Buttonforwards the Lynx tap event through itstapemit (tap: [event: TouchEvent]). Previously it emitted bare, so modifier-wrapped listeners merged in viaasChild— likeToastClose's@tap.stopover a button — crashed withundefined.stopPropagationon tap, leaving the toast close button broken. (#137)- Dead-code audit: remove modules with zero consumers across core, kit, and every example app. (#138)
Breaking (pre-1.0 patch by repo policy): removes never-used public exports —withDefault,useScrollTo, and thecomponents/originals/utilitiesregistry — from@vyui/core.- remove the unexported
shared/color/module (channel/convert/gradient/parse/utils) — ported for ColorArea/ColorSlider primitives that were never built - remove unused
lynx-ui-commonports:selector.ts,version.ts,getEventDetail,mainThreadify,popoverUtils,convertToPx - remove unused reka-ui ports:
useKbd,useGraceArea,useSelectionBehavior,useFormControl,isValidVNodeElement,withDefault,useScrollTo,Arrow.vue,countryList - remove the hand-maintained
constants.tsexport registry (components/originals/utilitiesexports) - remove the 31
*.story.vuefiles (no story runner exists;story/_*.vuetest fixtures are untouched) - drop the
vue-component-type-helpersruntime dependency (only importer waswithDefault) useId: drop the Vue <3.5 fallback branch — every workspace pins Vue 3.5+
- remove the unexported
- Overlay cleanup ahead of the shared-core refactor. (#143)
Breaking (pre-1.0 patch by repo policy): removes the deadvelocityThresholdprop fromSheetRoot(documented as unused/reserved — release logic uses the coast projection) and the consumerlessuseSheetBehavior()reactive wrapper,progressFor,pickSnap/PickSnapOpts(never adopted by Sheet;pickReleaseis the one release spec), and their types from@vyui/core. The pure spec helpers (pickRelease,directionAxis,viewportSnapsToPositions, …) remain.- kit
VyModal: wire the declared-but-deaddismissibleprop — backdrop taps are now blocked whenfalseand emitclose:prevent(mirrorsVyPopover) - sheet enter/exit keyframes now take their duration from the
durationprop (inlineanimation-durationlonghand) instead of a hardcoded 280ms, fixing the enter/settle desync for consumers likeVyTraythat passduration: 300
- kit
- Export
combineGroupStatefrom Presence and use it inDialogContent, deleting the inlined copy that noted "(not exported)". (#144) - Remove two unused shared exports that had no consumers.
useStateMachine(<Presence>ships its own 1:1-ported state machine and never used it) is dropped from the public@vyui/coreentry;useForwardRef(superseded byuseForwardExpose) is dropped from the@vyui/core/sharedentry. (#140) - Require
vue-lynx@^0.4.2and drop the local worklet-loader patch: upstream #190 now follows aliased and package worklet imports, withincludeWorkletPackagesfornode_modulesconsumers. NPM consumers must setpluginVueLynx({ includeWorkletPackages: ['@vyui/core', '@vyui/kit'] })— documented in the installation guide. (#142)
- @vyui/kitv0.3.2
@vyui/kit v0.3.2
Patch Changes
- Cleanup pass: dead code removal, export gaps, and a color-parsing bug fix. (#133)
@vyui/core: exportConfigProviderfrom the public API —useLocale/useDirectiondepend on its context but had no supported way to provide it@vyui/core: remove the disposableuseMtSmokediagnostic and unusedpick/omitutils@vyui/core: typeRadio.vue's click handler with@lynx-js/types'MouseEvent, not the DOM global@vyui/core: fixparseColor()/isValidColor()—hsv()strings always threw despite being documented as a supported alias ofhsb()@vyui/kit: exporttrayfrom@vyui/kit/theme(missing, unlike every other component)@vyui/kit: add exportedSelectEmits/ComboboxEmits/FeedListEmitsinterfaces;Combobox'supdate:modelValueis now typed instead ofany
- Performance pass around tab switching and theme resolution. (#134)
@vyui/core:TabsunmountOnHide: falsenow works — a panel mounts on first visit and stays mounted (hidden viadisplay: none+accessibility-elements-hidden) after; previously the flag was threaded into context but never read, so panels always unmounted@vyui/core: newTabsdeferContentprop — commits the content swap one macrotask after the trigger/indicator update so the tab bar responds instantly while a heavy panel mounts@vyui/core:TabsContentaccepts a per-panelunmountOnHideoverride (kit:TabsItem.unmountOnHide) — panels whose subtrees write styles from main-thread worklets (setStyleProperty/animate(fill: 'forwards')) can keep painting through the kept-alivedisplay: noneon device and should opt back into unmounting; kept-hidden panels also setvisibility: hiddenas defence@vyui/kit: themetvfactories are memoized per app config (defineThemeBuilder, also insideuseStyledComponent) instead of rebuilt per component instance — visible on Lynx's interpreter whenever a screenful of components mounts@vyui/kit:TabsforwardsdeferContent, resolves slot classes once per variant change instead of per trigger per render, and its triggers regain press feedback (active:opacity-60on the trigger itself — element opacity needs no CSS inheritance)@vyui/kit: the Tailwind preset safelist now emits the EXACT classes the packaged themes generate for the configured color set (collected by walking the tv configs) instead of everyutility × color × shade × variantcombination — ~90% less generated CSS (857 KB → 84 KB in kit-demo), and the deaddata-[…]/ring-*entries are gone
- Fix tailwind preset failing to load from source under jiti (
Cannot find module './theme/index.js') — import the theme barrel with an explicit.tsextension; Vite still emits./theme/index.jsin dist. (#135) - Updated dependencies [
0e34d18,ce424a7]:- @vyui/core@0.2.2
- Cleanup pass: dead code removal, export gaps, and a color-parsing bug fix. (#133)
- @vyui/kitv0.3.1
@vyui/kit v0.3.1
Patch Changes
- 62d9f13: Dark mode via semantic design tokens.
- New role-based token layer (the nuxt/ui + shadcn convention, Lynx-adapted): text (
text-highlighted/text-default/text-toned/text-muted/text-dimmed/text-inverted), background (bg-default/bg-muted/bg-elevated/bg-accented/bg-inverted) and border (border-default/border-muted/border-accented/border-inverted). Each is a single-hopvar(--ui-*)→ concretetheme()literal, so it resolves on Lynx and flips between modes on its own. - Every theme migrated off raw
text-neutral-*/bg-neutral-*/border-neutral-*onto these tokens, so a component flips in dark with zerodark:variants. Consumers get the same for their own chrome — writetext-muted, nottext-neutral-500 dark:text-neutral-400. - A single
.darkclass on an app-root ancestor (useColorMode()) redefines the tokens to their dark values; the neutral RAMP stays fixed (rawneutral-*is a literal shade in both modes — least surprise), and only the accent mode tier shifts-500→-400. useColorMode()composable ('light' | 'dark' | 'system', plusisDark/setMode/toggle) folds'system'to the OS appearance and follows it live on web. App-root contract: bind:class="{ dark: colorMode.isDark }"+:key="colorMode.mode"— the:keyremount re-skins an already-mounted tree on Lynx native.- Mode-tier tokens hold
theme()literals (notvar(--ui-color-*)refs), fixing a latent two-levelvar()collapse on device.rounded/shadcnstyle overlays (which redeclare:root) gain the full token set for light AND dark.
- New role-based token layer (the nuxt/ui + shadcn convention, Lynx-adapted): text (
- 3c08b29:
VyInputnow paints its colored border plus a shadow-ring while focused (tracked in JS — Lynx has no:focus-within), instead of only via the statichighlightprop. The ring is a flat arbitrarybox-shadow(0 0 0 2px var(--ui-color-{color}-200)) because the Lynx preset has noring*/boxShadowColorplugins; the per-color classes are safelisted in the Tailwind preset.highlightstill forces the same treatment on permanently. - 4ee6a7d: Make KeyboardAware work under vue-lynx: the root now receives keyboard height from the input's per-element
@keyboardevent (the globalkeyboardstatuschangedevent never reaches the vue-lynx background runtime), inputs self-register with a surroundingKeyboardAwareRootwithout needing aKeyboardAwareTriggerwrap, andVyTray'skeyboardAwarenow also covers the body (new'lift' | 'scroll'modes plusbodyScrollui slot) instead of silently doing nothing without a footer slot. - d4f9b1a: Update public package and documentation copy to describe Vy UI as Lynx-native UI primitives for Vue.
- 1637c40: Sortable rows no longer drag list chrome along with the finger. The kit theme now renders each row as a transparent shell (the element core transforms) around a new
itemContentpill slot, so only the pill visibly moves — the oldborder-bdivider look is gone.SortableItemflips aui-draggingclass (+data-state="dragging") on the lifted row, andVYUI_UI_STATESgainsdraggingso themes can restyle the lifted pill (default: stronger pill border viagroup-ui-dragging:). - 93c9827: Add
VyApp, the app-root shell: owns thedarkclass +:keyremount fromuseColorMode, mountsOverlayRoot(opt-out via:overlays="false"), sets--ui-radiusvia theradiusprop, and emitsviewport-changewith the root layout size.
Also exportresolveColorHex(from the barrel and the./provideentry) so consumers can bake semantic colors intoVyIcon'scolorprop — Lynx rasterizes<svg>, sotext-*classes never color an icon.
Fix icon-only (square) buttons rendering off-center: vue-lynx realizes empty slot/v-ifanchors as real zero-size nodes, so the basegap-*added phantom width after the icon.squarenow appliesjustify-center gap-0. - Updated dependencies 4ee6a7d
- Updated dependencies d4f9b1a
- Updated dependencies 1637c40
- @vyui/core@0.2.1
- 62d9f13: Dark mode via semantic design tokens.
- @vyui/corev0.2.2
@vyui/core v0.2.2
Patch Changes
- Cleanup pass: dead code removal, export gaps, and a color-parsing bug fix. (#133)
@vyui/core: exportConfigProviderfrom the public API —useLocale/useDirectiondepend on its context but had no supported way to provide it@vyui/core: remove the disposableuseMtSmokediagnostic and unusedpick/omitutils@vyui/core: typeRadio.vue's click handler with@lynx-js/types'MouseEvent, not the DOM global@vyui/core: fixparseColor()/isValidColor()—hsv()strings always threw despite being documented as a supported alias ofhsb()@vyui/kit: exporttrayfrom@vyui/kit/theme(missing, unlike every other component)@vyui/kit: add exportedSelectEmits/ComboboxEmits/FeedListEmitsinterfaces;Combobox'supdate:modelValueis now typed instead ofany
- Performance pass around tab switching and theme resolution. (#134)
@vyui/core:TabsunmountOnHide: falsenow works — a panel mounts on first visit and stays mounted (hidden viadisplay: none+accessibility-elements-hidden) after; previously the flag was threaded into context but never read, so panels always unmounted@vyui/core: newTabsdeferContentprop — commits the content swap one macrotask after the trigger/indicator update so the tab bar responds instantly while a heavy panel mounts@vyui/core:TabsContentaccepts a per-panelunmountOnHideoverride (kit:TabsItem.unmountOnHide) — panels whose subtrees write styles from main-thread worklets (setStyleProperty/animate(fill: 'forwards')) can keep painting through the kept-alivedisplay: noneon device and should opt back into unmounting; kept-hidden panels also setvisibility: hiddenas defence@vyui/kit: themetvfactories are memoized per app config (defineThemeBuilder, also insideuseStyledComponent) instead of rebuilt per component instance — visible on Lynx's interpreter whenever a screenful of components mounts@vyui/kit:TabsforwardsdeferContent, resolves slot classes once per variant change instead of per trigger per render, and its triggers regain press feedback (active:opacity-60on the trigger itself — element opacity needs no CSS inheritance)@vyui/kit: the Tailwind preset safelist now emits the EXACT classes the packaged themes generate for the configured color set (collected by walking the tv configs) instead of everyutility × color × shade × variantcombination — ~90% less generated CSS (857 KB → 84 KB in kit-demo), and the deaddata-[…]/ring-*entries are gone
- Cleanup pass: dead code removal, export gaps, and a color-parsing bug fix. (#133)
- @vyui/corev0.2.1
@vyui/core v0.2.1
Patch Changes
- 4ee6a7d: Make KeyboardAware work under vue-lynx: the root now receives keyboard height from the input's per-element
@keyboardevent (the globalkeyboardstatuschangedevent never reaches the vue-lynx background runtime), inputs self-register with a surroundingKeyboardAwareRootwithout needing aKeyboardAwareTriggerwrap, andVyTray'skeyboardAwarenow also covers the body (new'lift' | 'scroll'modes plusbodyScrollui slot) instead of silently doing nothing without a footer slot. - d4f9b1a: Update public package and documentation copy to describe Vy UI as Lynx-native UI primitives for Vue.
- 1637c40: Sortable rows no longer drag list chrome along with the finger. The kit theme now renders each row as a transparent shell (the element core transforms) around a new
itemContentpill slot, so only the pill visibly moves — the oldborder-bdivider look is gone.SortableItemflips aui-draggingclass (+data-state="dragging") on the lifted row, andVYUI_UI_STATESgainsdraggingso themes can restyle the lifted pill (default: stronger pill border viagroup-ui-dragging:).
- 4ee6a7d: Make KeyboardAware work under vue-lynx: the root now receives keyboard height from the input's per-element
- @vyui/kitv0.3.0
@vyui/kit v0.3.0
Minor Changes
- c344b72: Per-component subpath entries for tree-shakeable consumption.
@vyui/kitnow exposes every component as its own entry point (@vyui/kit/button,@vyui/kit/tray, …) with the same canonicalVy*bindings as the barrel, so migrating is a specifier swap. Because the vue-lynx main-thread worklet pipeline prunes bysideEffectsglobs over whatever is reached (bare side-effect imports erase export usage), deep entries are the only way to ship less: importing@vyui/kit/buttonnow reaches ~37 modules / 26 worklet registrations instead of the barrel's ~294 / 118.
To make this work end-to-end, kit's build rewrites its own@vyui/corebarrel imports to per-file deep specifiers (@vyui/core/dist/components/….vue.js), and@vyui/coreexposes a./dist/*.jswildcard export to resolve them. Barrel imports (import { VyButton } from '@vyui/kit') keep working unchanged — they just keep the everything-ships behavior.
Also fixes@vyui/kit'ssideEffects: false, which let bundlers drop the package entirely from the vue-lynx main-thread worklet slice (entered via a bare side-effect import that uses no exports), so every transitively-imported@vyui/coreworklet went unregistered and consumers crashed withbind of undefined. Kit now declares the samesideEffectsglobs as core. - c344b72: Ship per-file, source-shaped ESM dist (Vite lib + Rollup
preserveModules) instead of an rslib bundle.- Fixes the
__WEBPACK_EXTERNAL_MODULE_vue_lynx_* is not definedmain-thread crash for npm consumers of worklet-driven components (VyTray, VyDrawer, Slider, …): worklet modules now keep direct namedvue-lynximports the consumer's MT toolchain can follow. - Fixes the follow-on
cannot read property 'bind' of undefinedmain-thread crash: each pre-compiled worklet module retains a"main thread"marker so the consumer'sworklet-loader-mtextracts itsregisterWorkletInternalcalls (with_wkltIds matching the background bundle) instead of dropping them.@vyui/kit'ssideEffectsis widened to keep the transitive-core-worklet MT imports from being tree-shaken. - SFC
<style>CSS is now published and auto-imported per module — the old bundle stubbed it, so consumers silently lost component styles. - Each SFC ships as a single
X.vue.js; acheck-dist-shapeguard fails the build on any bundle fingerprint or a worklet module missing its marker.
- Fixes the
Patch Changes
- a2696c9: Add an experimental
VyCalendarcomponent with ISO-string date selection and a built-in Lynx date-runtime caveat. - a2696c9: Breaking: components are now exported under a single canonical
Vy*name only. The bare aliases (Button,Card,Icon,AspectRatio, …) have been removed so there is one name per component — the same one theVyUIplugin registers globally. Update imports fromimport { Button } from '@vyui/kit'toimport { VyButton } from '@vyui/kit'. The unprefixedIcon/AspectRatioprimitives remain available from@vyui/core. - Updated dependencies c344b72
- Updated dependencies c344b72
- @vyui/core@0.2.0
- c344b72: Per-component subpath entries for tree-shakeable consumption.
- @vyui/corev0.2.0
@vyui/core v0.2.0
Minor Changes
- c344b72: Per-component subpath entries for tree-shakeable consumption.
@vyui/kitnow exposes every component as its own entry point (@vyui/kit/button,@vyui/kit/tray, …) with the same canonicalVy*bindings as the barrel, so migrating is a specifier swap. Because the vue-lynx main-thread worklet pipeline prunes bysideEffectsglobs over whatever is reached (bare side-effect imports erase export usage), deep entries are the only way to ship less: importing@vyui/kit/buttonnow reaches ~37 modules / 26 worklet registrations instead of the barrel's ~294 / 118.
To make this work end-to-end, kit's build rewrites its own@vyui/corebarrel imports to per-file deep specifiers (@vyui/core/dist/components/….vue.js), and@vyui/coreexposes a./dist/*.jswildcard export to resolve them. Barrel imports (import { VyButton } from '@vyui/kit') keep working unchanged — they just keep the everything-ships behavior.
Also fixes@vyui/kit'ssideEffects: false, which let bundlers drop the package entirely from the vue-lynx main-thread worklet slice (entered via a bare side-effect import that uses no exports), so every transitively-imported@vyui/coreworklet went unregistered and consumers crashed withbind of undefined. Kit now declares the samesideEffectsglobs as core. - c344b72: Ship per-file, source-shaped ESM dist (Vite lib + Rollup
preserveModules) instead of an rslib bundle.- Fixes the
__WEBPACK_EXTERNAL_MODULE_vue_lynx_* is not definedmain-thread crash for npm consumers of worklet-driven components (VyTray, VyDrawer, Slider, …): worklet modules now keep direct namedvue-lynximports the consumer's MT toolchain can follow. - Fixes the follow-on
cannot read property 'bind' of undefinedmain-thread crash: each pre-compiled worklet module retains a"main thread"marker so the consumer'sworklet-loader-mtextracts itsregisterWorkletInternalcalls (with_wkltIds matching the background bundle) instead of dropping them.@vyui/kit'ssideEffectsis widened to keep the transitive-core-worklet MT imports from being tree-shaken. - SFC
<style>CSS is now published and auto-imported per module — the old bundle stubbed it, so consumers silently lost component styles. - Each SFC ships as a single
X.vue.js; acheck-dist-shapeguard fails the build on any bundle fingerprint or a worklet module missing its marker.
- Fixes the
- c344b72: Per-component subpath entries for tree-shakeable consumption.
- @vyui/kitv0.2.0
@vyui/kit v0.2.0
Minor Changes
- 5109043: Add
defineVyuiConfig(new@vyui/kit/configentry) so a project's theme is authored once and fed to both the Tailwind preset (build) andprovideVyUI/app.use(VyUI)(runtime), removing the hand-syncedcolorsduplication between the two planes.createVyuiPresetnow accepts adefineVyuiConfigresult ({ ui: { colors } }) alongside the flat{ colors, neutral, shades }formcreateVyuiPresetdev-warns when a semantic color can't be backed by a--ui-color-*var (no more silent "class resolves to nothing")@vyui/kit/configis a light, jiti-safe entry — importing it intailwind.config.tsnever pulls component code into the build path
- 5109043: Add
- @vyui/corev0.1.2
@vyui/core v0.1.2
Patch Changes
- cbbbcbf: Ensure tray, drawer, and action sheet content renders above the sheet backdrop by default, and disable automatic capitalization and correction for kit email and password inputs.
- @vyui/kitv0.1.2
@vyui/kit v0.1.2
Patch Changes
- cbbbcbf: Ensure tray, drawer, and action sheet content renders above the sheet backdrop by default, and disable automatic capitalization and correction for kit email and password inputs.
- Updated dependencies cbbbcbf
- @vyui/core@0.1.2
- @vyui/corev0.1.1
@vyui/core v0.1.1
Patch Changes
- bb4d208: Add
VyTray— a morphing, multi-view bottom sheet built on the coreSheetprimitives. Views (VyTrayView) are measured per screen and the panel animates its height to fit whichever is showing ("grows into place"), with a back stack (useTray().goBack/canGoBack), a persistent#footerslot, andfloating(detached card) vsflush(edge-anchored) variants.@vyui/coreSheetContentgains afitContentprop that sizes the panel to its natural content height instead of asnapPoints × viewportfraction — the drag/slide/backdrop physics reuse the measured height, so nothing else changes. This is the modeVyTraybuilds on. - 31c2202: Add side-aware sheet and drawer motion.
SheetRootnow acceptssidefor top, right, bottom, and left edge placement with matching slide animations and drag-to-dismiss physics.VyDrawerforwards its side to the core sheet, and sheet-backed kit components can opt into alternate edges.
Edge placement and the enter/leave slide keyframes key off avyui-sheet__content--{side}class rather than a[data-side]attribute selector, so they apply on Lynx native (which does not match attribute selectors in CSS) — restoring the open/close animation on device.
- bb4d208: Add
- @vyui/kitv0.1.1
@vyui/kit v0.1.1
Patch Changes
- bb4d208: Add
VyTray— a morphing, multi-view bottom sheet built on the coreSheetprimitives. Views (VyTrayView) are measured per screen and the panel animates its height to fit whichever is showing ("grows into place"), with a back stack (useTray().goBack/canGoBack), a persistent#footerslot, andfloating(detached card) vsflush(edge-anchored) variants.@vyui/coreSheetContentgains afitContentprop that sizes the panel to its natural content height instead of asnapPoints × viewportfraction — the drag/slide/backdrop physics reuse the measured height, so nothing else changes. This is the modeVyTraybuilds on. - 31c2202: Add side-aware sheet and drawer motion.
SheetRootnow acceptssidefor top, right, bottom, and left edge placement with matching slide animations and drag-to-dismiss physics.VyDrawerforwards its side to the core sheet, and sheet-backed kit components can opt into alternate edges.
Edge placement and the enter/leave slide keyframes key off avyui-sheet__content--{side}class rather than a[data-side]attribute selector, so they apply on Lynx native (which does not match attribute selectors in CSS) — restoring the open/close animation on device. - Updated dependencies bb4d208
- Updated dependencies 31c2202
- @vyui/core@0.1.1
- bb4d208: Add
- @vyui/corev0.1.0
@vyui/core v0.1.0
Minor Changes
- 0610d70: Input/Textarea: surface the on-screen keyboard via a normalized
keyboardevent.InputandTextarea(core) andVyInput/VyTextarea(kit) now emitkeyboardwith{ visible: boolean, height: number, safeAreaBottom: number }, normalized from Lynx's raw element payload{ show, keyBoardHeight, safeAreaBottom }(note the capital B inkeyBoardHeight).- This is the reliable keyboard signal under vue-lynx: the global
GlobalEventEmitterkeyboardstatuschangedevent is emitted natively but is not delivered to the vue-lynx background runtime, so the per-element event is what consumers (and keyboard-aware lifts) should use. Seedocs/upstream/vue-lynx-keyboard.md. VyTextareaalso now forwardsconfirm/focus/blur(previously onlyupdate:modelValue), matchingVyInput.
Patch Changes
- 0610d70: FeedList: honour a
noMoreDataprop so the end-of-list footer no longer shows while more pages remain.
Previously the coreFeedListrendered thenoMoreDataFooterslot wheneverloadingMorewas false, so "no more items" appeared immediately even with pages still loadable (the kitVyFeedListalready forwardednoMoreData, but core ignored it). The footer row now only renders whileloadingMore(load-more spinner) or oncenoMoreDataistrue(end-of-list); otherwise no footer renders. - baf0692: Fix drawer/sheet not opening fully: Lynx native drops the
dvhunit, collapsing the panel to its content height. Size the sheet panel withvhand switch all viewport-height classes in the kit themes (drawer, modal, select, combobox, popover, dropdownMenu, island, actionSheet) fromdvhtovh. - 0610d70: Fix Sortable drag-to-reorder doing nothing on device/web:
- Registry was empty on the main thread. Items registered their handle via a background-thread write to the
itemHandlesMTMainThreadRef, which vue-lynx 0.4.0 silently drops. The lifted row never moved, siblings never shifted, and the drop sawcount === 0so no reorder committed. Registration (element + index) now runs on the main thread, bound tomain-thread-binduiappear, with MT teardown on unmount andrunOnMainThreadsetter worklets for index/disabled sync. - Long-press used MT
setTimeout. The main-thread worklet runtime does not exposesetTimeout/clearTimeout(internal in@lynx-js/types), so the long-press timer threw and the row never lifted for anylongPressMs > 0. The hold is now timed by pollingrequestAnimationFrameon the main thread.
Public props/emits/slots unchanged. - Registry was empty on the main thread. Items registered their handle via a background-thread write to the
- 300e34f: Keep sheet snap and drag geometry synchronized with dynamic viewport changes, and make kit swipers fill their measured container when no explicit item width is provided.
- 0610d70: Input/Textarea: surface the on-screen keyboard via a normalized
- @vyui/kitv0.1.0
@vyui/kit v0.1.0
Minor Changes
- 0610d70: Input/Textarea: surface the on-screen keyboard via a normalized
keyboardevent.InputandTextarea(core) andVyInput/VyTextarea(kit) now emitkeyboardwith{ visible: boolean, height: number, safeAreaBottom: number }, normalized from Lynx's raw element payload{ show, keyBoardHeight, safeAreaBottom }(note the capital B inkeyBoardHeight).- This is the reliable keyboard signal under vue-lynx: the global
GlobalEventEmitterkeyboardstatuschangedevent is emitted natively but is not delivered to the vue-lynx background runtime, so the per-element event is what consumers (and keyboard-aware lifts) should use. Seedocs/upstream/vue-lynx-keyboard.md. VyTextareaalso now forwardsconfirm/focus/blur(previously onlyupdate:modelValue), matchingVyInput.
Patch Changes
- 300e34f: Add small horizontal (landscape) mode support — the work that gets the kit-demo running on mobile in landscape. Compact, viewport-safe layouts for overlays, sheets, menus, tabs, steppers, islands, cards, alerts, and toasts. Select and Combobox sheet structure is now fully themeable for responsive overrides.
- 0610d70: Input: forward
@focusand@blurevents.- The core
Inputemitsfocus/blur(with the current value), but theVyInputwrapper only re-emittedupdate:modelValueandconfirm, so consumers couldn't react to focus changes (e.g. to drive a keyboard-aware lift). It now forwards both.
- The core
- 0610d70: IslandButton: bake the icon color so the theme's foreground actually applies.
IslandButtonrendered its glyph through Lynx's<svg>, which rasterizes the XML and can't inheritcurrentColor— so thetext-slate-*utility on theleadingIconslot (and the darkertext-slate-900active shade) never reached the icon, leaving it stuck on its default fill (invisible on dark/active pills).- It now resolves the foreground utility off the merged
leadingIconclass — honoring the active state and any consumerui.leadingIconoverride — and passes it to<VyIcon :color>, matching the pattern already used byButton,Input,Alert, andCombobox. Non-palette colors (e.g. arbitrarytext-[#abc]) fall back to the icon'scurrentColordefault.
- 491db6a: Default input chrome to neutral: form controls no longer paint a primary border or icon by default.
- Resting border on
Input,Textarea,Select,Combobox,NumberFieldandPinInputis now neutral (border-neutral-200) regardless ofcolor— matching the no-focus-state reality on Lynx. The colored border stays available as opt-in via thehighlightprop. - Leading/trailing icons (and
NumberFieldsteppers) now default to neutral (dimmed) instead of thecolorpalette; override per-icon with your own:colorvia theleading/trailingslots. RadioGroup: space the control from its label withgap-2on the item instead ofms-2on the wrapper — Lynx ignores logical inline margins, which collapsed the dot and text together.
- Resting border on
- baf0692: Fix drawer/sheet not opening fully: Lynx native drops the
dvhunit, collapsing the panel to its content height. Size the sheet panel withvhand switch all viewport-height classes in the kit themes (drawer, modal, select, combobox, popover, dropdownMenu, island, actionSheet) fromdvhtovh. - 300e34f: Keep sheet snap and drag geometry synchronized with dynamic viewport changes, and make kit swipers fill their measured container when no explicit item width is provided.
- 14e0722: Add
@vyui/cli, a shadcn-style CLI that copies@vyui/kitcomponents (and their dependencies) into a project from a style-namespaced registry, rewriting imports to the consumer's aliases.init/add/stylescommands; tsconfig/jsconfig alias + package-manager detection.- Shadcn-style project preflight and automatic, idempotent app-entry/Tailwind wiring.
list,view,info, interactiveadd, and--dry-rundiscovery/preview workflows.- Safe upgrades: explicit components may be overwritten while shared files and transitive dependencies remain user-owned.
- Registry targets are contained to the project root (rejects
..// absolute / null-byte paths). - Cyclic registry dependency graphs resolve instead of deadlocking.
kit: drive the switch thumb with flex justification instead oftranslate-x-*(Lynx dropstransformpainting), and reset the native<textarea>user-agent border. - 300e34f: Make vertical tabs reserve a navigation rail and let their content fill the remaining width. Vertical triggers no longer inherit the horizontal
pillvariant'sflex-1stretch — they keep their natural height and left-align their icon/label so the rail reads as a sidebar list. - Updated dependencies 0610d70
- Updated dependencies 0610d70
- Updated dependencies baf0692
- Updated dependencies 0610d70
- Updated dependencies 300e34f
- @vyui/core@0.1.0
- 0610d70: Input/Textarea: surface the on-screen keyboard via a normalized
- @vyui/corev0.0.6
Gesture & scroll motion overhaul
A motion-focused release: the scroll and gesture primitives gain the physics they were missing on native Lynx.
FeedList gets a full pull-to-refresh + paging surface. Pull-to-refresh is a custom rubber-band driven by
:main-thread-bindtouch*worklets on a bare<list>— it only engages at the top edge, so normal scrolling and load-more are untouched. New:enableRefresh+v-model:refreshing,refreshThreshold,refresh/refreshStateChangeemits and arefreshHeaderslot ({ state, progress });enableBouncerubber-band overscroll at both edges;itemSnapnativeitem-snappaging with asnapemit; and debounced load-more onscrolltolowerwithloadMoreFooter/noMoreDataFooterslots. The legacy native<refresh>path (unregistered in the OSS Lynx runtime) is removed.ScrollView adds a main-thread custom bounce/overscroll system mirroring lynx-ui:
enableBounces,singleSidedBounce,alwaysBouncing, trigger-distance and estimated-size props,enableRTL,upperBounceItem/lowerBounceItemslots, and anonScrollToBouncesevent — driven by the newuseBouncecomposable.Swiper loop is now truly seamless — edge slides are cloned and the transform rebases invisibly across the first↔last seam under both drag-release and autoplay. Adds
loop/circular,axisLock,autoplay+interval, plus lynx-ui prop parity:spaceBetween,mode,align+containerWidth,offsetLimit, andrtl.Sortable, Draggable & SwipeAction tighten on-device gesture fidelity: velocity-aware release (fling/momentum), axis locking, autoscroll near Sortable list edges, and bounds clamping, via shared helpers in
shared/gesture/physics.ts. Fixes two regressions — Sortable long-press now arms entirely on the main thread (the BG round-trip was being dropped on-device), and SwipeAction cancels its in-flightfill: 'forwards'snap on touchstart so a follow-up slow drag isn't masked.Toast internals are prepped for stacking:
ToastRootbinds its own@layoutchangeto feed fan-out geometry, exposesdurationandprogressslot values, and ships a newToastSwipemain-thread swipe-to-dismiss layer (exporting the unit-testeddecideDismisspolicy). - @vyui/kitv0.0.4
Stacked toasts & FeedList gestures
@vyui/kit@0.0.4brings the new core gesture surface to the styled components.Toast gains Sonner-style behaviour, all off by default:
stacked— collapses toasts into an overlapping pile (front toast fully visible, the rest peeking scaled-down behind it) and fans them out when expanded; tap to toggle. PairstackFrom(top|bottom) with theToastViewportposition.swipe(+swipeDirection) — fling a toast sideways to dismiss. The card rides an innerToastSwipelayer so the swipe transform never collides with the stacking transform.progress— a thin countdown bar that drains with the auto-dismiss timer (pauses while expanded, hidden whenduration: 0).
A plain
VyToaststill renders as a single gapped-column card.FeedList wrapper now forwards the full pull-to-refresh surface to core —
enableRefresh,v-model:refreshing,refreshThreshold,enableBounce, therefresh/refreshStateChangeemits, and therefreshHeaderslot ({ state, progress }) — plus the newitemSnapprop andsnapemit. TheloadMoreFooterslot is aligned with core's new no-arg signature (the footer renders only while loading). - @vyui/corev0.0.5
@vyui/core v0.0.5
Patch Changes
- Add AspectRatio — headless
@vyui/coreprimitive (AspectRatioRoot, exported as bothAspectRatioandAspectRatioRoot) that constrains its default slot to a givenratio(number, default1). (#46)
Built for the Lynx render layer: it renders a single<view>using the native CSSaspect-ratioproperty (supported by Lynx's Starlight layout engine), with no absolutely-positioned padding wrapper. - Add Avatar — Lynx-native
@vyui/coreprimitives (AvatarRoot/AvatarImage/AvatarFallback).AvatarRootprovides image load-status context;AvatarImagerenders a Lynx<image>and downgrades to the error state onbinderror(@error);AvatarFallbackshows when no image is loaded, with adelayMsflash-avoidance delay. (#46)
Refactor@vyui/kit'sVyAvatarto compose the new core primitives for behaviour (load-status + fallback) while keeping its publicAvatarPropsAPI, initials derivation, chip overlay, theming, andAvatarGroupsize/color inheritance unchanged. - Fixes from #67, #68 and #70. (#71)
@vyui/core:- Icon: reject
colorvalues that could inject SVG markup when resolving icon sources (#67). - Sheet: multi-snap drag now settles to the nearest snap point, with main-thread usage fixes across
SheetContentImpl,DraggableanduseDragGesture(#68). - Primitive: treat
imageas a self-closing leaf — Vue's empty-slot fragment/comment anchors were materialized as real children by vue-lynx, and a native<image>with any child fails to render (native-only breakage; lynx-web tolerated it) (#70).
@vyui/kit:- Forward icon classes/props through ActionSheet, Alert, Button, Tabs, Toast, ToggleGroup and DropdownMenu items, and fix Drawer/theme slot classes so drawer animations work again (#70).
- Icon: reject
- Add AspectRatio — headless
- @vyui/kitv0.0.3
@vyui/kit v0.0.3
Patch Changes
- Add Avatar — Lynx-native
@vyui/coreprimitives (AvatarRoot/AvatarImage/AvatarFallback).AvatarRootprovides image load-status context;AvatarImagerenders a Lynx<image>and downgrades to the error state onbinderror(@error);AvatarFallbackshows when no image is loaded, with adelayMsflash-avoidance delay. (#46)
Refactor@vyui/kit'sVyAvatarto compose the new core primitives for behaviour (load-status + fallback) while keeping its publicAvatarPropsAPI, initials derivation, chip overlay, theming, andAvatarGroupsize/color inheritance unchanged. - Configurable semantic colors (nuxt/ui-style), defined once and extensible. (#48)
- Single source of truth
theme/color-constants.js(shared by themes + Tailwind preset);neutralsplit out of the configurableCOLORSlist and appended automatically (nuxt/ui parity). - Theme files are now builder functions
(colors) => themeObject;useStyledComponentinvokes them with the resolved list soappConfig.ui.colorsconfigures the set at runtime. @vyui/kit/tailwindadds acreateVyuiPreset({ colors })factory; the default export is unchanged.- True type parity via the augmentable
VyuiColorRegistryinterface —declare module '@vyui/kit' { interface VyuiColorRegistry { tertiary: true } }makes custom colors autocomplete + typo-check on every componentcolorprop, no build plugin needed.scripts/gen-colors.mjsgenerates the registry augmentation + CSS-var block. - Fixes a latent
ThemeTVwidening that typedcolor(and other variants) asPropertyKeyonuseStyledComponent-based components.
Breaking: theme default exports changed from objects to builder functions;COLORSno longer includesneutral(useALL_COLORS). - Single source of truth
- Fixes from #67, #68 and #70. (#71)
@vyui/core:- Icon: reject
colorvalues that could inject SVG markup when resolving icon sources (#67). - Sheet: multi-snap drag now settles to the nearest snap point, with main-thread usage fixes across
SheetContentImpl,DraggableanduseDragGesture(#68). - Primitive: treat
imageas a self-closing leaf — Vue's empty-slot fragment/comment anchors were materialized as real children by vue-lynx, and a native<image>with any child fails to render (native-only breakage; lynx-web tolerated it) (#70).
@vyui/kit:- Forward icon classes/props through ActionSheet, Alert, Button, Tabs, Toast, ToggleGroup and DropdownMenu items, and fix Drawer/theme slot classes so drawer animations work again (#70).
- Icon: reject
- Fix stranded foreground colors on Lynx (
enableCSSInheritance: false). Color set on a wrapping<view>slot never reached the nested text/icon, so it rendered in the default color — invisible on dark/colored surfaces (e.g. a neutral-solid button or solid card). (#52)
Foregroundtext-*now lands on the text-bearing slots (label / title / description / icon / input value) acrossinput,textarea,numberField,select,combobox,toggle,toggleGroup,chip,islandButton,card,accordion,dropdownMenu,tabs,stepper. State-driven colors on child elements use thegroup-data-[state=…]form, and the Tailwind preset safelist is widened to cover those variants so they aren't purged. - Updated dependencies [
1b0d3bc,1b0d3bc,9a0241c]:- @vyui/core@0.0.5
- Add Avatar — Lynx-native
- @vyui/corev0.0.4
@vyui/core v0.0.4
Patch Changes
- Add NumberField — headless
@vyui/coreprimitive (NumberFieldRoot/NumberFieldInput/NumberFieldIncrement/NumberFieldDecrement) with min/max/step, clamp/snap and decimal-precision handling, plus a styledVyNumberFieldin@vyui/kit. (#44)
FixInputnot reflecting programmatic value changes on native Lynx — controlled updates that don't originate from typing are now pushed through the imperativesetValuepath (the reactivevaluebinding is initial-only on a native<input>). This makes NumberField's increment/decrement buttons update the field on iOS/Android, not just web.
Avatar now falls back to initials/icon when its image fails to load (wires the Lynx<image>binderrorevent).
DocumentVyComboboxas the autocomplete pattern —searchablefiltering over a fixed set covers the use case, so there is no separate Autocomplete component.
Widen@vyui/kit's@vyui/corepeer-dependency range from^to~so it tracks0.0.xcore patches without forcing a major bump.
- Add NumberField — headless
- @vyui/kitv0.0.2
NumberField, Island DX & polish
@vyui/kit@0.0.2adds a component, sharpens an existing one, and fixes a handful of native-Lynx rough edges.NumberField — a new headless
@vyui/coreprimitive (NumberFieldRoot/NumberFieldInput/NumberFieldIncrement/NumberFieldDecrement) with min/max/step, clamp/snap and decimal-precision handling, plus a styledVyNumberFieldin@vyui/kit.Island gets a clearer API and better defaults. A new
layerprop (overlay/base/inline) splits stacking from placement:positionnow only picks the viewport edge (top/bottom), whilelayerdecides whether the island floats over content, sits on a low layer beneath modals and drawers, or drops into normal flow for a parent to place. Floating is applied via an inlinestyle, so a lone<VyIsland>hovers with no wrapper on Lynx. Out of the boxIslanddefaults tooverlayandIslandGrouptobottom, so both float sensibly with zero config.Fixes & docs
Inputnow reflects programmatic value changes on native Lynx — controlled updates that don't come from typing are pushed through the imperativesetValuepath, so NumberField's increment/decrement work on iOS and Android, not just web.Avatarfalls back to initials/icon when its image fails to load, wiring the Lynx<image>binderrorevent.VyComboboxis documented as the autocomplete pattern —searchablefiltering covers the use case, so there's no separate Autocomplete component.- The
@vyui/corepeer-dependency range widens from^to~, so kit tracks0.0.xcore patches without forcing a major bump.
- @vyui/corev0.0.3
Lynx-native a11y & shared gestures
Accessibility is wired through Lynx-native APIs instead of DOM assumptions, and the Swiper's gesture physics is extracted into a shared engine behind
useDragGesturewith a genericpickSnaphelper — ready to power any swipe-driven primitive. - @vyui/kitv0.0.1
Styled Button, Switch & Tabs
@vyui/kitpublishes its first styled components — Button, Switch, and Tabs — composed on top of@vyui/coreprimitives. Available now on npm as@vyui/kit@0.0.1. - @vyui/corev0.0.2
Performance & packaging
Icon resolution is memoized across instances to cut repeated work, with dependency pruning and packaging hardening to keep
@vyui/corelean and safe to install. - @vyui/corev0.0.1
First release
@vyui/corepublishes its first primitives: behavioural, accessible building blocks that run natively on Lynx and on the web from a single Vue codebase. Zero styling — compose them yourself or reach for@vyui/kit. - @vyui/kitInitial
Kit scaffolded
@vyui/kitis scaffolded as part of the monorepo, sitting alongside@vyui/coreand the documentation site — the home for opinionated, themed components.