diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..2bd5a0a9 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a865cc6..fa82dc71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,19 +13,348 @@ item (see `ROADMAP.md`, Week 4). ## [Unreleased] -Tracking toward **1.9.0**, classified **minor** per [RELEASING.md](RELEASING.md): -additive only — a new package consumers may adopt, new resolution paths, and -internals changes that keep observable behavior identical. Nothing is removed, -narrowed, or deprecated, and no peer-dependency floor moves. +Heading to **2.0**, not to 1.9. `package.json` carries `2.0.0-alpha.0` so +nothing here can be published as a version nobody chose — 1.9.0 was a working +number bumped ahead of any release decision, and it is now skipped entirely. + +The `ThemeInit` fix that briefly lived under a 1.9.0 heading shipped instead as +**1.8.4** (published 2026-08-28), cut from the tree that produced the published +1.8.3 so it reached `^1.8.x` consumers with nothing to migrate. Its entry is +below, in its own release. + +Classification is **open**. The framework adapter below is a migration step for +Next.js apps, which is not a minor — see the release PR for the options. + +**One migration step for Next.js apps.** No component, prop, export, token, or +CSS class is removed or renamed; no type union narrows; no peer-dependency floor +moves; the package installs exactly the same dependency set as 1.8.3. But +`ElementType` (which backs `SmartLink` and any `Button` / `Card` / +`ToggleButton` with `href`), `Media`, `Logo`, `MegaMenu` and `Kbar` now render +through the adapter layer, whose defaults are plain DOM — ``, ``, +`window.location.assign`. Keeping 1.8.x behavior is a one-line change — the +import path for `LayoutProvider`: + +```diff +- import { LayoutProvider } from "@once-ui-system/core"; ++ import { LayoutProvider } from "@once-ui-system/core/next"; +``` + +That provider is core's `LayoutProvider` with the Next adapters already +installed. No provider is added to the tree and no props change. Apps that +compose `AdapterProvider` themselves can keep using `NextAdapterProvider` +directly. + +Without either, internal links full-page reload and images skip `next/image` +optimization. The DOM fallbacks are what make core usable outside Next, and are +covered by `adapter-fallbacks.test.tsx`. + +Automatic detection was investigated and rejected on evidence rather than +taste. The bundler half works — a guarded `await import("next/link")` builds +clean under esbuild and Vite with no Next installed, and degrades to the DOM +fallback. React is the blocker: the DOM `useNavigate` returns a closure while +the Next one calls `useRouter` and `useCallback`, so swapping implementations +after mount breaks the rules of hooks. Resolution must therefore settle before +the first render, and a browser bundle has no synchronous way to conditionally +resolve an optional module. + +### Breaking + +**Prop API standardisation.** Every place where one prop name carried two +meanings, or one meaning went by two names, is resolved. All of it is +mechanical: `scripts/codemod-2.0.mjs` applies the renames component-scoped, +and running it twice is a no-op. + +```bash +node scripts/codemod-2.0.mjs src +``` + +Boolean props that toggle visibility now read `showX`, leaving the plain name +for the thing itself: + +| Component | 1.8.x | 2.0 | +| --- | --- | --- | +| `ProgressBar` | `label?: boolean` | `showLabel` | +| `Feedback`, `Toast` | `icon?: boolean` | `showIcon` | +| `DataTooltip` | `colors?: boolean` | `showSwatches` | + +State props drop the `is`/`has` prefix, restoring the convention the docs +already prescribed (`basics/components` — "use `open` instead of `isOpen`"): + +| Component | 1.8.x | 2.0 | +| --- | --- | --- | +| `Dialog`, `Modal`, `DatePicker`, `DropdownWrapper`, `EmojiPickerDropdown`, `KbarContent` | `isOpen` | `open` | +| `Checkbox`, `RadioButton`, `Switch` | `isChecked` | `checked` | +| `Checkbox` | `isIndeterminate` | `indeterminate` | +| `DatePicker`, `DropdownWrapper` | `isNested` | `nested` | +| `NavIcon` | `isActive` | `active` | +| `Input`, `Textarea`, `Option` | `hasPrefix` / `hasSuffix` | `prefix` / `suffix` | + +Four of those names were held by React's own DOM attribute types — `checked` +and `size` on `InputHTMLAttributes`, `prefix` on the base `HTMLAttributes` (the +RDFa attribute) — which is why the prefixes existed at all. Those components +now `Omit` the inherited declaration and declare their own. The cost is that +the native attribute can no longer be forwarded: `` is the token +scale, not the HTML character-width attribute. + +`radius` now means one thing everywhere — the roundness scale that +`StyleProps` has always defined. Corner selection, which had been overloading +the same name on five components, moves to `corners`: + +```diff +-