@viax.io/uxm 4.43.0 → 4.43.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/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## [4.43.1](https://github.com/viax-io/uxm/compare/v4.43.0...v4.43.1) (2026-09-21)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **app-sidebar:** hide the closed mobile drawer, not just translate it ([b397ee0](https://github.com/viax-io/uxm/commit/b397ee088c4431b8e4306fe27cfe371dc88a8bc3))
7
+ * **app-sidebar:** ship the mobile-drawer CSS the markup already assumed ([8f02e21](https://github.com/viax-io/uxm/commit/8f02e21218133e344690115914e03616e4804848))
8
+
1
9
  # [4.43.0](https://github.com/viax-io/uxm/compare/v4.42.0...v4.43.0) (2026-09-16)
2
10
 
3
11
 
package/README.md CHANGED
@@ -104,6 +104,100 @@ import { ButtonPrimary, themeTokens } from '@viax.io/uxm';
104
104
  | `@viax.io/uxm/studio/generate-css` | `generateOverridesCss` + the CSS sanitizers — turn saved studio overrides and a `BrandConfig` into a stylesheet on the server, without pulling in the workbench UI. |
105
105
  | `@viax.io/uxm/studio.css` | Tailwind utilities for the studio shell + token declarations. Does **not** bundle the atom CSS — a studio host imports `ui.css` alongside it. |
106
106
 
107
+ ## CDN usage
108
+
109
+ For pages that can't run npm at all — prototypes, CodePen repros, embeds in someone else's site — every release also publishes a handful of self-contained `<script>`/`<link>`-ready bundles, built by `npm run build:cdn` and uploaded automatically on release.
110
+
111
+ > **If your project has a bundler, this is not what you want.** Use [`npm i @viax.io/uxm`](#install) — the CDN bundles below exist specifically for pages that can't.
112
+
113
+ All files live at `https://uxm.viax.io/<version>/<file>` — versioned and immutable (`Cache-Control: public, max-age=31536000, immutable`). There is no `/latest/`: pin an exact version, and use the same version across every file you load on one page.
114
+
115
+ | File | Format | React | For |
116
+ |---|---|---|---|
117
+ | `uxm.esm.js` | ESM, `react` / `react-dom` / `react/jsx-runtime` **external** | the host page's, via import map | pages that already have React 19 |
118
+ | `uxm.standalone.js` | IIFE, `window.UXM`, React **bundled in** | its own, inside the bundle | pages with **no** React at all |
119
+ | `uxm.css` | flattened `tokens.css` + `ui.css` | — | both |
120
+ | `uxm-generate-css.esm.js` | ESM, no deps | — | only if you need `generateOverridesCss` (the theming applier) |
121
+ | `uxm.esm.d.ts` / `uxm-generate-css.esm.d.ts` | bundled `.d.ts` per JS artifact | needs `@types/react` | editor/type-check support |
122
+ | `cdn-manifest.json` | byte size, gzip size, `sha384` SRI hash per file | — | verify what you fetched |
123
+
124
+ ⚠️ **React 19 ships no UMD build.** The classic "two `<script>` tags from a CDN plus our UMD on top" isn't possible — that's why there are two separate formats above instead of one.
125
+
126
+ ### ESM — page that already has React 19
127
+
128
+ ```html
129
+ <script type="importmap">
130
+ {
131
+ "imports": {
132
+ "react": "https://esm.sh/react@19",
133
+ "react-dom/client": "https://esm.sh/react-dom@19/client",
134
+ "@viax.io/uxm/ui": "https://uxm.viax.io/4.43.0/uxm.esm.js"
135
+ }
136
+ }
137
+ </script>
138
+ <link rel="stylesheet" href="https://uxm.viax.io/4.43.0/uxm.css" />
139
+ <script type="module">
140
+ import { ButtonPrimary } from '@viax.io/uxm/ui';
141
+ import { createElement } from 'react';
142
+ import { createRoot } from 'react-dom/client';
143
+
144
+ createRoot(document.getElementById('root')).render(createElement(ButtonPrimary, null, 'Hello UXM'));
145
+ </script>
146
+ ```
147
+
148
+ ### Standalone — page with no React
149
+
150
+ ```html
151
+ <link rel="stylesheet" href="https://uxm.viax.io/4.43.0/uxm.css" />
152
+ <script src="https://uxm.viax.io/4.43.0/uxm.standalone.js"></script>
153
+ <script>
154
+ const { React, createRoot, ButtonPrimary } = window.UXM;
155
+ createRoot(document.getElementById('root')).render(React.createElement(ButtonPrimary, null, 'Hello UXM'));
156
+ </script>
157
+ ```
158
+
159
+ ⚠️ **Never load `uxm.standalone.js` on a page that already has another React instance** — two copies of React break hooks and context. If the page has React, use the ESM build above instead.
160
+
161
+ ### Required host baseline
162
+
163
+ A CDN page gets none of the resets a bundler-based app inherits from its own global stylesheet. Add this once, after `uxm.css` — skip it and the app renders in Times New Roman with inputs overflowing their containers. This is the same baseline npm consumers need; see `skills/viax-uxm/references/quick-recipes.md` (recipe 0) for the full version and why each line is load-bearing.
164
+
165
+ ```css
166
+ *, *::before, *::after { box-sizing: border-box; }
167
+ html, body, #root { height: 100%; }
168
+ :root { --font-sans: var(--font-inter, 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif); }
169
+ html, body, #root { font-family: var(--brand-font, var(--font-sans)); }
170
+ button, input, select, textarea { font: inherit; }
171
+ ```
172
+
173
+ ### Types
174
+
175
+ The `.d.ts` bundles need `@types/react` in your project — they extend React's own prop types (`ButtonHTMLAttributes` and friends), which aren't ours to inline. Fetch one alongside your CDN import and map it in `tsconfig.json`:
176
+
177
+ ```sh
178
+ UXM=4.43.0
179
+ curl -fsSL -o types/uxm.esm.d.ts https://uxm.viax.io/$UXM/uxm.esm.d.ts
180
+ ```
181
+
182
+ ```json
183
+ { "compilerOptions": { "paths": { "@viax.io/uxm/ui": ["./types/uxm.esm.d.ts"] } } }
184
+ ```
185
+
186
+ If your project can take even a dev-only npm dependency, `npm i -D @viax.io/uxm@<version>` is simpler and gives every subpath's types at once — the curl route above exists for projects that can't.
187
+
188
+ ### Verifying what you fetched (SRI)
189
+
190
+ `cdn-manifest.json` next to every version's files lists a `sha384` hash per file — pass it as `integrity` on the `<script>` tag:
191
+
192
+ ```html
193
+ <script
194
+ type="module"
195
+ src="https://uxm.viax.io/4.43.0/uxm.esm.js"
196
+ integrity="sha384-X5wmfaVzW+An31gN2SpznF1F6UxTbD4SsCynzec/bElec2EneSTZf2l0O81YHLVt"
197
+ crossorigin="anonymous"
198
+ ></script>
199
+ ```
200
+
107
201
  ## Component catalog
108
202
 
109
203
  Every component folder ships a `README.md` documenting props, CSS variables, MODO-configurable design tokens, states/variants, and accessibility. Click through any name for the full reference.
@@ -185,4 +185,89 @@
185
185
  }
186
186
  .uxm-app-sidebar__footer--collapsed {
187
187
  font-size: inherit;
188
+ }
189
+
190
+ /* ── Mobile drawer ──────────────────────────────────────────────────────────
191
+ The 768px line is `AppTopBar`'s: its `__menu` hamburger is hidden at
192
+ min-width 768px, so below that a hamburger is on screen driving `mobileOpen`.
193
+ `app-sidebar.tsx` has always emitted `--mobile-open` and `__mobile-backdrop`
194
+ for it (and its comment promised "CSS hides both `--mobile-*` chrome at
195
+ min-width: 768px"), but the stylesheet never carried the matching rules — the
196
+ classes rendered and nothing reacted, so the 256px rail stayed in flow and
197
+ pushed the page sideways. These three rules are that missing half.
198
+
199
+ Physical `left` + `translateX`, not the logical pair: nothing in `src/ui`
200
+ handles `[dir]` today, and a logical inset with a physical transform would
201
+ anchor the drawer to the inline-start edge in RTL while still sliding it the
202
+ other way. When the library takes on RTL, both move together.
203
+ ──────────────────────────────────────────────────────────────────────────── */
204
+ @media (max-width: 767px) {
205
+ .uxm-app-sidebar {
206
+ /* Out of flow, so the shell's row collapses to the content column and the
207
+ page stops overflowing horizontally. */
208
+ bottom: 0;
209
+ box-shadow: var(--uxm-app-sidebar-drawer-shadow, var(--shadow-lg));
210
+ left: 0;
211
+ position: fixed;
212
+ top: 0;
213
+ /* Closed by default at this width — `--mobile-open` is what slides it in,
214
+ so a consumer that never wires `mobileOpen` gets a hidden drawer rather
215
+ than a rail parked over its content. */
216
+ transform: translateX(-100%);
217
+ /* `width` stays in the transition: the collapse toggle is still reachable
218
+ here. `visibility` is stepped, not eased — delayed by the slide duration
219
+ when closing so the drawer is still painted while it leaves, and
220
+ immediate when opening so it is visible from the first frame. */
221
+ transition: transform 0.2s ease, width 0.2s ease, visibility 0s linear 0.2s;
222
+ /* Translating alone would leave the closed drawer VISIBLE-but-off-screen:
223
+ its nav rows stay keyboard tab stops with no focus indicator anywhere on
224
+ screen, screen readers announce the landmark as present, and its
225
+ box-shadow — zero x-offset, 20px blur, right edge resting exactly on x=0
226
+ — bleeds a grey strip down the left of every page. `hidden` takes it out
227
+ of the tab order and the a11y tree, and stops it painting. */
228
+ visibility: hidden;
229
+ /* Below `--z-dialog` (60) and `--z-toast` (80): a dialog opened from a nav
230
+ row, and any toast, must still cover the drawer. */
231
+ z-index: var(--z-drawer, 40);
232
+ }
233
+ .uxm-app-sidebar--mobile-open {
234
+ transform: translateX(0);
235
+ transition: transform 0.2s ease, width 0.2s ease, visibility 0s;
236
+ visibility: visible;
237
+ }
238
+ .uxm-app-sidebar__mobile-backdrop {
239
+ /* Matches `.uxm-dialog__backdrop` — same scrim knobs, so an app that
240
+ retints one gets the other for free. */
241
+ backdrop-filter: blur(var(--backdrop-blur, 4px));
242
+ -webkit-backdrop-filter: blur(var(--backdrop-blur, 4px));
243
+ background-color: var(--backdrop-color, rgba(0, 0, 0, 0.5));
244
+ border: none;
245
+ bottom: 0;
246
+ cursor: pointer;
247
+ left: 0;
248
+ /* It is a <button>, so the UA padding has to go or it inflates the hit
249
+ area's box beyond the viewport. */
250
+ padding: 0;
251
+ position: fixed;
252
+ right: 0;
253
+ top: 0;
254
+ z-index: calc(var(--z-drawer, 40) - 1);
255
+ }
256
+ }
257
+ /* The backdrop only renders while `mobileOpen` is true, and a consumer holding
258
+ that flag through a resize past the breakpoint would otherwise be left with a
259
+ full-viewport scrim over an in-flow sidebar. */
260
+ @media (min-width: 768px) {
261
+ .uxm-app-sidebar__mobile-backdrop {
262
+ display: none;
263
+ }
264
+ }
265
+ /* A full-height panel flying in from the edge is exactly the motion a reduced-
266
+ motion preference is about; the drawer still opens and closes, it just
267
+ arrives. First such guard in `src/ui` — the pattern to copy when another
268
+ atom needs one. */
269
+ @media (prefers-reduced-motion: reduce) {
270
+ .uxm-app-sidebar {
271
+ transition: none;
272
+ }
188
273
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viax.io/uxm",
3
- "version": "4.43.0",
3
+ "version": "4.43.1",
4
4
  "description": "Viax UXM — React 19 UI primitives and design tokens.",
5
5
  "license": "MIT",
6
6
  "private": false,