@synopackageland/ui 0.25.3 → 0.25.5

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.
@@ -1,5 +1,8 @@
1
- /** Container width (px) at which the gallery gains its 2nd…6th column (layout-spec §2.6). */
2
- export declare const GALLERY_BREAKPOINTS: readonly [640, 768, 1024, 1280, 1536];
1
+ /**
2
+ * Container width (px) at which the gallery gains its 2nd…6th column (layout-spec §2.6). A page beside a
3
+ * sidebar on a portrait tablet (about 470px) holds two cards of at least 212px; a phone keeps one.
4
+ */
5
+ export declare const GALLERY_BREAKPOINTS: readonly [440, 720, 1024, 1280, 1536];
3
6
  /** Gallery columns for a container `width` px wide; 0 while the width is unknown. */
4
7
  export declare function galleryColumns(width: number): number;
5
8
  /**
@@ -299,9 +299,9 @@ const { state, width } = useAsidePreference('studio.aside', 'editor', { state: '
299
299
  | --- | --- | --- |
300
300
  | docked | `web`, the page keeps 480px | keeps `--ui-aside-open-width` and slides by `translate` while the region's width moves (`--ui-motion-panel`, ease-in-out both ways); the rail button fades in 150ms into a collapse |
301
301
  | floating | an open panel the page has no room for, or a 641–959 container | slides over the page from the rail (in `panel` ease-out, out `normal` ease-in); the region keeps its 48px |
302
- | sheet | a phone (≤640) | a 48px button at the end corner (above the tab bar and the safe area) opens a 90% bottom sheet; `expanded` is full screen; the scrim, Escape and a downward swipe on the header close it; the page is inert |
302
+ | sheet | a phone (≤640) | a 48px button at the end corner (above the tab bar and the safe area) opens a 90% bottom sheet; `expanded` is full screen; the scrim, Escape and a downward swipe on the header close it; the page is `aria-hidden` and out of reach |
303
303
 
304
- - The panel is never unmounted: collapsed, it is `inert` and `aria-hidden`, and the thread's scroll and the composer's draft stay. The page is `inert` while the panel is expanded or a sheet is up.
304
+ - The panel is never unmounted: collapsed, it is `inert` and `aria-hidden`, and the thread's scroll and the composer's draft stay. The page is `inert` and `aria-hidden` while the panel is expanded (it has no width then). While a sheet is up the page is `aria-hidden` but not `inert`: the sheet keeps it out of reach as an app-local dialog does (docs/app-local-modal.md).
305
305
  - Focus moves to the composer's first field on open (not on touch, `initial-focus` changes it) and back to the rail button or the phone button on collapse.
306
306
  - Escape: docked and expanded leave it to the content (stop a reply). A floating panel with focus inside, or a sheet, collapses on Escape after any layer opened above it (a menu, a popover) has closed.
307
307
  - No transition runs before the first paint has settled. The state needs no `transitionend`: only a floating panel's exit waits for one, with the `normal` token as its deadline, and a new state change or reduced motion ends it at once.
@@ -23,9 +23,9 @@ errors; a rejected overlay must be closed/unmounted by the app's error handler.
23
23
 
24
24
  | Concern | Document mode (opt-in) | App mode (default) |
25
25
  | --- | --- | --- |
26
- | Pointer | Original Reka document stack and body lock. | Inert only this root's background and lower local modal layers; other roots/host remain interactive. |
26
+ | Pointer | Original Reka document stack and body lock. | The mask over this root's background takes presses, wheel and touch; lower local modal layers are inert. Other roots/host remain interactive. |
27
27
  | Scroll | Shared body scroll lock and compensation policy. | Lock the background wrapper, preserve its scroll position, and contain backdrop wheel/touch gestures locally. Active panels and host/other-app scrolling remain native. |
28
- | Accessibility | Original Reka hideOthers semantics. | Local inert/aria-hidden ownership; keep the active dialog and registered popup branches accessible. Dialogs keep role, title and description references and omit aria-modal, since the rest of the page is available. |
28
+ | Accessibility | Original Reka hideOthers semantics. | Local aria-hidden on the background and inert/aria-hidden on lower layers; keep the active dialog and registered popup branches accessible. Dialogs keep role, title and description references and omit aria-modal, since the rest of the page is available. |
29
29
  | Focus | Original document FocusScope coordination. | Loop Tab/Shift+Tab in the active local panel while focus belongs to it. Deliberate other-app/host transitions remain outside the local trap. |
30
30
  | Escape | Original document layer routing. | Route once to the focused root's top layer. IME composition is ignored and cancellation is preserved. Host/body events infer no local app. |
31
31
  | Cleanup | Shared Reka lock counts. | Restore locally owned inert/ARIA/style values only when still owned. Remove listeners/branches on unmount and never mutate body. |
@@ -38,6 +38,21 @@ Nested panels retain the parent background lock, temporarily hide/inert the
38
38
  parent panel, and restore its interaction on close. Presence/exit animations
39
39
  retain layer ownership until Reka actually removes the layer.
40
40
 
41
+ The background is not `inert`: Chrome restyles every element under an element
42
+ whose `inert` changes, so an inert background made opening a dialog restyle the
43
+ whole app before its first frame. The mask, the focus loop and `focusin` (focus
44
+ that reaches the background goes back to the panel) keep the background out of
45
+ reach; focus left on the background when a dialog skips autofocus is released,
46
+ as inert would.
47
+
48
+ AppLayout's phone overlays work the same way. The navigation drawer and the
49
+ aside's bottom sheet are local modal layers. The FAB speed dial is a trapped
50
+ focus scope over its own scrim: Tab loops in the dial and focus that reaches
51
+ the page comes back, although the dial is not a layer. While any of them is
52
+ open the page column (`[data-ui-layout-main]`) is `aria-hidden`, never
53
+ `inert`. Only an expanded aside, which leaves the page no width, makes the page
54
+ `inert`.
55
+
41
56
  A host must provide an accessible window switch mechanism: a looping Tab
42
57
  sequence inside a local dialog does not itself reach another app. There is no
43
58
  claim of whole-document modality in app mode. VoiceOver/NVDA exposure and
@@ -127,7 +142,7 @@ DropdownMenu), ContextMenu (through Menu), and Tooltip use the same patched
127
142
  DismissableLayer/focus boundary. Portal branches are part of the local stack;
128
143
  closing a child does not also close the parent. Tooltip-open coordination is
129
144
  local to the root. Toast portals/live announcements remain in the sibling
130
- portal host, outside the inert background. Raw Reka primitives from a separate
145
+ portal host, outside the hidden background. Raw Reka primitives from a separate
131
146
  consumer import are outside this contract. Each popper wrapper takes its
132
147
  z-index from the local layer stack, not from Reka's copy of the content's own
133
148
  z-index, so a modal backdrop never covers a popup opened inside the modal.
@@ -139,7 +154,7 @@ submenu-only step for DropdownMenu and OverflowToolbar in both document and
139
154
  app-local mode.
140
155
 
141
156
  The wrapper is the registered background scroll container in this version.
142
- Scroll containers within it are blocked by inert/background containment while
157
+ Scroll containers within it are blocked by the mask and background containment while
143
158
  a local modal is present. Apps with their own external Teleports or desktop
144
159
  scrolling cannot claim independent containment. Keep custom portals inside the
145
160
  root and inside the active panel, or use the library's supported popup components.
@@ -156,7 +171,11 @@ Independent modality is the default.
156
171
  module plus exact before/after hunks. `build/rekaPatch.ts` verifies the installed
157
172
  version and all input hashes before the build; each hunk must match exactly
158
173
  once. The Vite plugin loads patched modules in memory, never edits node_modules,
159
- and is also used by Vitest. Any source/hash/hunk mismatch fails the build.
174
+ and is also used by Vitest and every dev server (Histoire, the gallery,
175
+ `tests/dsm-host`). The dependency optimizer would pre-bundle reka-ui without the
176
+ patch, so the plugin keeps reka-ui out of `optimizeDeps` in any config it is
177
+ in; `tests/dev-server-reka.test.ts` fails if a dev config or a script's dev
178
+ server goes without it. Any source/hash/hunk mismatch fails the build.
160
179
 
161
180
  The body-scroll patch follows the U3 shared-lock candidate: save owned body
162
181
  padding-right, margin-right, overflow, pointer-events and root scrollbar-width
@@ -724,8 +724,11 @@ change: the same slot moves.
724
724
  as labelled 48px pills over a scrim, staggered in. Focus moves to the first
725
725
  and stays in the dial; Escape, Back, a press on the scrim or the FAB close
726
726
  it and focus returns to the FAB. A pick runs the action and closes the dial;
727
- an action that opens a menu closes it once the menu closes. The page behind
728
- is inert while it is open. Reduced motion shows and hides it at once.
727
+ an action that opens a menu closes it once the menu closes. While it is
728
+ open the page behind is out of reach: the scrim takes presses, Tab loops in
729
+ the dial, focus that reaches the page comes back, and the page is
730
+ `aria-hidden` (not `inert`, which would restyle the whole page). Reduced
731
+ motion shows and hides it at once.
729
732
  - Every pill is its icon and its text. An action with no text of its own (an
730
733
  `IconButton` such as "More actions", an icon-only Button) shows its
731
734
  accessible name, from `aria-label` or `title`; the pill sizes to it and a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synopackageland/ui",
3
- "version": "0.25.3",
3
+ "version": "0.25.5",
4
4
  "description": "Shared Vue 3 UI component library with a DSM 7 inspired application theme",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -79,6 +79,34 @@
79
79
  "@tiptap/vue-3": "3.32.1",
80
80
  "zod": "^4.6.5"
81
81
  },
82
+ "devDependencies": {
83
+ "@eslint/js": "10.0.1",
84
+ "@histoire/plugin-vue": "1.0.0-beta.1",
85
+ "@tailwindcss/vite": "4.3.3",
86
+ "@tanstack/vue-virtual": "3.13.39",
87
+ "@vitejs/plugin-vue": "6.0.9",
88
+ "@vue/test-utils": "2.5.1",
89
+ "@vueuse/core": "14.4.0",
90
+ "eslint": "10.11.0",
91
+ "eslint-plugin-vue": "10.11.1",
92
+ "flexsearch": "0.8.212",
93
+ "globals": "17.12.0",
94
+ "histoire": "1.0.0-beta.1",
95
+ "jsdom": "30.1.1",
96
+ "playwright": "1.58.2",
97
+ "reka-ui": "2.11.0",
98
+ "tailwindcss": "4.3.3",
99
+ "typescript": "5.9.3",
100
+ "typescript-eslint": "8.71.0",
101
+ "vite": "7.3.6",
102
+ "vite-plugin-dts": "5.1.1",
103
+ "vitest": "5.0.2",
104
+ "vscode-oniguruma": "2.0.1",
105
+ "vscode-textmate": "9.3.2",
106
+ "vue": "3.5.43",
107
+ "vue-eslint-parser": "10.4.1",
108
+ "vue-tsc": "3.3.11"
109
+ },
82
110
  "engines": {
83
111
  "node": ">=20"
84
112
  },
@@ -89,5 +117,65 @@
89
117
  "reka-ui": "2.11.0"
90
118
  }
91
119
  },
92
- "homepage": "https://www.varvis.me/docs/"
93
- }
120
+ "homepage": "https://www.varvis.me/docs/",
121
+ "scripts": {
122
+ "check:docs": "node --test scripts/check-doc-versions.test.mjs && node scripts/check-doc-versions.mjs",
123
+ "dev": "vite build --watch",
124
+ "build": "vite build && vite build --mode json && vite build --mode rich-text && vite build --mode grid && vite build --mode cli && vite build --mode preview && vite build --mode embedded && node scripts/postbuild.mjs",
125
+ "lint": "eslint .",
126
+ "typecheck": "vue-tsc --noEmit -p tsconfig.json",
127
+ "test": "vitest run",
128
+ "test:watch": "vitest",
129
+ "test:changed": "echo \"test:changed skips the tests the changes since ${SYNOPKGLAND_GATE_BASE:-origin/master} do not reach (vitest --changed); pnpm check runs them all\" && vitest run --changed ${SYNOPKGLAND_GATE_BASE:-origin/master}",
130
+ "story": "histoire dev",
131
+ "story:build": "histoire build",
132
+ "check:host": "node scripts/dsm-host-shots.mjs",
133
+ "check:switch": "node scripts/check-switch.mjs",
134
+ "check:tabs": "node scripts/check-tabs.mjs",
135
+ "check:mobile": "node scripts/check-mobile-overflow.mjs",
136
+ "check:ui-patch": "node scripts/check-ui-patch.mjs",
137
+ "check:narrow": "node scripts/check-narrow-controls.mjs",
138
+ "check:drawer-form": "node scripts/check-drawer-form.mjs",
139
+ "check:responsive": "node scripts/check-responsive.mjs",
140
+ "check:adaptive": "node scripts/check-adaptive.mjs",
141
+ "check:sidebar-focus": "node scripts/check-sidebar-focus.mjs",
142
+ "check:json-render": "node scripts/check-json-render.mjs",
143
+ "check:motion": "node scripts/check-motion.mjs",
144
+ "check:topology": "node scripts/check-topology.mjs",
145
+ "check:wizard": "node scripts/check-wizard.mjs",
146
+ "check:press": "node scripts/check-press.mjs",
147
+ "check:edges": "node scripts/check-edges.mjs",
148
+ "check:data-grid": "node scripts/check-data-grid.mjs",
149
+ "check:truncation": "node scripts/check-truncation.mjs",
150
+ "check:link": "node scripts/check-link.mjs",
151
+ "check:text-select": "node scripts/check-text-select.mjs",
152
+ "check:date-picker": "node scripts/check-date-picker.mjs",
153
+ "check:full-page": "node scripts/check-full-page.mjs",
154
+ "check:short-viewport": "node scripts/check-short-viewport.mjs",
155
+ "check:phone-layout": "node scripts/check-phone-layout.mjs",
156
+ "check:phone-bars": "node scripts/check-phone-bars.mjs",
157
+ "check:text-zoom": "node scripts/check-text-zoom.mjs",
158
+ "check:focus-restore": "node scripts/check-focus-restore.mjs",
159
+ "check:detail-surface": "node scripts/check-detail-surface.mjs",
160
+ "check:internal": "node --test scripts/check-internal-content.test.mjs scripts/check-npm-metadata.test.mjs && node scripts/check-internal-content.mjs --repo .",
161
+ "check:npm-metadata": "node scripts/check-npm-metadata.mjs",
162
+ "check:package": "node scripts/check-package-content.mjs",
163
+ "trace:restyle": "node scripts/trace-restyle.mjs",
164
+ "check:public-api-consumer": "node scripts/check-public-api-consumer.mjs",
165
+ "notices:update": "node scripts/third-party-notices.mjs --write",
166
+ "notices:check": "node scripts/third-party-notices.mjs --check",
167
+ "check": "bash scripts/docker-check.sh \"npm run gates\"",
168
+ "check:fast": "bash scripts/docker-check.sh \"npm run gates\"",
169
+ "check:local-browser": "node scripts/app-local-browser.mjs",
170
+ "check:infotip": "node scripts/check-infotip.mjs",
171
+ "release:stage": "node scripts/stage-release.mjs",
172
+ "check:polish": "bash scripts/docker-check.sh --build --out .polish-shots 'node scripts/check-polish.mjs'",
173
+ "check:acceptance": "node scripts/check-acceptance.mjs",
174
+ "check:palette": "node scripts/check-palette.mjs",
175
+ "gates": "node scripts/gates.mjs",
176
+ "gates:full": "node scripts/gates.mjs --full",
177
+ "check:layout": "node scripts/check-layout.mjs",
178
+ "release:smoke": "node scripts/release-smoke.mjs",
179
+ "check:browser-install": "playwright install chromium webkit"
180
+ }
181
+ }