@abuzareal/contour-ui 0.1.1 → 0.2.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 +86 -11
- package/CODE_OF_CONDUCT.md +5 -0
- package/CONTRIBUTING.md +15 -0
- package/NEXTJS.md +51 -0
- package/README.md +176 -76
- package/RELEASING.md +14 -28
- package/SECURITY.md +1 -1
- package/dist/components/hero/SceneBoundary.js +1 -0
- package/dist/components/hero/Sculpture.js +6 -4
- package/dist/components/three/ObjectStudy.js +7 -3
- package/dist/components/ui/Alert.js +1 -0
- package/dist/components/ui/BrandMark.js +1 -0
- package/dist/components/ui/Checkbox.js +1 -0
- package/dist/components/ui/CountUp.js +1 -0
- package/dist/components/ui/DataTable.js +1 -0
- package/dist/components/ui/Dialog.js +1 -0
- package/dist/components/ui/DropdownMenu.d.ts +7 -1
- package/dist/components/ui/DropdownMenu.js +62 -14
- package/dist/components/ui/FlipCard.js +1 -0
- package/dist/components/ui/Magnetic.js +1 -0
- package/dist/components/ui/Pagination.js +1 -0
- package/dist/components/ui/Popover.d.ts +4 -1
- package/dist/components/ui/Popover.js +7 -5
- package/dist/components/ui/RadioGroup.js +1 -0
- package/dist/components/ui/Reveal.js +1 -0
- package/dist/components/ui/ScrollProgress.js +1 -0
- package/dist/components/ui/SegmentedControl.js +1 -0
- package/dist/components/ui/SelectField.d.ts +11 -1
- package/dist/components/ui/SelectField.js +6 -4
- package/dist/components/ui/Slider.js +1 -0
- package/dist/components/ui/Switch.js +1 -0
- package/dist/components/ui/Tabs.d.ts +4 -1
- package/dist/components/ui/Tabs.js +12 -5
- package/dist/components/ui/Tag.js +1 -0
- package/dist/components/ui/TextArea.d.ts +8 -1
- package/dist/components/ui/TextArea.js +21 -7
- package/dist/components/ui/TextField.d.ts +10 -1
- package/dist/components/ui/TextField.js +17 -6
- package/dist/components/ui/ThemeToggle.js +1 -0
- package/dist/components/ui/TiltCard.js +1 -0
- package/dist/components/ui/ToastProvider.js +1 -0
- package/dist/components/ui/Tooltip.js +1 -0
- package/dist/fonts/dm-mono-LICENSE.txt +93 -0
- package/dist/fonts/dm-mono-latin-400-normal.woff2 +0 -0
- package/dist/fonts/dm-sans-LICENSE.txt +93 -0
- package/dist/fonts/dm-sans-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/space-grotesk-LICENSE.txt +93 -0
- package/dist/fonts/space-grotesk-latin-wght-normal.woff2 +0 -0
- package/dist/fonts-latin.css +35 -0
- package/dist/hooks/useClipboardCopy.js +1 -0
- package/dist/hooks/useControlRef.d.ts +6 -0
- package/dist/hooks/useControlRef.js +13 -0
- package/dist/hooks/useDismiss.js +1 -0
- package/dist/hooks/useFormReset.d.ts +3 -0
- package/dist/hooks/useFormReset.js +31 -0
- package/dist/hooks/useHeaderScroll.js +1 -0
- package/dist/hooks/useIdleTrigger.js +1 -0
- package/dist/hooks/useInitialHashScroll.js +1 -0
- package/dist/hooks/useMobileMenu.js +1 -0
- package/dist/hooks/useMotionPreference.js +1 -0
- package/dist/hooks/useOpenState.d.ts +2 -0
- package/dist/hooks/useOpenState.js +15 -0
- package/dist/hooks/usePrintPreparation.js +1 -0
- package/dist/hooks/useScrollSpy.js +1 -0
- package/dist/hooks/useSmoothScroll.js +1 -0
- package/dist/hooks/useTheme.js +1 -0
- package/dist/hooks/useThreeStage.d.ts +4 -0
- package/dist/hooks/useThreeStage.js +165 -128
- package/dist/hooks/useToast.js +1 -0
- package/package.json +29 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,20 +1,95 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
Changes to the published `@abuzareal/contour-ui` package. Version headings refer to package versions, not catalogue or application versions. Consumer projects pin their own version and update separately.
|
|
4
|
+
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
No changes recorded.
|
|
8
|
+
|
|
9
|
+
## 0.2.0
|
|
10
|
+
|
|
11
|
+
- TextField, TextArea and SelectField expose native refs while retaining React 18/19 compatibility. Caller-supplied aria-invalid is preserved when no component error overrides it.
|
|
12
|
+
- Uncontrolled field counters and textarea height follow native form reset, including external form association and canceled resets. A browser task waits for the native reset default action, and native input keeps counters current when an edit repeats the pre-reset value.
|
|
13
|
+
- Tabs adds activeTabId/onTabChange; Popover and DropdownMenu add open/defaultOpen/onOpenChange. Existing uncontrolled defaults remain.
|
|
14
|
+
- DropdownMenu skips disabled items, opens from ArrowUp/ArrowDown, supports label typeahead and accepts stable item ids.
|
|
15
|
+
- Three stages release resources after partial initialization failure; setup can register disposal immediately. ObjectStudy reflows paused scenes on resize instead of arranging every animation frame.
|
|
16
|
+
- Interactive modules retain client boundaries; ESM export targets also provide a default condition for framework dynamic-module resolution. A pinned packed Next.js App Router fixture checks the candidate. Latin font assets use relative packaged URLs with their licenses for webpack compatibility.
|
|
17
|
+
- Contributor setup and issue/PR templates are portable. Release checks reuse one archive across compatibility consumers and publish that verified archive without rerunning source lifecycle gates.
|
|
18
|
+
|
|
19
|
+
These changes are absent from 0.1.2. A separate Next.js fixture mode covers lazy Three.js; Lenis, alternate-bundler and wider framework support remain outside these checks.
|
|
20
|
+
|
|
21
|
+
## 0.1.2 — 2026-10-07
|
|
22
|
+
|
|
23
|
+
An optional font-loading entry and clearer setup documentation. Component APIs and runtime dependencies are unchanged from 0.1.1.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- `fonts-latin.css`: load Latin WOFF2 files for the three system typefaces. The existing `fonts.css` retains full character coverage.
|
|
28
|
+
|
|
29
|
+
### Documentation
|
|
30
|
+
|
|
31
|
+
- Clarify installation, CSS import order, public exports, optional dependencies, and upgrade guidance.
|
|
32
|
+
- Pair live examples with their source code on the documentation site. Site navigation, theme transitions, responsive layouts, and the homepage specimen were refined; these site changes do not alter package component APIs.
|
|
33
|
+
|
|
34
|
+
### Compatibility
|
|
35
|
+
|
|
36
|
+
- React 18 and 19 remain supported. Three.js and Lenis remain optional. Packed optional-entry checks cover strict TypeScript declarations and production builds.
|
|
37
|
+
|
|
38
|
+
### Upgrading from 0.1.1
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
npm install @abuzareal/contour-ui@0.1.2 --save-exact
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
No component changes are required. To use the smaller Latin font set, replace your `fonts.css` import with `@abuzareal/contour-ui/fonts-latin.css`. Keep the existing import for additional character coverage. Update your manifest and lockfile and check your application's rendered text.
|
|
45
|
+
|
|
3
46
|
## 0.1.1
|
|
4
47
|
|
|
5
|
-
|
|
48
|
+
A compatibility and interaction update for apps using React 18.3.1 or React 19.
|
|
49
|
+
|
|
50
|
+
### Compatibility
|
|
51
|
+
|
|
52
|
+
- Extend the React and React DOM peer ranges to React 19. The compatibility script checks packed consumers on 18.3.1, 19.0.0, and 19.3.0 and compiles library source against React 19 types.
|
|
53
|
+
- Preserve inert flip-card faces in both React majors, accept nullable refs in public hooks, and initialize the clipboard timer ref for React 19 types.
|
|
54
|
+
|
|
55
|
+
### Fixes
|
|
56
|
+
|
|
57
|
+
- **Dialog and drawers:** lock background scrolling while open. Nested dialogs retain the lock until the last closes; closing or unmounting restores the document's previous styles and scrollbar space. Dialog content remains scrollable on short screens.
|
|
58
|
+
- **TextField, TextArea, SelectField:** accept caller IDs and retain caller `aria-describedby` references alongside field hints and errors.
|
|
59
|
+
- **Text counters:** reflect programmatic changes to controlled values.
|
|
60
|
+
- **ThemeToggle:** use a button type that doesn't submit an enclosing form.
|
|
61
|
+
- **CountUp:** show the final value when IntersectionObserver is unavailable.
|
|
62
|
+
|
|
63
|
+
### Upgrading from 0.1.0
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
npm install @abuzareal/contour-ui@0.1.1 --save-exact
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Commit `package.json` and `package-lock.json`, then run your app's checks. Review dialogs and drawers on short screens and with nested overlays. If your app has its own background-scroll workaround, test its interaction with the library lock before removing it. Check caller-supplied field IDs/descriptions and controlled counter updates.
|
|
6
70
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- Document direct package imports and
|
|
71
|
+
React 18.3.1 remains supported; adopting React 19 isn't required. Three.js and Lenis remain optional peers behind `/three` and `/scroll`. The 3D API remains experimental. The compatibility matrix doesn't establish React Server Component support or universal framework compatibility.
|
|
72
|
+
|
|
73
|
+
### Developer tooling
|
|
74
|
+
|
|
75
|
+
- Document direct package imports and add a repeatable packed-consumer compatibility check before publication.
|
|
12
76
|
|
|
13
77
|
## 0.1.0
|
|
14
78
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
79
|
+
The first package release, extracted from the portfolio and standalone catalogue.
|
|
80
|
+
|
|
81
|
+
### Added
|
|
82
|
+
|
|
83
|
+
- Reusable React components, theme tokens, CSS, and separate optional 3D and smooth-scroll entries.
|
|
84
|
+
- Typed ESM exports with font and reset styles available as opt-in imports.
|
|
85
|
+
- Regression tests, archive-content checks, and consumer build validation.
|
|
86
|
+
|
|
87
|
+
### Interaction behavior
|
|
88
|
+
|
|
89
|
+
- Synchronize theme controls, tolerate blocked preference storage, and respect system reduced motion for theme changes.
|
|
90
|
+
- Reject executable link schemes, preserve tooltip descriptions, and make hidden flip-card faces inert.
|
|
91
|
+
- Handle empty tab lists and zero-range sliders.
|
|
92
|
+
|
|
93
|
+
### Compatibility at release
|
|
19
94
|
|
|
20
|
-
|
|
95
|
+
React 18.3.1 was the verified baseline for 0.1.0; React 19 compatibility was added in 0.1.1. The 3D API was introduced as experimental.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Code of conduct
|
|
2
|
+
|
|
3
|
+
Treat contributors and users with respect. Focus criticism on behavior and code. Harassment, discrimination, threats, sharing private information without permission and repeated personal attacks are unacceptable.
|
|
4
|
+
|
|
5
|
+
Maintainers may remove disruptive material or restrict participation to keep the project usable. Send conduct concerns privately to abuzart1999@gmail.com, including links or evidence you are comfortable sharing. Reports are handled privately where possible; do not publish other people's personal details in issues.
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Contributing to Contour UI
|
|
2
|
+
|
|
3
|
+
Contour is a styled React library. The catalogue is a separate application at [contour.abuzr.in](https://contour.abuzr.in/). Domain workflows and product scenes belong in applications; reusable controls, semantic tokens and shared browser lifecycles belong here.
|
|
4
|
+
|
|
5
|
+
Start with an issue describing the user task, current behavior and a small reproduction. For a new component, describe its stable props, native semantics, keyboard behavior, failure/empty states and why existing exports cannot compose the result. Do not include secrets or private application data. Report security concerns through [SECURITY.md](SECURITY.md).
|
|
6
|
+
|
|
7
|
+
Use Node 22.12+ or 24+, then `npm ci --ignore-scripts`. Install Chromium once with `npx playwright install chromium`. Run `npm run validate` for types, lint, build, component and browser regressions, and archive checks. Run `npm run test:compat` for packed React 18/19 consumers and source declarations; run `npm run test:next` for the pinned App Router fixture. Compatibility installs need network access. The core fixture must work without Three.js or Lenis. Optional entries have separate consumers.
|
|
8
|
+
|
|
9
|
+
Keep strict types and validate untrusted data before use. Components own presentation and nearby state; hooks own subscriptions and browser lifecycles; helpers own pure calculations. Use native controls, linked labels/errors, visible focus and explicit button types. Respect reduced motion. Release every listener, timer, observer and GPU resource on unmount and partial initialization failure. Use `onDispose` immediately for resources allocated in a custom stage setup.
|
|
10
|
+
|
|
11
|
+
For a fix, add a regression that fails for the previous behavior. Use component tests for interaction contracts and real browsers for native dialogs, event timing, layout and GPU integration. Check narrow layouts, long content and both themes. Do not replace browser evidence with jsdom when a browser owns the behavior. Avoid duplicate tests that only restate implementation.
|
|
12
|
+
|
|
13
|
+
Update the README and Unreleased changelog when public behavior changes. Keep documented APIs consistent with the archive. New APIs need a migration/usage example. Generic CSS class names and the legacy `aa-theme` storage key are existing compatibility constraints; do not rename them silently.
|
|
14
|
+
|
|
15
|
+
A pull request should name the concrete behavior change, why it helps, which checks passed and any remaining limitation. Review public exports, packed files and dependency changes. No publish, deploy or merge is implied by opening a pull request. Consumers adopt an exact published release with a lockfile update; local source edits do not update them.
|
package/NEXTJS.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Next.js App Router integration
|
|
2
|
+
|
|
3
|
+
This guide describes **Contour 0.2.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
|
+
|
|
5
|
+
In `app/layout.tsx`, load CSS once:
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import "@abuzareal/contour-ui/styles.css";
|
|
9
|
+
import "@abuzareal/contour-ui/fonts-latin.css"; // Optional; use your own fonts if preferred.
|
|
10
|
+
import { themeBootstrapScript } from "@abuzareal/contour-ui/theme-bootstrap";
|
|
11
|
+
|
|
12
|
+
export default function Layout({ children }: { children: React.ReactNode }) {
|
|
13
|
+
return (
|
|
14
|
+
<html lang="en" suppressHydrationWarning>
|
|
15
|
+
<head>
|
|
16
|
+
<script dangerouslySetInnerHTML={{ __html: themeBootstrapScript }} />
|
|
17
|
+
</head>
|
|
18
|
+
<body>{children}</body>
|
|
19
|
+
</html>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The narrowly scoped hydration suppression handles the root theme attribute changed before hydration; do not use it to hide arbitrary mismatch errors. A restrictive CSP requires a nonce or hash for the bootstrap script. `reset.css` is optional and global; decide whether your host reset already supplies equivalent defaults, visible focus and a `[hidden]` rule.
|
|
25
|
+
|
|
26
|
+
A client component owns event handlers, state and providers:
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
"use client";
|
|
30
|
+
import { useState } from "react";
|
|
31
|
+
import { TextField, Button } from "@abuzareal/contour-ui";
|
|
32
|
+
|
|
33
|
+
export default function SearchForm() {
|
|
34
|
+
const [query, setQuery] = useState("");
|
|
35
|
+
return (
|
|
36
|
+
<form>
|
|
37
|
+
<TextField
|
|
38
|
+
label="Search"
|
|
39
|
+
name="query"
|
|
40
|
+
value={query}
|
|
41
|
+
onChange={(event) => setQuery(event.target.value)}
|
|
42
|
+
/>
|
|
43
|
+
<Button type="submit">Search</Button>
|
|
44
|
+
</form>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
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
|
+
|
|
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.
|
package/README.md
CHANGED
|
@@ -1,139 +1,239 @@
|
|
|
1
1
|
# Contour UI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React components and CSS tokens for interfaces with paper and ink surfaces, a lime accent, and light and dark themes. Contour started as the shared UI in a personal portfolio; it now supplies components to the portfolio, its catalogue, Atmos, and Orbit.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Use it when you want this visual style with typed controls, feedback, navigation, and motion. Applications own their layouts, data, validation, and workflows. Three.js scenes and smooth scrolling are separate, optional entries.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
[Live catalogue](https://contour.abuzr.in/) · [npm package](https://www.npmjs.com/package/@abuzareal/contour-ui) · [Maintainer](https://github.com/abuzareal)
|
|
8
|
+
|
|
9
|
+
This source checkout targets **0.2.0**. Package publication and application upgrades are separate; check the npm registry for release availability and each application's manifest for its installed version. Catalogue examples include application patterns as well as package components. Check the exports below before treating an example as an importable component.
|
|
8
10
|
|
|
9
11
|
## Install
|
|
10
12
|
|
|
13
|
+
Use an existing React application with a bundler that resolves package CSS imports. Contour ships ESM and TypeScript declarations. The package declares Node.js 22 or newer; repository tooling uses Vite 8, which needs Node.js 22.12+ or 24+.
|
|
14
|
+
|
|
11
15
|
```sh
|
|
12
|
-
npm install @abuzareal/contour-ui
|
|
13
|
-
# Your application must supply matching React and React DOM versions:
|
|
14
|
-
# React 18.3.1 or React 19.
|
|
16
|
+
npm install @abuzareal/contour-ui@0.2.0 --save-exact
|
|
15
17
|
```
|
|
16
18
|
|
|
19
|
+
Your app must provide matching `react` and `react-dom` versions: **18.3.1 or React 19**. The compatibility script covers 18.3.1, 19.0.0, and 19.3.0; it doesn't establish support for every framework or React Server Component setup. Put interactive components inside a client boundary when your framework requires one.
|
|
20
|
+
|
|
21
|
+
## First component
|
|
22
|
+
|
|
23
|
+
Import the styles once in your application entry. Import components directly from the package; no generated files or local re-exports are needed.
|
|
24
|
+
|
|
17
25
|
```tsx
|
|
18
|
-
import
|
|
19
|
-
import "@abuzareal/contour-ui/
|
|
26
|
+
import { useState } from "react";
|
|
27
|
+
import "@abuzareal/contour-ui/reset.css";
|
|
28
|
+
import "@abuzareal/contour-ui/fonts.css";
|
|
20
29
|
import "@abuzareal/contour-ui/styles.css";
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
export function App() {
|
|
30
|
+
import { Button, TextField, ThemeToggle } from "@abuzareal/contour-ui";
|
|
31
|
+
|
|
32
|
+
export default function App() {
|
|
33
|
+
const [name, setName] = useState("");
|
|
34
|
+
const [savedName, setSavedName] = useState("");
|
|
35
|
+
|
|
29
36
|
return (
|
|
30
|
-
<
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
<main data-motion="off">
|
|
38
|
+
<ThemeToggle animate={false} />
|
|
39
|
+
<TextField
|
|
40
|
+
label="Name"
|
|
41
|
+
value={name}
|
|
42
|
+
onChange={(event) => setName(event.target.value)}
|
|
43
|
+
/>
|
|
44
|
+
<Button variant="accent" onClick={() => setSavedName(name)}>
|
|
45
|
+
Save locally
|
|
46
|
+
</Button>
|
|
47
|
+
<p role="status">{savedName && `Saved for this session: ${savedName}`}</p>
|
|
48
|
+
</main>
|
|
37
49
|
);
|
|
38
50
|
}
|
|
39
51
|
```
|
|
40
52
|
|
|
41
|
-
|
|
53
|
+
This example keeps the value in React state until the component unmounts. To persist it or send it to a server, add that behavior in your app.
|
|
42
54
|
|
|
43
|
-
|
|
55
|
+
`Button` and ordinary controls need no provider. Toasts require `ToastProvider` around components that call `useToast()`.
|
|
44
56
|
|
|
45
|
-
|
|
57
|
+
### Optional Latin fonts
|
|
58
|
+
|
|
59
|
+
`fonts-latin.css`, added in 0.1.2, keeps DM Sans, Space Grotesk, and DM Mono while loading only Latin WOFF2 files. Use it instead of `fonts.css` for Latin text; keep `fonts.css` when the additional character sets are needed. Existing font imports continue to work.
|
|
46
60
|
|
|
47
61
|
```tsx
|
|
48
|
-
import
|
|
62
|
+
import "@abuzareal/contour-ui/fonts-latin.css";
|
|
63
|
+
```
|
|
49
64
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
65
|
+
## Styles and customization
|
|
66
|
+
|
|
67
|
+
| Import | Purpose |
|
|
68
|
+
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
69
|
+
| `styles.css` | Required for the supplied appearance: tokens, component styles, and utilities. |
|
|
70
|
+
| `reset.css` | Optional global reset. Omit it if your app already has equivalent native-control defaults, border-box sizing, focus styles, and a `[hidden]` rule. |
|
|
71
|
+
| `fonts-latin.css` | Optional Latin-only WOFF2 fonts, available since 0.1.2. Use instead of `fonts.css`, not alongside it. |
|
|
72
|
+
| `fonts.css` | Optional self-hosted Space Grotesk, DM Sans, and DM Mono via Fontsource. Your bundler emits the font assets. |
|
|
73
|
+
| `tokens.css` | Tokens alone, for an app that supplies its own component styles. Already included by `styles.css`. |
|
|
74
|
+
|
|
75
|
+
Override semantic tokens after importing the styles:
|
|
76
|
+
|
|
77
|
+
```css
|
|
78
|
+
:root {
|
|
79
|
+
--font-sans: system-ui, sans-serif;
|
|
80
|
+
--font-display: system-ui, sans-serif;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
:root[data-theme="dark"] {
|
|
84
|
+
--bg: #111411;
|
|
85
|
+
--fg: #edf0e8;
|
|
56
86
|
}
|
|
57
87
|
```
|
|
58
88
|
|
|
59
|
-
|
|
89
|
+
Styles use global component classes and document-level theme tokens. Scope your application's classes to avoid collisions with names such as `.card` and `.button`. Contour's CSS isn't isolated with Shadow DOM or CSS Modules.
|
|
90
|
+
|
|
91
|
+
The three Fontsource packages and `lucide-react` are direct dependencies. Omitting `fonts.css` keeps its font assets out of your application's bundle; the font packages still install. React and React DOM are peers. Three.js and Lenis are optional peers. Development tools aren't installed as Contour's consumer dependencies; tests and tooling are excluded from the npm archive.
|
|
92
|
+
|
|
93
|
+
## Theme and motion
|
|
94
|
+
|
|
95
|
+
Set `data-theme="light"` or `"dark"` on `<html>`. `ThemeToggle` and `useTheme(animate)` read the same document state. The hook returns `{ theme, toggle }`; call `toggle(buttonElement)` from a user action. Preferences use the `aa-theme` local-storage key and are local to the site's origin.
|
|
96
|
+
|
|
97
|
+
For theme selection before first paint, inject `themeBootstrapScript` from `@abuzareal/contour-ui/theme-bootstrap` into the HTML head at build time. The exported string has no user interpolation. With a strict Content Security Policy, authorize that script with a nonce or hash.
|
|
60
98
|
|
|
61
|
-
|
|
99
|
+
`useMotionPreference()` returns whether the user prefers reduced motion. Combine it with your app's pause state, set `data-motion="off"` on the app wrapper when needed, and pass `motionEnabled` to scenes and `animate` to theme controls. System reduced motion also disables the theme reveal.
|
|
62
100
|
|
|
63
|
-
|
|
101
|
+
## Components
|
|
64
102
|
|
|
65
|
-
|
|
103
|
+
| Area | Public exports |
|
|
104
|
+
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
105
|
+
| Actions | `Button`, `IconButton`, `ExternalLink`, `SegmentedControl`, `ThemeToggle` |
|
|
106
|
+
| Forms | `Field`, `TextField`, `TextArea`, `SelectField`, `Checkbox`, `RadioGroup`, `Switch`, `Slider` |
|
|
107
|
+
| Navigation | `Tabs`, `Breadcrumbs`, `Pagination`, `Stepper`, `DropdownMenu` |
|
|
108
|
+
| Data and layout | `Avatar`, `Badge`, `Card`, `DataTable`, `Disclosure`, `Divider`, `EmptyState`, `Kbd`, `SectionLabel`, `Stat`, `Tag`, `Timeline` |
|
|
109
|
+
| Feedback | `Alert`, `ProgressBar`, `Skeleton`, `Spinner`, `ToastProvider`, `useToast` |
|
|
110
|
+
| Motion and identity | `BrandMark`, `CountUp`, `DepthButton`, `FlipCard`, `Magnetic`, `Marquee`, `Reveal`, `RevealHeading`, `ScrollProgress`, `TiltCard`, `GitHubIcon`, `LinkedInIcon` |
|
|
111
|
+
| Overlays and scene fallback | `Dialog`, `Popover`, `Tooltip`, `SceneBoundary` |
|
|
66
112
|
|
|
67
|
-
|
|
113
|
+
The root entry also exports theme, motion, clipboard, dismissal, navigation, and browser-lifecycle hooks plus utility functions. Check `src/index.ts` in a source checkout or `dist/index.d.ts` in the installed package for the complete API. Component declarations describe their props.
|
|
68
114
|
|
|
69
|
-
|
|
115
|
+
Use visible field labels, accessible names for icon buttons, and text alongside status colors. `DataTable` sorts scalar string/number values; `Pagination` expects a positive integer page count and a page within that range. Flip cards use a button and make the hidden face inert.
|
|
70
116
|
|
|
71
|
-
|
|
117
|
+
Props support varies between components. Don't assume every export forwards DOM refs, accepts arbitrary native attributes, supports router `asChild` composition, or integrates with form libraries through refs. Verify the relevant type before adopting it.
|
|
72
118
|
|
|
73
|
-
|
|
119
|
+
## Optional scenes and scrolling
|
|
74
120
|
|
|
75
|
-
|
|
121
|
+
Core imports don't load Three.js or Lenis. Install either only if you use its entry:
|
|
76
122
|
|
|
77
123
|
```sh
|
|
78
|
-
npm install three@^0.186.1
|
|
79
|
-
npm install lenis@^1.3.26
|
|
124
|
+
npm install three@^0.186.1 # @abuzareal/contour-ui/three
|
|
125
|
+
npm install lenis@^1.3.26 # @abuzareal/contour-ui/scroll
|
|
126
|
+
# TypeScript consumers of /three also need the matching declarations:
|
|
127
|
+
npm install -D @types/three@^0.186.0
|
|
80
128
|
```
|
|
81
129
|
|
|
130
|
+
`/scroll` exports `useSmoothScroll`. `/three` exports `Sculpture`, `ObjectStudy`, `useThreeStage`, geometry/material helpers, and associated types. CSS depth controls such as `TiltCard` and `FlipCard` are core exports and don't require Three.js.
|
|
131
|
+
|
|
82
132
|
```tsx
|
|
83
133
|
import { lazy, Suspense } from "react";
|
|
134
|
+
import { SceneBoundary } from "@abuzareal/contour-ui";
|
|
135
|
+
|
|
84
136
|
const Sculpture = lazy(() =>
|
|
85
137
|
import("@abuzareal/contour-ui/three").then((module) => ({
|
|
86
138
|
default: module.Sculpture,
|
|
87
139
|
})),
|
|
88
140
|
);
|
|
89
141
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
142
|
+
export function Preview() {
|
|
143
|
+
return (
|
|
144
|
+
<div>
|
|
145
|
+
<p>Three interlocking links, shown in a WebGL preview when available.</p>
|
|
146
|
+
<SceneBoundary>
|
|
147
|
+
<Suspense fallback={<p>Loading preview…</p>}>
|
|
148
|
+
<Sculpture motionEnabled={false} />
|
|
149
|
+
</Suspense>
|
|
150
|
+
</SceneBoundary>
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
94
154
|
```
|
|
95
155
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
## Components and typed APIs
|
|
156
|
+
Give scenes a sized container and a usable static fallback. `SceneBoundary` hides a scene on failure; it doesn't supply fallback content, so keep that content outside the boundary. The 3D API is experimental. Stage setup functions are captured on mount; remount a scene when its setup configuration changes. WebGL availability is a runtime requirement for scenes.
|
|
99
157
|
|
|
100
|
-
|
|
158
|
+
## Release notes and upgrades
|
|
101
159
|
|
|
102
|
-
|
|
103
|
-
- Forms: `Field`, `TextField`, `TextArea`, `SelectField`, `Checkbox`, `RadioGroup`, `Switch`, `Slider`.
|
|
104
|
-
- Navigation: `Tabs`, `Breadcrumbs`, `Pagination`, `Stepper`, `DropdownMenu`.
|
|
105
|
-
- Data and layout: `Avatar`, `Badge`, `Card`, `DataTable`, `Disclosure`, `Divider`, `EmptyState`, `Kbd`, `SectionLabel`, `Stat`, `Tag`, `Timeline`.
|
|
106
|
-
- Feedback: `Alert`, `ProgressBar`, `Skeleton`, `Spinner`, `ToastProvider`, `useToast`.
|
|
107
|
-
- Motion and signature: `BrandMark`, `CountUp`, `DepthButton`, `FlipCard`, `Magnetic`, `Marquee`, `Reveal`, `RevealHeading`, `ScrollProgress`, `TiltCard`, `GitHubIcon`, `LinkedInIcon`.
|
|
108
|
-
- Overlays: `Dialog`, `Popover`, `Tooltip`, `SceneBoundary`.
|
|
109
|
-
|
|
110
|
-
`Dialog` locks document scrolling while open, including drawer placements. Its content remains scrollable on short screens. Nested dialogs share the lock; closing or unmounting the last restores the document's previous styles. This behavior is included in version 0.1.1.
|
|
111
|
-
|
|
112
|
-
Use visible labels for fields, concise action labels for buttons, accessible labels for icon-only controls, and text alongside status colors. Sortable tables expect scalar string/number row values. Pagination expects a positive integer page count and a page in that range. Flip cards switch through their button; the hidden side is inert.
|
|
113
|
-
|
|
114
|
-
Global colors, fonts, radii, duration, and spacing variables are in `tokens.css`. Override semantic tokens rather than copying components into your app. `styles.css` includes tokens and component styles; `fonts.css` and `reset.css` are separately opt-in. Do not import catalogue or portfolio CSS into other apps.
|
|
115
|
-
|
|
116
|
-
## Independent repositories and updates
|
|
117
|
-
|
|
118
|
-
This repository contains only the library. The catalogue and portfolio remain separate repositories and consume the same published npm version. During first-release validation, a packed archive can be installed locally; replace the archive dependency with the registry version before committing consumer manifests.
|
|
160
|
+
**0.2.0** adds native field refs, controlled tabs and disclosure state, menu keyboard improvements, native reset synchronization, Three.js lifecycle fixes and a tested Next.js App Router integration. Existing uncontrolled defaults and React 18/19 support remain. `fonts-latin.css`, introduced in 0.1.2, now uses packaged relative font URLs for webpack resolution. `CHANGELOG.md` is included in the package archive and [npm's Code view](https://www.npmjs.com/package/@abuzareal/contour-ui?activeTab=code); it contains the upgrade details for each version.
|
|
119
161
|
|
|
120
162
|
```sh
|
|
121
|
-
npm install @abuzareal/contour-ui@0.
|
|
163
|
+
npm install @abuzareal/contour-ui@0.2.0 --save-exact
|
|
122
164
|
```
|
|
123
165
|
|
|
124
|
-
Commit both
|
|
166
|
+
Commit your manifest and lockfile, run your application's checks, and review both themes, dialogs, forms, and motion before deploying. Version 0.1.1 includes a document scroll lock for dialogs and drawers. Review any app-level lock before removing it, including nested dialogs and short screens. Package publication doesn't update deployed consumers automatically.
|
|
167
|
+
|
|
168
|
+
The package and [source repository](https://github.com/abuzareal/contour-ui) are public under MIT. Report reproducible defects in the [issue tracker](https://github.com/abuzareal/contour-ui/issues); use [SECURITY.md](SECURITY.md) for private vulnerability reporting. Public users can inspect code, CSS, declarations and release documents through GitHub or npm.
|
|
169
|
+
|
|
170
|
+
## Develop this library
|
|
125
171
|
|
|
126
|
-
|
|
172
|
+
From a source checkout:
|
|
127
173
|
|
|
128
174
|
```sh
|
|
129
175
|
npm ci --ignore-scripts
|
|
130
|
-
npx playwright install chromium
|
|
176
|
+
npx playwright install chromium
|
|
131
177
|
npm run validate
|
|
132
|
-
npm run test:compat
|
|
178
|
+
npm run test:compat
|
|
133
179
|
npm audit
|
|
134
180
|
npm pack --dry-run
|
|
135
181
|
```
|
|
136
182
|
|
|
137
|
-
|
|
183
|
+
`validate` checks types, lint, builds, Node/component tests, dialog and Three.js browser behavior, and archive contents. `test:compat` builds packed React 18/19 consumers and compiles the source with React 19 types. `test:consumer` checks one packed consumer, with optional peers absent. `test:optional` verifies strict `/three` and `/scroll` declarations and builds their imports with explicit peers and Three.js types. The compatibility gate runs both checks and verifies the Latin font assets. Compatibility runs need registry access for clean installs.
|
|
184
|
+
|
|
185
|
+
`src/` owns components, hooks, helpers, and public entries. `styles/` owns CSS. `tests/` owns regressions and browser checks; `scripts/` owns builds and package/consumer checks. `dist/` is generated. `RELEASING.md` in the source checkout and package archive covers validation and publication.
|
|
186
|
+
|
|
187
|
+
MIT licensed. Fontsource fonts and dependencies retain their own licenses.
|
|
188
|
+
|
|
189
|
+
## APIs added in 0.2.0
|
|
190
|
+
|
|
191
|
+
Version 0.2.0 adds native refs to TextField, TextArea and SelectField; synchronizes uncontrolled counters and auto-resize after native form reset; adds controlled Tabs (`activeTabId`, `onTabChange`) and disclosures (`open`, `defaultOpen`, `onOpenChange`); and adds disabled menu items, stable item ids and typeahead. These additions are absent from 0.1.2. Switching a field/disclosure between controlled and uncontrolled ownership during its lifetime is unsupported.
|
|
192
|
+
|
|
193
|
+
Existing uncontrolled calls remain valid. Use a native ref to focus a field or integrate a form controller; controlled tabs and disclosures require the caller to update the selected id or open state:
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
import { useRef, useState } from "react";
|
|
197
|
+
import { Button, Popover, Tabs, TextField } from "@abuzareal/contour-ui";
|
|
198
|
+
|
|
199
|
+
export function AccountControls() {
|
|
200
|
+
const input = useRef<HTMLInputElement>(null);
|
|
201
|
+
const [activeTabId, setActiveTabId] = useState("details");
|
|
202
|
+
const [open, setOpen] = useState(false);
|
|
203
|
+
|
|
204
|
+
return (
|
|
205
|
+
<>
|
|
206
|
+
<TextField ref={input} label="Name" name="name" />
|
|
207
|
+
<Button onClick={() => input.current?.focus()}>Focus name</Button>
|
|
208
|
+
<Tabs
|
|
209
|
+
label="Account"
|
|
210
|
+
activeTabId={activeTabId}
|
|
211
|
+
onTabChange={setActiveTabId}
|
|
212
|
+
tabs={[
|
|
213
|
+
{ id: "details", label: "Details", content: <p>Account details</p> },
|
|
214
|
+
{
|
|
215
|
+
id: "settings",
|
|
216
|
+
label: "Settings",
|
|
217
|
+
content: <p>Account settings</p>,
|
|
218
|
+
},
|
|
219
|
+
]}
|
|
220
|
+
/>
|
|
221
|
+
<Popover trigger="Help" title="Help" open={open} onOpenChange={setOpen}>
|
|
222
|
+
<p>Account help</p>
|
|
223
|
+
</Popover>
|
|
224
|
+
</>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Interactive module boundaries retain `use client` in the built package. The pinned Next.js App Router consumer checks the packed 0.2.0 archive. Follow [Next.js setup](NEXTJS.md) for the pinned Next.js 16.4.0 / React 19.3.0 pair. Pure URL/theme-bootstrap helpers remain usable from the server. A separate fixture mode checks lazy optional Three.js loading and navigation cleanup. Lenis integration, other Next versions and alternate bundlers remain separate compatibility questions.
|
|
230
|
+
|
|
231
|
+
Custom `useThreeStage` setup can register disposal through `context.onDispose?.(cleanup)` immediately after acquiring each resource, including resources allocated before setup returns. Registered resources are released on failed initialization and unmount; avoid also disposing the same resource in `StageScene.dispose`. A stage may implement `resize()` to reflow after aspect changes while motion is paused.
|
|
232
|
+
|
|
233
|
+
## Compatibility boundaries
|
|
234
|
+
|
|
235
|
+
Styles are global, not a scoped CSS engine. Existing class names such as `.field` and `.menu` can collide with host CSS. Import Contour styles before deliberate application overrides; keep application selectors area-prefixed. The optional reset and fonts affect the document. Tokens use root `data-theme`; `useTheme` retains the legacy `aa-theme` local-storage key for compatibility. A multi-library page must decide which system owns its root theme.
|
|
236
|
+
|
|
237
|
+
Menus and popovers remain local panels without portals or collision detection; use them where the container can accommodate their content. No submenu or arbitrary trigger-composition contract is claimed. BrandMark and page/editorial helpers preserve existing identity and layout assumptions; they are optional exports rather than universal primitives. Do not copy private internals or deep-import them.
|
|
138
238
|
|
|
139
|
-
|
|
239
|
+
For contribution setup and review expectations, see [CONTRIBUTING.md](CONTRIBUTING.md). Contour currently provides React components; no Vue, Svelte or Angular adapter is included.
|
package/RELEASING.md
CHANGED
|
@@ -1,37 +1,23 @@
|
|
|
1
1
|
# Releasing Contour UI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Source is public at [github.com/abuzareal/contour-ui](https://github.com/abuzareal/contour-ui); the package is public under MIT. Query npm for the latest published version; the source manifest does not prove publication. Source changes are not delivered to consumers until publication and an exact dependency/lockfile update. The npm README changes only when a new version is published; do not overwrite or unpublish an existing version to edit it.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
2. Run `npm ci --ignore-scripts`, install Chromium with `npx playwright install chromium` on a new machine, then run `npm run validate`, `npm run test:compat`, and `npm audit`. Review every advisory; do not release with unexplained audit findings.
|
|
7
|
-
3. Test the actual packed archive in clean React 18 and React 19 consumers without optional Three.js or Lenis, and in the catalogue and portfolio with their full browser suites.
|
|
8
|
-
4. Check `npm pack --dry-run --json` and licenses. No private data, credentials, unrelated assets, maps, or app content may enter the archive.
|
|
9
|
-
5. Push the reviewed source and release commit to the independent GitHub repository. The owner currently keeps this repository private; the npm distribution remains public under MIT.
|
|
10
|
-
6. Set `private` to `false` only for an approved, validated release. Publish from the verified owner account: `npm publish --access public`. Complete npm's required authentication/2FA personally.
|
|
11
|
-
7. Verify registry metadata, install the exact registry version in both consumers, rerun checks, and commit the manifest/lockfile changes. Do not commit temporary filesystem/archive dependencies.
|
|
12
|
-
8. Tag the matching package version and record the release. Every future release requires a new version; npm versions cannot be overwritten.
|
|
5
|
+
Before selecting a release version, review public API and visual changes, types, dependencies, licenses, changelog and compatibility limits. Use patch for compatible fixes, minor for additions, and document every incompatible 0.x change explicitly. README examples must match the archive. Record publication dates only after verifying the registry. A local check is not deployment evidence.
|
|
13
6
|
|
|
14
|
-
##
|
|
7
|
+
## Verify and publish one archive
|
|
15
8
|
|
|
16
|
-
|
|
9
|
+
1. Obtain maintainer authorization to publish; validation alone is not authorization.
|
|
10
|
+
2. Select an unused registry version and update package.json, its lockfile and release notes. Never reuse an existing registry version; a locally verified archive does not make it publishable under an already released version. Use a reviewed release commit on main and a matching `vVERSION` tag. The hosted workflow checks the tag version and that its source is an ancestor of origin/main. It cannot publish from a pull request.
|
|
11
|
+
3. Run `npm ci --ignore-scripts`; install Chromium once with `npx playwright install chromium`. Use Node 22.12+ or 24+.
|
|
12
|
+
4. Set `CONTOUR_RELEASE_DIR` to an existing temporary directory and run `npm run verify:release`. It runs validate once, packs one archive, tests that archive in clean React 18/19 core and optional consumers plus the pinned Next App Router fixture, audits dependencies, and writes `verified-archive.json` with its SHA-256. Core consumers must have no Three.js or Lenis installed. Review failures and advisories instead of bypassing gates.
|
|
13
|
+
5. Inspect the archive contents, verify its checksum against `verified-archive.json`, and publish that exact path using `npm publish /absolute/path/to/archive.tgz --ignore-scripts --access public`. `--ignore-scripts` is appropriate here because that exact archive has already passed the release gates; it is not permission to publish an unchecked package. Source `npm publish` retains a prepublishOnly guard. The hosted workflow verifies the checksum again and publishes the archive with provenance, avoiding duplicate source gates.
|
|
14
|
+
6. The owner completes npm authentication/2FA personally. Never put tokens, OTPs or credentials in this repository or logs. Prefer configured trusted publishing and short-lived identity over persistent tokens.
|
|
15
|
+
7. Verify registry version, tarball/integrity and publication date; install that exact registry version in each affected consumer, commit its manifest/lockfile, and run its required checks before deployment. Do not substitute a local link or source copy for release validation.
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
The tag workflow is configured for a public repository on main. Configuration does not prove that its npm trusted-publisher relationship is active; check npm/GitHub settings before relying on it. Publication requires authorization even when CI passes.
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
npm trust github @abuzareal/contour-ui --file release.yml --repo abuzareal/contour-ui --allow-publish
|
|
22
|
-
npm trust list @abuzareal/contour-ui
|
|
23
|
-
```
|
|
19
|
+
## Validation cost
|
|
24
20
|
|
|
25
|
-
|
|
21
|
+
Browser checks remain for native dialog, reset timing and graphics lifecycle behavior. jsdom does not reproduce all browser default actions. Compatibility checks reuse one archive and avoid another library build when `CONTOUR_ARCHIVE` is provided. Documentation-only diffs retain types, lint, build, archive checks and audit; runtime diffs keep component, browser and complete compatibility checks. Changes to components, hooks, entries, styles, build/package configuration or the Next fixture also run the pinned Next check. Unknown history falls back to complete gates. No measured CI speedup is claimed until the updated workflow has run.
|
|
26
22
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Increment PATCH for compatible fixes, MINOR for compatible additions, and MAJOR for incompatible APIs or substantial incompatible default behavior. While the package is at 0.x, document every change carefully and keep consumers pinned to exact versions. Review visual changes in consuming apps even when the API is compatible.
|
|
30
|
-
|
|
31
|
-
## Dependency updates and cost
|
|
32
|
-
|
|
33
|
-
Dependabot is selected because it is integrated with GitHub and requires no extra hosted service or bot account. It proposes weekly npm and action updates; the library is not auto-merged. npm distribution is public. Standard-hosted Actions run only if the repository is public; checks run locally while it stays private. Do not enable paid runners, private npm distribution, or paid security subscriptions as part of this setup.
|
|
34
|
-
|
|
35
|
-
The validation workflows deliberately skip private repositories. If a consumer stays private, run its checks locally before merging dependency updates. Enabling private-repository Actions later requires reviewing that account's included usage and spending settings first.
|
|
36
|
-
|
|
37
|
-
Consumers install with `npm install @abuzareal/contour-ui@VERSION --save-exact`, commit their lockfile, run their checks, and redeploy. Keep the three Git repositories independent.
|
|
23
|
+
Dependabot proposes dependency/action changes for review; changes are not auto-merged. Actions use pinned revisions and minimal permissions. Do not enable paid runners or private distribution as part of a release. Contributor and application checks remain separate from registry/deployment verification.
|
package/SECURITY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Security
|
|
2
2
|
|
|
3
|
-
Report suspected vulnerabilities privately
|
|
3
|
+
Report suspected vulnerabilities privately to abuzart1999@gmail.com. Include the affected version and a minimal reproduction without credentials or unrelated personal data. If GitHub private vulnerability reporting is available on this repository, it is another private channel. Do not post exploit details or credentials in a public issue.
|
|
4
4
|
|
|
5
5
|
The latest published release is the supported version. Dependency audits identify known reported vulnerabilities, not all possible defects. No claim of complete security or formal accessibility certification is made.
|
|
6
6
|
|