@abuzareal/contour-ui 0.2.1 → 0.3.0
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 +17 -0
- package/COMPOSITION.md +193 -0
- package/CONTRACTS.md +24 -24
- package/NEXTJS.md +3 -1
- package/PUBLIC-API.md +174 -174
- package/README.md +7 -7
- package/dist/components/ui/Button.d.ts +10 -2
- package/dist/components/ui/Button.js +7 -3
- package/dist/components/ui/Checkbox.d.ts +7 -1
- package/dist/components/ui/Checkbox.js +8 -6
- package/dist/components/ui/DepthButton.d.ts +5 -2
- package/dist/components/ui/DepthButton.js +8 -3
- package/dist/components/ui/Dialog.d.ts +21 -3
- package/dist/components/ui/Dialog.js +17 -11
- package/dist/components/ui/ExternalLink.d.ts +3 -2
- package/dist/components/ui/ExternalLink.js +7 -3
- package/dist/components/ui/IconButton.d.ts +10 -2
- package/dist/components/ui/IconButton.js +7 -3
- package/dist/components/ui/Popover.d.ts +19 -4
- package/dist/components/ui/Popover.js +16 -9
- package/dist/components/ui/Slider.d.ts +2 -1
- package/dist/components/ui/Slider.js +5 -4
- package/dist/components/ui/Switch.d.ts +2 -1
- package/dist/components/ui/Switch.js +5 -4
- package/dist/components/ui/Tabs.d.ts +16 -4
- package/dist/components/ui/Tabs.js +46 -24
- package/dist/components/ui/ThemeToggle.d.ts +2 -1
- package/dist/components/ui/ThemeToggle.js +5 -3
- package/dist/hooks/useControlRef.js +2 -2
- package/dist/hooks/useTabRefs.d.ts +4 -0
- package/dist/hooks/useTabRefs.js +12 -0
- package/dist/lib/componentProps.d.ts +5 -0
- package/dist/lib/componentProps.js +1 -0
- package/dist/lib/composeEventHandlers.d.ts +4 -0
- package/dist/lib/composeEventHandlers.js +8 -0
- package/dist/lib/composeRefs.d.ts +3 -0
- package/dist/lib/composeRefs.js +24 -0
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,23 @@ Changes to the published `@abuzareal/contour-ui` package. Version headings refer
|
|
|
6
6
|
|
|
7
7
|
No changes recorded.
|
|
8
8
|
|
|
9
|
+
## 0.3.0
|
|
10
|
+
|
|
11
|
+
Native refs and focused composition APIs for existing components. This heading records source work, not npm publication.
|
|
12
|
+
|
|
13
|
+
- Button/IconButton/DepthButton/ThemeToggle, ExternalLink, Checkbox, Slider and Switch forward their native targets on React 18/19. Existing native field refs remain; callback refs stay stable across ordinary prop updates and support React 19 cleanup.
|
|
14
|
+
- Dialog, Tabs and Popover accept native root props and typed props/refs for existing parts. Custom IDs preserve relationships; internal state/ARIA/classes remain intact. Caller handlers can prevent default selection/toggle/dismissal, while native dialog cancel remains prop-controlled.
|
|
15
|
+
- Tabs retain ID-based focus/refs on reorder and remeasure the underline when root/list customization changes. Existing controlled/default state, menu disabled items/typeahead/stable IDs and focus return remain supported.
|
|
16
|
+
- Publish native anchor and pinned Next Link recipes, preserving ordinary/canceled/middle-click navigation without nesting interactive elements or adding Button polymorphism. No runtime dependency or CSS/theme/form contract change is introduced.
|
|
17
|
+
|
|
18
|
+
Existing JSX calls remain valid. Render components through React; newly ref-enabled exports are forwardRef components rather than plain callable functions. Named Switch/Slider/RadioGroup submission, overlay positioning/nesting corrections and CSS isolation remain separate work. See [COMPOSITION.md](COMPOSITION.md).
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
npm install @abuzareal/contour-ui@0.3.0 --save-exact
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Use that command after registry availability is verified; update the exact pin/lockfile and validate your app.
|
|
25
|
+
|
|
9
26
|
## 0.2.1
|
|
10
27
|
|
|
11
28
|
A baseline contract release with compatible Checkbox ARIA and clipboard lifecycle fixes. Export names, prop types, visual defaults, dependencies and optional peer boundaries are unchanged from 0.2.0.
|
package/COMPOSITION.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Refs, parts, state and links
|
|
2
|
+
|
|
3
|
+
Contract version **0.3.0**. These additions are absent from npm 0.2.1; a source heading does not establish publication. Use the version named by the release notes. Import components directly and load CSS once. This guide describes existing DOM parts; no copying step or polymorphic Button system is required.
|
|
4
|
+
|
|
5
|
+
## Native ref targets
|
|
6
|
+
|
|
7
|
+
| Export | Target and use |
|
|
8
|
+
| -------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
9
|
+
| Button, IconButton, DepthButton, ThemeToggle | HTMLButtonElement: focus, measurement, external trigger adapter |
|
|
10
|
+
| ExternalLink | HTMLAnchorElement: focus and native-link integration |
|
|
11
|
+
| TextField, Checkbox, Slider | HTMLInputElement: focus/measurement; Checkbox retains indeterminate behavior |
|
|
12
|
+
| TextArea / SelectField | HTMLTextAreaElement / HTMLSelectElement; existing native reset/validation preserved |
|
|
13
|
+
| Dialog | HTMLDialogElement: focus/measurement; open stays prop-owned, do not bypass it with showModal/close |
|
|
14
|
+
| Tabs | Root HTMLDivElement; list/button/panel parts have separate native refs |
|
|
15
|
+
| Popover | Root HTMLDivElement; triggerProps.ref is its owned button, panelProps.ref its local panel |
|
|
16
|
+
|
|
17
|
+
Object refs become null on unmount/ref replacement. Callback refs receive the node then null; on React 19 they may return cleanup instead. Internal refs coexist with caller refs. Stable callback identities avoid deliberate ref replacement. Targets remain mounted while a dialog/panel is closed/hidden.
|
|
18
|
+
|
|
19
|
+
RadioGroup, SegmentedControl, Pagination and DropdownMenu contain multiple controls; no singular input/action ref is claimed. Field is a frame, Tooltip describes a caller-owned child, Disclosure has native details semantics, and FlipCard/Tag have conditional controls. Presentation/scene wrappers receive no arbitrary native control handle. Part refs for those require a demonstrated reusable need.
|
|
20
|
+
|
|
21
|
+
React 18 compatibility uses forwardRef; React 19 uses the same JSX API. Render through React; do not call exports as plain functions or depend on typeof being function. [React ref guidance](https://react.dev/reference/react/forwardRef) explains why low-level controls expose DOM nodes.
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { useRef } from "react";
|
|
25
|
+
import { Button, TextField } from "@abuzareal/contour-ui";
|
|
26
|
+
export function FocusName() {
|
|
27
|
+
const action = useRef<HTMLButtonElement>(null);
|
|
28
|
+
const input = useRef<HTMLInputElement>(null);
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
<TextField ref={input} label="Name" name="name" />
|
|
32
|
+
<Button ref={action} onClick={() => input.current?.focus()}>
|
|
33
|
+
Focus name
|
|
34
|
+
</Button>
|
|
35
|
+
</>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
JavaScript uses the same imports/JSX and `useRef(null)` without type arguments.
|
|
41
|
+
|
|
42
|
+
## Root and part customization
|
|
43
|
+
|
|
44
|
+
Root className/style/id/data/ARIA and supported native handlers target the documented native root. Classes append to defaults. Part props customize existing DOM; children/raw markup/replacement components stay owned by Contour. CSS stays global; this API adds no isolation, scoped theme, portal or collision engine.
|
|
45
|
+
|
|
46
|
+
| API | DOM target |
|
|
47
|
+
| ------------------------------------------ | ------------------------------------------------------------------------------------------ |
|
|
48
|
+
| Dialog root/ref | dialog; title is its visible heading, onClose remains a no-argument request |
|
|
49
|
+
| Dialog panelProps/bodyProps/footerProps | div.dialog-panel / div.dialog-body / footer.dialog-footer; footer only mounts with content |
|
|
50
|
+
| Dialog closeButtonProps | Native close button; protected type=button, default label Close dialog |
|
|
51
|
+
| Tabs root/ref / listProps | Outer div.tabs / div with tablist role and state-owned indicator variables |
|
|
52
|
+
| TabItem.buttonProps/panelProps | Tab button / div tabpanel, each with native ref/id/class/style/events/data/ARIA |
|
|
53
|
+
| Popover root/ref / triggerProps/panelProps | Outer div / owned trigger button / local nonmodal panel |
|
|
54
|
+
|
|
55
|
+
Roles, button types, tabIndex, selected/expanded/hidden state and ARIA relationships stay component-owned. Custom tab/panel IDs update both sides of their relationships. Extra label/description IDs compose with required IDs; callers supply referenced nodes. Native styles/classes can still make a composition unusable. Disabled-tab semantics are not introduced by buttonProps.
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
import { useRef, useState } from "react";
|
|
59
|
+
import { Button, Dialog, Popover, Tabs } from "@abuzareal/contour-ui";
|
|
60
|
+
export function SettingsParts() {
|
|
61
|
+
const [open, setOpen] = useState(false);
|
|
62
|
+
const [activeTabId, setActiveTabId] = useState("profile");
|
|
63
|
+
const dialog = useRef<HTMLDialogElement>(null);
|
|
64
|
+
const body = useRef<HTMLDivElement>(null);
|
|
65
|
+
const help = useRef<HTMLButtonElement>(null);
|
|
66
|
+
return (
|
|
67
|
+
<>
|
|
68
|
+
<Button onClick={() => setOpen(true)}>Open settings</Button>
|
|
69
|
+
<Dialog
|
|
70
|
+
ref={dialog}
|
|
71
|
+
id="settings"
|
|
72
|
+
className="account-dialog"
|
|
73
|
+
open={open}
|
|
74
|
+
onClose={() => setOpen(false)}
|
|
75
|
+
title="Settings"
|
|
76
|
+
bodyProps={{ ref: body, "data-owner": "account" }}
|
|
77
|
+
closeButtonProps={{ "aria-label": "Close settings" }}
|
|
78
|
+
>
|
|
79
|
+
<Tabs
|
|
80
|
+
label="Settings sections"
|
|
81
|
+
activeTabId={activeTabId}
|
|
82
|
+
onTabChange={setActiveTabId}
|
|
83
|
+
listProps={{ className: "account-tabs" }}
|
|
84
|
+
tabs={[
|
|
85
|
+
{
|
|
86
|
+
id: "profile",
|
|
87
|
+
label: "Profile",
|
|
88
|
+
content: <p>Profile content</p>,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
id: "preferences",
|
|
92
|
+
label: "Preferences",
|
|
93
|
+
content: <p>Preferences content</p>,
|
|
94
|
+
},
|
|
95
|
+
]}
|
|
96
|
+
/>
|
|
97
|
+
<Popover
|
|
98
|
+
trigger="Help"
|
|
99
|
+
title="Settings help"
|
|
100
|
+
triggerProps={{ ref: help }}
|
|
101
|
+
panelProps={{ id: "settings-help", className: "account-help" }}
|
|
102
|
+
>
|
|
103
|
+
<p>Local panels need room in their layout.</p>
|
|
104
|
+
</Popover>
|
|
105
|
+
</Dialog>
|
|
106
|
+
</>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Popover still has local absolute placement and the known nested-dismissal limit. trigger is content inside an owned button, not an arbitrary interactive replacement. No submenu/initial-focus API is implied. Overlay corrections remain separate.
|
|
112
|
+
|
|
113
|
+
## Event composition
|
|
114
|
+
|
|
115
|
+
For the composed action handlers on Dialog, Tabs and Popover, the caller handler on the same owned element runs first. preventDefault vetoes internal tab click/arrow selection, popover trigger toggle, and dialog close-button/backdrop dismissal. stopPropagation controls propagation, not this cancellation contract. Root bubbling handlers run after descendant handlers; intercept through the relevant part or capture phase.
|
|
116
|
+
|
|
117
|
+
Dialog onCancel receives the native event. Caller preventDefault vetoes onClose; Contour always prevents the browser from closing independently of open. An allowed cancel requests onClose and the parent must update open. Native disabled buttons cannot activate; Button loading also disables it. Native field counter/resize bookkeeping runs before its caller input/change handler and remains intact; preventDefault on those notifications is not a counter-reset API.
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
// Fragment: the application owns open, saving and its operation/recovery.
|
|
121
|
+
<Dialog
|
|
122
|
+
open={open}
|
|
123
|
+
title="Unsaved changes"
|
|
124
|
+
onClose={() => setOpen(false)}
|
|
125
|
+
onCancel={(event) => {
|
|
126
|
+
if (saving) event.preventDefault();
|
|
127
|
+
}}
|
|
128
|
+
closeButtonProps={{ disabled: saving }}
|
|
129
|
+
>
|
|
130
|
+
<p>Finish the current operation before closing.</p>
|
|
131
|
+
</Dialog>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
If dismissal is suppressed, provide a usable cancel/recovery path. Do not disable essential interaction indefinitely.
|
|
135
|
+
|
|
136
|
+
## State and callback audit
|
|
137
|
+
|
|
138
|
+
| Family | Existing ownership/callback contract |
|
|
139
|
+
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
|
|
140
|
+
| Button/IconButton/DepthButton/ExternalLink | Native events; supported button disabled/loading; navigation remains browser/router-owned |
|
|
141
|
+
| TextField/TextArea/SelectField/Checkbox | Native event callbacks; controlled value/checked or native defaults; refs, validity/reset remain intact |
|
|
142
|
+
| Slider / Switch | Required number value / boolean checked, onChange(value); no new name/form/default/reset API |
|
|
143
|
+
| RadioGroup / SegmentedControl | Required string value, onChange(value); generated radio name / button group |
|
|
144
|
+
| Tabs | activeTabId + onTabChange(id), or internal defaultTabId; invalid/removed IDs fall back to first available |
|
|
145
|
+
| Popover / DropdownMenu | open + onOpenChange(boolean), or internal defaultOpen; unchanged values do not notify |
|
|
146
|
+
| Dialog | Required open and onClose() request; no independent imperative ownership |
|
|
147
|
+
| Disclosure | defaultOpen on native details; no controlled open/onOpenChange |
|
|
148
|
+
| Pagination | page/pageCount and onChange(page); app owns URL/fetch/bounds |
|
|
149
|
+
| FlipCard / Tooltip | Internal flipped / hover-focus state; no new controlled API |
|
|
150
|
+
| ThemeToggle/useTheme | Shared document theme/preference, not scoped provider |
|
|
151
|
+
| Tag, EmptyState, Alert, toast APIs | Caller removal/actions or supplied feedback/library toast commands; no business operation/persistence implied |
|
|
152
|
+
|
|
153
|
+
Do not switch controlled/uncontrolled ownership mid-mount. A later default does not reset established state. Controlled callbacks never silently overwrite parent state. Native event callbacks are not cosmetically renamed into value callbacks. Switch/Slider/RadioGroup form integration remains FORM scope.
|
|
154
|
+
|
|
155
|
+
## Native and router links
|
|
156
|
+
|
|
157
|
+
Use Button for actions and an anchor/router Link for navigation. buttonClassName supplies existing styles without interception. Preserve modified clicks, context menus, open-in-new-tab, destinations and keyboard activation. Do not nest interactive Buttons and anchors.
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
import { buttonClassName } from "@abuzareal/contour-ui";
|
|
161
|
+
export function ReportsLink() {
|
|
162
|
+
return (
|
|
163
|
+
<a href="/reports" className={buttonClassName("secondary")}>
|
|
164
|
+
Open reports
|
|
165
|
+
</a>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The pinned Next App Router configuration renders a native anchor and owns client navigation. Put interactive handlers in a client boundary. This does not claim universal router support.
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
"use client";
|
|
174
|
+
import { useRef } from "react";
|
|
175
|
+
import Link from "next/link";
|
|
176
|
+
import { buttonClassName } from "@abuzareal/contour-ui";
|
|
177
|
+
export function NextReportsLink() {
|
|
178
|
+
const link = useRef<HTMLAnchorElement>(null);
|
|
179
|
+
return (
|
|
180
|
+
<Link ref={link} href="/reports" className={buttonClassName("secondary")}>
|
|
181
|
+
Open reports
|
|
182
|
+
</Link>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Other routers can use the same recipe when their documented Link forwards standard anchor props/ref. Browser checks cover native links and middle-click background tabs; the pinned Next fixture covers ordinary, canceled and middle-click navigation. [Next Link documentation](https://nextjs.org/docs/app/api-reference/components/link) distinguishes onClick from onNavigate: modified clicks retain native behavior.
|
|
188
|
+
|
|
189
|
+
Anchors have no native disabled state. Render a suitable non-link explanation when navigation is unavailable, or omit the destination with explicit disabled semantics and app-owned keyboard/pointer behavior. Use a disabled Button when it is an action. CSS alone does not disable a link. ExternalLink retains scheme filtering and new-tab opener protection; internal routes use the router Link.
|
|
190
|
+
|
|
191
|
+
## Abstraction review
|
|
192
|
+
|
|
193
|
+
Each addition serves a bounded need: native focus/measurement/trigger refs; existing Dialog/Tabs/Popover root/part access; ID-based reorder-safe tab focus; caller handler cancellation. Private helpers implement those lifecycles. No runtime dependency, provider, build/distribution/styling system, generic component-replacement API or blanket polymorphism is introduced. Existing menu disabled items, keyboard opening, typeahead, stable identity and focus return remain covered.
|
package/CONTRACTS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Architecture, scope and compatibility
|
|
2
2
|
|
|
3
|
-
Contract version **0.
|
|
3
|
+
Contract version **0.3.0**, reviewed 12 October 2026. Contour is an early 0.x React library with typed controls, paper/ink surfaces, a lime accent, light/dark themes, restrained motion and separate optional depth/scene APIs. The [public API and maturity register](PUBLIC-API.md) lists every exported value, type and stylesheet, its maintainer, status, use and limits.
|
|
4
4
|
|
|
5
5
|
## Intended adoption scope
|
|
6
6
|
|
|
@@ -16,10 +16,10 @@ No promise of enterprise completeness, universal embedding, all locales/RTL, eve
|
|
|
16
16
|
|
|
17
17
|
## Package and application ownership
|
|
18
18
|
|
|
19
|
-
| Area
|
|
20
|
-
|
|
|
21
|
-
| Reusable controls, semantic tokens, shared theme/motion/browser lifecycle, public APIs and package releases
|
|
22
|
-
| Catalogue shell/chapters, executable documentation/source display, discovery, examples and site delivery
|
|
19
|
+
| Area | Owner |
|
|
20
|
+
| ------------------------------------------------------------------------------------------------------------- | ---------------- |
|
|
21
|
+
| Reusable controls, semantic tokens, shared theme/motion/browser lifecycle, public APIs and package releases | contour-ui |
|
|
22
|
+
| Catalogue shell/chapters, executable documentation/source display, discovery, examples and site delivery | Design-System |
|
|
23
23
|
| Product layouts/routes, domain data/calculations, workflows, requests, validation and product-specific scenes | Each application |
|
|
24
24
|
|
|
25
25
|
Repositories remain independent. Contour source is public and MIT licensed; catalogue source is currently private while hosted documentation is public. The catalogue's `private: true` prevents npm publication; it is separate from GitHub visibility. Visibility, history/assets/licensing review and CI runner policy are separate decisions.
|
|
@@ -30,12 +30,12 @@ Catalogue UI/hook/scene compatibility modules intentionally forward public packa
|
|
|
30
30
|
|
|
31
31
|
React is the UI runtime. Strict TypeScript/TSX source compiles through `tsc` into ES2022 ESM modules and declarations. The build copies CSS and selected licensed Latin font assets. It does not bundle React or compile the library into a Vite app. Vite serves development/consumer fixtures; Design-System is the Vite application. Consumers can write JavaScript or TypeScript.
|
|
32
32
|
|
|
33
|
-
| Category
|
|
34
|
-
|
|
|
35
|
-
| Runtime dependencies
|
|
36
|
-
| Required peers
|
|
37
|
-
| Optional peers
|
|
38
|
-
| Development dependencies | TypeScript, Vite, Oxlint, Testing Library, jsdom, Playwright, React/types and optional peer dev copies | Source authoring/tests/build; not downstream Contour production dependencies.
|
|
33
|
+
| Category | Current packages | Consumer consequence |
|
|
34
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
|
|
35
|
+
| Runtime dependencies | lucide-react; @fontsource-variable/dm-sans; @fontsource-variable/space-grotesk; @fontsource/dm-mono | Install with Contour. Selected imports determine browser output. Font CSS is optional; font installation currently is not. |
|
|
36
|
+
| Required peers | react/react-dom: React 18 (`^18.3.1`) or React 19 (`^19.0.0`) | App supplies a matching pair; allowed range is broader than individually tested versions. |
|
|
37
|
+
| Optional peers | three: `^0.186.1`; lenis: `^1.3.26` | Core works without either. Install explicitly for the matching optional entry. |
|
|
38
|
+
| Development dependencies | TypeScript, Vite, Oxlint, Testing Library, jsdom, Playwright, React/types and optional peer dev copies | Source authoring/tests/build; not downstream Contour production dependencies. |
|
|
39
39
|
|
|
40
40
|
Use explicit public paths; deep imports into dist internals are unsupported. CSS is marked as side-effectful to retain imported styles. The archive contains built modules/declarations/assets and release documents, with no consumer install scripts or private research.
|
|
41
41
|
|
|
@@ -43,16 +43,16 @@ Archive bytes, installed dependency bytes, browser JS/CSS, emitted fonts and act
|
|
|
43
43
|
|
|
44
44
|
## Environment and validation policy
|
|
45
45
|
|
|
46
|
-
| Environment
|
|
47
|
-
|
|
|
48
|
-
| React/ReactDOM
|
|
49
|
-
| TypeScript
|
|
50
|
-
| Node
|
|
51
|
-
| ESM/CSS-capable bundler | Packed Vite consumers exercise imports/declarations/CSS/fonts with no optional peers
|
|
52
|
-
| Next.js
|
|
53
|
-
| Optional Three
|
|
54
|
-
| Optional scroll
|
|
55
|
-
| Browsers
|
|
46
|
+
| Environment | Declared or exercised scope | Limits |
|
|
47
|
+
| ----------------------- | ------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
|
|
48
|
+
| React/ReactDOM | Peer range above; packed matrix exercises 18.3.1, 19.0.0 and 19.3.0 | Matching pairs required. Other allowed patches are not individually certified. |
|
|
49
|
+
| TypeScript | Strict source/fixtures; pinned optional/Next fixtures use 5.9.3; React 18 and 19 declarations compiled | No universal older/newer compiler support claim; JS users need no compiler at runtime. |
|
|
50
|
+
| Node | Manifest >=22 for installation/tooling; use 22.12+ or 24+ with current Vite tooling | Browser components do not need Node in the browser. Actual runner versions are recorded per check. |
|
|
51
|
+
| ESM/CSS-capable bundler | Packed Vite consumers exercise imports/declarations/CSS/fonts with no optional peers | No CommonJS build or unbundled-browser CSS loader promised. |
|
|
52
|
+
| Next.js | Next 16.4.0 / React and ReactDOM 19.3.0, App Router, webpack production build | [NEXTJS.md](NEXTJS.md); other versions, Turbopack, Pages Router and Next Lenis remain unverified. |
|
|
53
|
+
| Optional Three | Separate consumers and lazy Next mode; explicit Three/types, WebGL, sized host and static fallback | Experimental API; sampled lifecycle checks do not validate every custom scene/GPU/device. |
|
|
54
|
+
| Optional scroll | Separate declaration/build consumer with Lenis and one scroll owner | Caller handles pause/reduced motion; generic Next Lenis integration unverified. |
|
|
55
|
+
| Browsers | Playwright Chromium exercises selected native-dialog/reset/graphics/hydration/catalogue journeys | Emulation is not a physical device. Chromium/axe is not Firefox/WebKit/Safari or manual VoiceOver/NVDA verification. |
|
|
56
56
|
|
|
57
57
|
Release gates use one exact archive: types/lint/build, Node/component tests, native-dialog/graphics browser checks, exports/archive inspection, React matrix, optional consumers, pinned Next core/lazy-Three modes and dependency audit. Catalogue has separate required checks/affected journeys. Record checksum, actual environments and passed/failed/skipped/blocked results. Historical results are dated evidence, not a fresh pass; see [RELEASING.md](RELEASING.md).
|
|
58
58
|
|
|
@@ -70,11 +70,11 @@ Respect system reduced motion and app pause. CSS depth is core; WebGL is separat
|
|
|
70
70
|
|
|
71
71
|
Preserve native input/select/details/dialog behavior where appropriate. Future replacements require a written rationale and equivalent labels/descriptions, submission/disabled omission, validation/reset timing, keyboard/focus, modal scroll locking and cleanup, with relevant component/browser checks.
|
|
72
72
|
|
|
73
|
-
-
|
|
74
|
-
- Checkbox
|
|
73
|
+
- Actions, ExternalLink, native fields/choices, Dialog and the documented Tabs/Popover parts forward their native targets on React 18/19. Multi-control/presentation wrappers do not imply a singular input ref; see [the composition contract](COMPOSITION.md).
|
|
74
|
+
- Checkbox retains its generated ID and internal indeterminate behavior alongside the forwarded input ref; caller ARIA validity and descriptions remain composed. Switch/Slider/RadioGroup lack caller-named form contracts; serialize state deliberately.
|
|
75
75
|
- Popover/DropdownMenu use local absolute panels without portal/collision/submenu/custom-trigger contracts. Independent document listeners close both nested Popovers on one Escape: a reproduced defect.
|
|
76
76
|
- Tooltip closes on pointer leave and CSS prevents content hover: a source finding/accessibility risk requiring correction and pointer/focus verification, not a formal conformance audit. Hints must be supplemental and noninteractive.
|
|
77
|
-
- Dialog/Tabs/Popover
|
|
77
|
+
- Dialog/Tabs/Popover expose typed root and owned-part props/refs; state and ARIA wiring stay protected and caller cancellation composes with internal behavior. Arbitrary trigger replacement, portals/collision and general polymorphism remain outside this API.
|
|
78
78
|
- Disclosure is native details with defaultOpen and required className, not controlled open/onOpenChange. Controlled open state belongs to Popover/DropdownMenu.
|
|
79
79
|
- BrandMark/editorial section/heading helpers and selected navigation/print hooks retain inherited page assumptions. They remain supported legacy APIs, not universal product primitives.
|
|
80
80
|
|
package/NEXTJS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Next.js App Router integration
|
|
2
2
|
|
|
3
|
-
This guide describes **Contour 0.
|
|
3
|
+
This guide describes **Contour 0.3.0**. npm 0.1.2 does not include the client directives, native refs or relative Latin font files described here. Check npm for release availability before installing. The fixture pins Next 16.4.0 with React/React DOM 19.3.0, uses the webpack production build, and consumes an npm archive. It does not establish every Next version, Turbopack, Pages Router, scroll entries or hosting platform. A separate optional mode checks a lazy Three.js scene with SSR disabled and disposal on navigation.
|
|
4
4
|
|
|
5
5
|
In `app/layout.tsx`, load CSS once:
|
|
6
6
|
|
|
@@ -49,3 +49,5 @@ export default function SearchForm() {
|
|
|
49
49
|
Provide real submission behavior in the application. Toast consumers need ToastProvider; dialogs need application-owned open/onClose. Keep function props inside client boundaries. A server component can pass serializable props to a package client component or import pure `safeHref`; a client directive does not remove normal server prerendering.
|
|
50
50
|
|
|
51
51
|
Run `npm run test:next` to rebuild and consume the candidate archive. The fixture checks server imports/prerendering, hydration, CSS/fonts, refs, native resets, dialog dismissal/focus, toast, tabs, theme persistence and client navigation without Three.js/Lenis installed. Run `CONTOUR_NEXT_OPTIONAL=1 node scripts/test-next.mjs` for the separate lazy `/three` route; ordinary consumers still require neither optional peer. Compatibility evidence applies to the fixture's exact pair and local runtime, not deployment.
|
|
52
|
+
|
|
53
|
+
The 0.3.0 fixture also verifies Button native-ref focus and styled Next Link ordinary/canceled/middle-click navigation. Use [the composition guide](COMPOSITION.md#native-and-router-links) for the Link recipe; Button remains an action rather than a polymorphic navigation component.
|