aeon-ui-engine 1.0.1 → 1.0.3
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 +51 -0
- package/CONSUMER.md +86 -0
- package/INTEGRATION.md +2 -0
- package/README.md +15 -4
- package/package.json +17 -1
- package/packages/core/dist/index.d.ts +257 -0
- package/packages/core/dist/index.js +378 -0
- package/packages/core/node_modules/.bin/esbuild +21 -0
- package/packages/core/node_modules/.bin/jiti +21 -0
- package/packages/core/node_modules/.bin/tsc +21 -0
- package/packages/core/node_modules/.bin/tsserver +21 -0
- package/packages/core/node_modules/.bin/tsup +21 -0
- package/packages/core/node_modules/.bin/tsup-node +21 -0
- package/packages/core/node_modules/.bin/tsx +21 -0
- package/packages/core/node_modules/.bin/vite +21 -0
- package/packages/core/node_modules/.bin/vitest +21 -0
- package/packages/core/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/packages/geometry/dist/index.d.ts +50 -0
- package/packages/geometry/dist/index.js +88 -0
- package/packages/geometry/node_modules/.bin/esbuild +21 -0
- package/packages/geometry/node_modules/.bin/jiti +21 -0
- package/packages/geometry/node_modules/.bin/tsc +21 -0
- package/packages/geometry/node_modules/.bin/tsserver +21 -0
- package/packages/geometry/node_modules/.bin/tsup +21 -0
- package/packages/geometry/node_modules/.bin/tsup-node +21 -0
- package/packages/geometry/node_modules/.bin/tsx +21 -0
- package/packages/panda/src/recipes/button.ts +4 -4
- package/packages/panda/src/recipes/combobox.ts +1 -1
- package/packages/panda/src/recipes/select.ts +1 -1
- package/packages/panda/src/theme/tokens.ts +5 -0
- package/packages/panda/styled-system/styles.css +10 -6
- package/packages/panda/styled-system/tokens/index.mjs +32 -0
- package/packages/panda/styled-system/tokens/tokens.d.ts +2 -2
- package/packages/primitives/dist/chunk-5S4DIRL4.js +42 -0
- package/packages/primitives/dist/chunk-H4KOEPYC.js +52 -0
- package/packages/primitives/dist/chunk-HONROWUG.js +41 -0
- package/packages/primitives/dist/chunk-J6JHKMVM.js +90 -0
- package/packages/primitives/dist/chunk-JBAG5E6W.js +46 -0
- package/packages/primitives/dist/chunk-LVNCD5F2.js +43 -0
- package/packages/primitives/dist/chunk-PB3LFHXU.js +72 -0
- package/packages/primitives/dist/chunk-SZV6KGXN.js +49 -0
- package/packages/primitives/dist/chunk-WVICCOQH.js +56 -0
- package/packages/primitives/dist/chunk-ZMGRX64G.js +74 -0
- package/packages/primitives/dist/index.d.ts +11 -0
- package/packages/primitives/dist/index.js +60 -0
- package/packages/primitives/dist/machines/accordion.d.ts +34 -0
- package/packages/primitives/dist/machines/accordion.js +6 -0
- package/packages/primitives/dist/machines/async.d.ts +111 -0
- package/packages/primitives/dist/machines/async.js +12 -0
- package/packages/primitives/dist/machines/button.d.ts +45 -0
- package/packages/primitives/dist/machines/button.js +8 -0
- package/packages/primitives/dist/machines/dialog.d.ts +57 -0
- package/packages/primitives/dist/machines/dialog.js +6 -0
- package/packages/primitives/dist/machines/field.d.ts +72 -0
- package/packages/primitives/dist/machines/field.js +12 -0
- package/packages/primitives/dist/machines/popover.d.ts +54 -0
- package/packages/primitives/dist/machines/popover.js +6 -0
- package/packages/primitives/dist/machines/slider.d.ts +71 -0
- package/packages/primitives/dist/machines/slider.js +10 -0
- package/packages/primitives/dist/machines/tabs.d.ts +39 -0
- package/packages/primitives/dist/machines/tabs.js +6 -0
- package/packages/primitives/dist/machines/toast.d.ts +31 -0
- package/packages/primitives/dist/machines/toast.js +6 -0
- package/packages/primitives/dist/machines/toggle.d.ts +54 -0
- package/packages/primitives/dist/machines/toggle.js +6 -0
- package/packages/primitives/node_modules/.bin/jiti +21 -0
- package/packages/primitives/node_modules/.bin/tsc +21 -0
- package/packages/primitives/node_modules/.bin/tsserver +21 -0
- package/packages/primitives/node_modules/.bin/tsup +21 -0
- package/packages/primitives/node_modules/.bin/tsup-node +21 -0
- package/packages/primitives/node_modules/.bin/tsx +21 -0
- package/packages/primitives/node_modules/.bin/vitest +21 -0
- package/packages/primitives/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/packages/react/dist/index.d.ts +564 -0
- package/packages/react/dist/index.js +3461 -0
- package/packages/react/node_modules/.bin/jiti +21 -0
- package/packages/react/node_modules/.bin/tsc +21 -0
- package/packages/react/node_modules/.bin/tsserver +21 -0
- package/packages/react/node_modules/.bin/tsup +21 -0
- package/packages/react/node_modules/.bin/tsup-node +21 -0
- package/packages/react/node_modules/.bin/tsx +21 -0
- package/packages/react/node_modules/.bin/vitest +21 -0
- package/packages/react/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/packages/react-scene/dist/index.d.ts +121 -0
- package/packages/react-scene/dist/index.js +430 -0
- package/packages/react-scene/node_modules/.bin/jiti +21 -0
- package/packages/react-scene/node_modules/.bin/tsc +21 -0
- package/packages/react-scene/node_modules/.bin/tsserver +21 -0
- package/packages/react-scene/node_modules/.bin/tsup +21 -0
- package/packages/react-scene/node_modules/.bin/tsup-node +21 -0
- package/packages/react-scene/node_modules/.bin/tsx +21 -0
- package/packages/scene/dist/index.d.ts +46 -0
- package/packages/scene/dist/index.js +54 -0
- package/packages/scene/node_modules/.bin/jiti +21 -0
- package/packages/scene/node_modules/.bin/tsc +21 -0
- package/packages/scene/node_modules/.bin/tsserver +21 -0
- package/packages/scene/node_modules/.bin/tsup +21 -0
- package/packages/scene/node_modules/.bin/tsup-node +21 -0
- package/packages/scene/node_modules/.bin/tsx +21 -0
- package/packages/signal/dist/index.d.ts +54 -0
- package/packages/signal/dist/index.js +169 -0
- package/packages/signal/node_modules/.bin/jiti +21 -0
- package/packages/signal/node_modules/.bin/tsc +21 -0
- package/packages/signal/node_modules/.bin/tsserver +21 -0
- package/packages/signal/node_modules/.bin/tsup +21 -0
- package/packages/signal/node_modules/.bin/tsup-node +21 -0
- package/packages/signal/node_modules/.bin/tsx +21 -0
- package/packages/site/dist/index.d.ts +31 -0
- package/packages/site/dist/index.js +62 -0
- package/packages/site/node_modules/.bin/jiti +21 -0
- package/packages/site/node_modules/.bin/tsc +21 -0
- package/packages/site/node_modules/.bin/tsserver +21 -0
- package/packages/site/node_modules/.bin/tsup +21 -0
- package/packages/site/node_modules/.bin/tsup-node +21 -0
- package/packages/site/node_modules/.bin/tsx +21 -0
- package/packages/solid/dist/index.d.ts +30 -0
- package/packages/solid/dist/index.js +174 -0
- package/packages/solid/node_modules/.bin/jiti +21 -0
- package/packages/solid/node_modules/.bin/tsc +21 -0
- package/packages/solid/node_modules/.bin/tsserver +21 -0
- package/packages/solid/node_modules/.bin/tsup +21 -0
- package/packages/solid/node_modules/.bin/tsup-node +21 -0
- package/packages/solid/node_modules/.bin/tsx +21 -0
- package/packages/surface/dist/index.d.ts +8 -0
- package/packages/surface/dist/index.js +14 -0
- package/packages/surface/node_modules/.bin/jiti +21 -0
- package/packages/surface/node_modules/.bin/tsc +21 -0
- package/packages/surface/node_modules/.bin/tsserver +21 -0
- package/packages/surface/node_modules/.bin/tsup +21 -0
- package/packages/surface/node_modules/.bin/tsup-node +21 -0
- package/packages/surface/node_modules/.bin/tsx +21 -0
- package/packages/svelte/dist/index.d.ts +12 -0
- package/packages/svelte/dist/index.js +15 -0
- package/packages/svelte/node_modules/.bin/acorn +21 -0
- package/packages/svelte/node_modules/.bin/jiti +21 -0
- package/packages/svelte/node_modules/.bin/svelte-check +21 -0
- package/packages/svelte/node_modules/.bin/tsc +21 -0
- package/packages/svelte/node_modules/.bin/tsserver +21 -0
- package/packages/svelte/node_modules/.bin/tsup +21 -0
- package/packages/svelte/node_modules/.bin/tsup-node +21 -0
- package/packages/svelte/node_modules/.bin/tsx +21 -0
- package/packages/svelte/node_modules/.bin/vite +21 -0
- package/packages/ui/dist/index.d.ts +200 -0
- package/packages/ui/dist/index.js +429 -0
- package/packages/ui/node_modules/.bin/jiti +21 -0
- package/packages/ui/node_modules/.bin/tsc +21 -0
- package/packages/ui/node_modules/.bin/tsserver +21 -0
- package/packages/ui/node_modules/.bin/tsup +21 -0
- package/packages/ui/node_modules/.bin/tsup-node +21 -0
- package/packages/ui/node_modules/.bin/tsx +21 -0
- package/packages/ui/src/select.tsx +3 -4
- package/packages/vue/dist/index.d.ts +71 -0
- package/packages/vue/dist/index.js +193 -0
- package/packages/vue/node_modules/.bin/jiti +21 -0
- package/packages/vue/node_modules/.bin/tsc +21 -0
- package/packages/vue/node_modules/.bin/tsserver +21 -0
- package/packages/vue/node_modules/.bin/tsup +21 -0
- package/packages/vue/node_modules/.bin/tsup-node +21 -0
- package/packages/vue/node_modules/.bin/tsx +21 -0
- package/scripts/vite-aliases.mjs +125 -0
- package/.changeset/README.md +0 -23
- package/.changeset/combobox-and-changesets.md +0 -9
- package/.changeset/config.json +0 -30
- package/.changeset/framework-adapters.md +0 -12
- package/.changeset/scene-3d.md +0 -6
- package/.changeset/scene-plane.md +0 -8
- package/.changeset/site-og.md +0 -5
- package/.cursor/rules/aeon-ui.mdc +0 -22
- package/ACCESSIBILITY.md +0 -32
- package/AGENTS.md +0 -226
- package/COMPONENTS.md +0 -41
- package/FRAMEWORKS.md +0 -57
- package/PHILOSOPHY.md +0 -71
- package/PUBLISHING.md +0 -85
- package/SCENE.md +0 -113
- package/STATES.md +0 -243
- package/apps/demo/index.html +0 -31
- package/apps/demo/og.config.mjs +0 -20
- package/apps/demo/package.json +0 -41
- package/apps/demo/public/favicon.svg +0 -16
- package/apps/demo/public/og-aeon-ui-engine.png +0 -0
- package/apps/demo/public/og.svg +0 -35
- package/apps/demo/scripts/generate-og-png.mjs +0 -62
- package/apps/demo/src/App.tsx +0 -31
- package/apps/demo/src/aeonDiagrams.ts +0 -42
- package/apps/demo/src/app.css +0 -16
- package/apps/demo/src/components/ActionCluster.tsx +0 -20
- package/apps/demo/src/components/AsyncMachineDemo.tsx +0 -97
- package/apps/demo/src/components/ButtonLifecycleDemo.tsx +0 -63
- package/apps/demo/src/components/CodeExample.tsx +0 -7
- package/apps/demo/src/components/ComboboxDemo.tsx +0 -30
- package/apps/demo/src/components/DiagramsSection.tsx +0 -37
- package/apps/demo/src/components/FieldStateDemo.tsx +0 -51
- package/apps/demo/src/components/GridMesh.tsx +0 -8
- package/apps/demo/src/components/HeroInstall.tsx +0 -13
- package/apps/demo/src/components/LandingFaq.tsx +0 -29
- package/apps/demo/src/components/LandingFooter.tsx +0 -30
- package/apps/demo/src/components/LandingHeader.tsx +0 -62
- package/apps/demo/src/components/LandingHero.tsx +0 -55
- package/apps/demo/src/components/LayersSection.tsx +0 -47
- package/apps/demo/src/components/MermaidDiagram.tsx +0 -86
- package/apps/demo/src/components/PhilosophySection.tsx +0 -31
- package/apps/demo/src/components/PlaygroundBlock.tsx +0 -18
- package/apps/demo/src/components/PlaygroundControlsDemo.tsx +0 -63
- package/apps/demo/src/components/PlaygroundSection.tsx +0 -120
- package/apps/demo/src/components/ScrollRegionDemo.tsx +0 -74
- package/apps/demo/src/components/ThemeSwitcher.tsx +0 -79
- package/apps/demo/src/demo-layout.css +0 -293
- package/apps/demo/src/landing.css +0 -1151
- package/apps/demo/src/main.tsx +0 -14
- package/apps/demo/src/nav.ts +0 -9
- package/apps/demo/src/site.ts +0 -218
- package/apps/demo/tsconfig.json +0 -17
- package/apps/demo/vite-plugins/og-meta.ts +0 -30
- package/apps/demo/vite.config.ts +0 -69
- package/docs/API_PATTERNS.md +0 -234
- package/docs/ARCHITECTURE.md +0 -125
- package/docs/COMPONENT_CHECKLIST.md +0 -103
- package/docs/FILE_MAP.md +0 -122
- package/docs/GLOSSARY.md +0 -125
- package/docs/INDEX.md +0 -53
- package/docs/LAYOUT_COORDINATES.md +0 -178
- package/docs/MACHINES.md +0 -171
- package/docs/MIGRATION_FROM_ARK.md +0 -295
- package/docs/VITE_CONSUMER.md +0 -114
- package/pnpm-workspace.yaml +0 -3
- package/vercel.json +0 -12
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
# Layout coordinates (responsive play surfaces)
|
|
2
|
-
|
|
3
|
-
**Problem:** Games and rich apps need the same **logical** interaction space on desktop (split columns) and mobile (stacked lanes or overlays). CSS alone does not give you stable pointer math when regions move.
|
|
4
|
-
|
|
5
|
-
**Aeon answer:** Separate three concerns — same pattern as `UI = f(state)`:
|
|
6
|
-
|
|
7
|
-
| Layer | Owns | Package |
|
|
8
|
-
|-------|------|---------|
|
|
9
|
-
| **Behavior** | Statecharts, rounds, bets | `@aeon-ui/primitives` + app |
|
|
10
|
-
| **Composition** | *Where* regions sit (split / stack / overlay) | CSS + `ResponsiveLayoutSpec` |
|
|
11
|
-
| **Coordinates** | *Where* the user points inside a region | `@aeon-ui/core` `layout` module |
|
|
12
|
-
|
|
13
|
-
Pointer logic uses a **normalized plane** (0–100 per axis) inside a measured `DOMRect`, not raw pixels.
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## North star
|
|
18
|
-
|
|
19
|
-
**Layout mode is explicit. Coordinates are normalized. CSS projects mode to pixels.**
|
|
20
|
-
|
|
21
|
-
- `data-aeon-layout-mode="compact" | "wide"`
|
|
22
|
-
- `data-aeon-layout-composition="split" | "stack" | "overlay"`
|
|
23
|
-
- Anatomy: `playSurfaceAnatomy` → `mat`, `table`, `dice`, `bank`
|
|
24
|
-
|
|
25
|
-
Agents: [AGENTS.md](../AGENTS.md) · API: `packages/core/src/layout.ts`
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## Normalized plane
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
import {
|
|
33
|
-
normFromClient,
|
|
34
|
-
clientFromNorm,
|
|
35
|
-
clampNorm,
|
|
36
|
-
NORM_PLANE,
|
|
37
|
-
} from '@aeon-ui/core'
|
|
38
|
-
|
|
39
|
-
const rect = bowlEl.getBoundingClientRect()
|
|
40
|
-
const aim = normFromClient(rect, e.clientX, e.clientY, {
|
|
41
|
-
origin: NORM_PLANE.bottomLeft, // y up — throws, dice
|
|
42
|
-
bounds: { xMin: 10, xMax: 90, yMin: 26, yMax: 74 },
|
|
43
|
-
})
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
| Origin | Y direction | Use |
|
|
47
|
-
|--------|-------------|-----|
|
|
48
|
-
| `bottom-left` | Up | Dice throw, spatial games, scene aim |
|
|
49
|
-
| `top-left` | Down | HUD-aligned hit targets (optional) |
|
|
50
|
-
|
|
51
|
-
**Rule:** Pick one origin per region and keep it in the spec. Do not mix origins in one gesture pipeline.
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
## Responsive layout spec
|
|
56
|
-
|
|
57
|
-
Declare **composition per mode**, not ad hoc media queries in TS:
|
|
58
|
-
|
|
59
|
-
```ts
|
|
60
|
-
import type { ResponsiveLayoutSpec } from '@aeon-ui/core'
|
|
61
|
-
|
|
62
|
-
export const ceeLoPlayLayout: ResponsiveLayoutSpec = {
|
|
63
|
-
compactMq: '(max-width: 39.99rem)',
|
|
64
|
-
modes: {
|
|
65
|
-
wide: {
|
|
66
|
-
id: 'ceelo-wide',
|
|
67
|
-
composition: 'split',
|
|
68
|
-
regions: [
|
|
69
|
-
{ region: 'dice', slot: 'bowl', zIndex: 1 },
|
|
70
|
-
{ region: 'table', slot: 'felt', zIndex: 1 },
|
|
71
|
-
],
|
|
72
|
-
bounds: {
|
|
73
|
-
dice: { xMin: 8, xMax: 40, yMin: 8, yMax: 92 },
|
|
74
|
-
table: { xMin: 0, xMax: 100, yMin: 0, yMax: 100 },
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
compact: {
|
|
78
|
-
id: 'ceelo-compact',
|
|
79
|
-
composition: 'stack',
|
|
80
|
-
regions: [
|
|
81
|
-
{ region: 'table', slot: 'felt', zIndex: 2 },
|
|
82
|
-
{ region: 'dice', slot: 'dice', zIndex: 4 },
|
|
83
|
-
],
|
|
84
|
-
bounds: {
|
|
85
|
-
dice: { xMin: 10, xMax: 90, yMin: 26, yMax: 74 },
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
}
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
| Composition | Meaning | CSS strategy |
|
|
93
|
-
|-------------|---------|--------------|
|
|
94
|
-
| `split` | Regions side by side | `grid-template-columns: col + 1fr` |
|
|
95
|
-
| `stack` | Regions in rows | `grid-template-rows: auto + lane` |
|
|
96
|
-
| `overlay` | Same cell, z-index | Single `mat` area + absolute children |
|
|
97
|
-
|
|
98
|
-
**Do not** encode `overlay` until stack/split is stable — Casino-games reverted overlay for this reason.
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
## React
|
|
103
|
-
|
|
104
|
-
```ts
|
|
105
|
-
import { useLayoutMode } from '@aeon-ui/react'
|
|
106
|
-
import { layoutModeAttrs, regionBoundsForMode } from '@aeon-ui/core'
|
|
107
|
-
|
|
108
|
-
const { mode, composition } = useLayoutMode(ceeLoPlayLayout)
|
|
109
|
-
const diceBounds = regionBoundsForMode(ceeLoPlayLayout, mode, 'dice')
|
|
110
|
-
|
|
111
|
-
<div
|
|
112
|
-
data-aeon-scope="play"
|
|
113
|
-
data-aeon-part="mat"
|
|
114
|
-
{...layoutModeAttrs(mode, composition.composition)}
|
|
115
|
-
>
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
Measure the **dice region** element for `normFromClient`, not the full mat, when bounds are defined per region.
|
|
119
|
-
|
|
120
|
-
```ts
|
|
121
|
-
import { measureElement, resolveRegionRect, normFromClient } from '@aeon-ui/core'
|
|
122
|
-
|
|
123
|
-
const diceRect = resolveRegionRect(
|
|
124
|
-
measureElement(bowlRef.current),
|
|
125
|
-
measureElement(matRef.current),
|
|
126
|
-
)
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
---
|
|
130
|
-
|
|
131
|
-
## CSS contract
|
|
132
|
-
|
|
133
|
-
1. Map `composition.regions[].slot` → `grid-template-areas` per `data-aeon-layout-mode`.
|
|
134
|
-
2. Never drive throw math from `grid-template-columns` percentages — measure DOM.
|
|
135
|
-
3. Optional Panda conditions:
|
|
136
|
-
|
|
137
|
-
```css
|
|
138
|
-
[data-aeon-scope='play'][data-aeon-layout-mode='compact'][data-aeon-layout-composition='stack'] {
|
|
139
|
-
/* stack rules */
|
|
140
|
-
}
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
---
|
|
144
|
-
|
|
145
|
-
## Checklist (new game surface)
|
|
146
|
-
|
|
147
|
-
- [ ] List logical regions (`table`, `dice`, `bank`, …).
|
|
148
|
-
- [ ] Add `ResponsiveLayoutSpec` with `wide` + `compact` compositions.
|
|
149
|
-
- [ ] Add per-mode `bounds` for any region that receives drag/aim.
|
|
150
|
-
- [ ] CSS: two layouts keyed on `data-aeon-layout-mode` (no overlay until tested).
|
|
151
|
-
- [ ] TS: `useLayoutMode` + `normFromClient` on measured region rects.
|
|
152
|
-
- [ ] Demo or story: resize across breakpoint; aim stays inside bounds.
|
|
153
|
-
|
|
154
|
-
---
|
|
155
|
-
|
|
156
|
-
## Relation to scene plane
|
|
157
|
-
|
|
158
|
-
| Package | Role |
|
|
159
|
-
|---------|------|
|
|
160
|
-
| `@aeon-ui/core` `layout` | App/play surface, pointer norm, breakpoints |
|
|
161
|
-
| `@aeon-ui/geometry` | Decorative scene grids, SVG, `sceneAttrs` |
|
|
162
|
-
| `@aeon-ui/scene` | CSS 3D presentation |
|
|
163
|
-
|
|
164
|
-
Scene coordinates are visual; **layout coordinates** are for input and game logic.
|
|
165
|
-
|
|
166
|
-
---
|
|
167
|
-
|
|
168
|
-
## Reference implementation
|
|
169
|
-
|
|
170
|
-
[Casino-games](https://github.com/GenericCPU/Casino-games) `shared/game/dicePlayLayout.ts` and `diceAim.ts` already use this model informally (`AimBounds` = `NormRect`). Migrating to `@aeon-ui/core` keeps one API across products.
|
|
171
|
-
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
## See also
|
|
175
|
-
|
|
176
|
-
- [STATES.md](../STATES.md) — scroll snapshot (derived spatial state, same idea)
|
|
177
|
-
- [docs/ARCHITECTURE.md](./ARCHITECTURE.md)
|
|
178
|
-
- [packages/core/README.md](../packages/core/README.md)
|
package/docs/MACHINES.md
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
# Machines truth table
|
|
2
|
-
|
|
3
|
-
**Purpose:** Prevent agents from wiring the wrong state layer. This table reflects **current implementation**, not aspirational catalog rows.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Summary table
|
|
8
|
-
|
|
9
|
-
| Component | `@aeon-ui/primitives` machine | Headless runtime | `data-aeon-state` source |
|
|
10
|
-
|-----------|------------------------------|------------------|--------------------------|
|
|
11
|
-
| **Async** | `createAsyncMachine()` | `useAeonMachine` in `Async` context | Snapshot value (+ `stale` flag on readout) |
|
|
12
|
-
| **Field** | `fieldMachine` (parallel) | `useAeonMachine` | `stateToAttr(snapshot.value)` |
|
|
13
|
-
| **Button** | `buttonLifecycleMachine` | **`status` prop** on `Button.Root` | Prop string, not machine by default |
|
|
14
|
-
| **Switch** | `toggleMachine` | `useAeonMachine` | Snapshot |
|
|
15
|
-
| **Checkbox** | `toggleMachine` | `useAeonMachine` | Snapshot |
|
|
16
|
-
| **Dialog** | `dialogMachine` | `useAeonMachine` | `open` / `closed` |
|
|
17
|
-
| **Tabs** | `tabsMachine` | `useAeonMachine` | Selected tab value |
|
|
18
|
-
| **Accordion** | `accordionMachine` | `useAeonMachine` | Per-item open |
|
|
19
|
-
| **Menu** | `popoverMachine` | `useAeonMachine` | `open` / `closed` |
|
|
20
|
-
| **Popover** | `popoverMachine` | `useAeonMachine` | `open` / `closed` |
|
|
21
|
-
| **Toast** (item) | `toastMachine` | `useAeonMachine` | visible / hidden |
|
|
22
|
-
| **Select** | `popoverMachine` | `useAeonMachine` | `open` / `closed`; highlight in React |
|
|
23
|
-
| **Combobox** | — | **`useState`** (open, query, highlight) | `open` / `closed` + highlight attrs |
|
|
24
|
-
| **Tooltip** | — | **`useState(open)`** | `open` / `closed`; touch tap toggle |
|
|
25
|
-
| **Scroll** | — | **`getScrollSnapshot()`** | Derived overflow/edge tokens |
|
|
26
|
-
| **Separator** | — | **static orientation prop** | horizontal / vertical |
|
|
27
|
-
| **Progress** | — | **props** (`value`, `indeterminate`) | idle / loading / complete |
|
|
28
|
-
| **RadioGroup** | `tabsMachine` | `useAeonMachine` | selected value |
|
|
29
|
-
| **Slider** | `sliderMachine` | `useAeonMachine` | idle / dragging + numeric value |
|
|
30
|
-
| **PinInput** | — | **`useState`** | idle / complete |
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## Button: prop vs machine (critical)
|
|
35
|
-
|
|
36
|
-
Two valid patterns — **do not conflate them**:
|
|
37
|
-
|
|
38
|
-
### Pattern A — Headless / styled (default)
|
|
39
|
-
|
|
40
|
-
```tsx
|
|
41
|
-
<Button.Root status="pending" aria-busy>
|
|
42
|
-
Saving…
|
|
43
|
-
</Button.Root>
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
- File: `packages/react/src/button/index.tsx`
|
|
47
|
-
- `status` ∈ `idle` | `pending` | `success` | `failure` | `disabled`
|
|
48
|
-
- Sets `data-aeon-state` via `partAttrs`
|
|
49
|
-
- **No** `useAeonMachine` inside Button
|
|
50
|
-
|
|
51
|
-
### Pattern B — Chart-driven (app / demo)
|
|
52
|
-
|
|
53
|
-
```tsx
|
|
54
|
-
const [snapshot, send] = useAeonMachine(buttonLifecycleMachine)
|
|
55
|
-
const status = snapshot.value as ButtonStatus
|
|
56
|
-
<Button.Root status={status} onClick={() => send({ type: 'PRESS' })} />
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
- Machine: `packages/primitives/src/machines/button.ts`
|
|
60
|
-
- Demo: playground **Button lifecycle**
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## Async events
|
|
65
|
-
|
|
66
|
-
Machine: `packages/primitives/src/machines/async.ts`
|
|
67
|
-
Factory: `createAsyncMachine<T>()` — use per-region instance in context.
|
|
68
|
-
|
|
69
|
-
| Event | Effect |
|
|
70
|
-
|-------|--------|
|
|
71
|
-
| `FETCH` | → `loading` |
|
|
72
|
-
| `RESOLVE` `{ data }` | → `success` or `empty` if `isEmptyAsyncData(data)` |
|
|
73
|
-
| `REJECT` `{ error }` | → `failure` |
|
|
74
|
-
| `RESET` | → `idle`, clear context |
|
|
75
|
-
| `STALE` | Context flag while staying in `success` |
|
|
76
|
-
| `REFRESH` | Re-fetch from success/empty/failure |
|
|
77
|
-
|
|
78
|
-
Constants: `ASYNC_LIFECYCLE_STATES`, `isEmptyAsyncData()`.
|
|
79
|
-
|
|
80
|
-
React: `Async.Root` provides context; children call `send` via `useAsyncContext()`.
|
|
81
|
-
|
|
82
|
-
---
|
|
83
|
-
|
|
84
|
-
## Field events (orthogonal)
|
|
85
|
-
|
|
86
|
-
Machine: `packages/primitives/src/machines/field.ts` — **parallel** regions.
|
|
87
|
-
|
|
88
|
-
| Region | States |
|
|
89
|
-
|--------|--------|
|
|
90
|
-
| `interaction` | `pristine` · `dirty` |
|
|
91
|
-
| `validation` | `valid` · `invalid` |
|
|
92
|
-
| `submission` | `idle` · `pending` |
|
|
93
|
-
|
|
94
|
-
| Event | Typical use |
|
|
95
|
-
|-------|-------------|
|
|
96
|
-
| `INPUT` | User typed |
|
|
97
|
-
| `BLUR` | Left control |
|
|
98
|
-
| `VALIDATE` `{ valid: boolean }` | After rule run |
|
|
99
|
-
| `SUBMIT` | Form submit started |
|
|
100
|
-
| `SUBMIT_DONE` / `SUBMIT_FAIL` | End pending |
|
|
101
|
-
| `RESET` | Clear field |
|
|
102
|
-
|
|
103
|
-
Flatten for DOM:
|
|
104
|
-
|
|
105
|
-
```ts
|
|
106
|
-
import { stateToAttr } from '@aeon-ui/core'
|
|
107
|
-
stateToAttr(snapshot.value) // "interaction:dirty validation:invalid submission:idle"
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
---
|
|
111
|
-
|
|
112
|
-
## Button lifecycle events
|
|
113
|
-
|
|
114
|
-
Machine: `buttonLifecycleMachine` in `packages/primitives/src/machines/button.ts`
|
|
115
|
-
|
|
116
|
-
| Event | From → To (simplified) |
|
|
117
|
-
|-------|------------------------|
|
|
118
|
-
| `PRESS` | idle → pending |
|
|
119
|
-
| `SUCCEED` | pending → success |
|
|
120
|
-
| `FAIL` | pending → failure |
|
|
121
|
-
| `DISABLE` / `ENABLE` | ↔ disabled |
|
|
122
|
-
| `RESET` | → idle |
|
|
123
|
-
|
|
124
|
-
---
|
|
125
|
-
|
|
126
|
-
## Dialog / toggle / tabs / accordion
|
|
127
|
-
|
|
128
|
-
| Machine | File | Notes |
|
|
129
|
-
|---------|------|-------|
|
|
130
|
-
| `dialogMachine` | `machines/dialog.ts` | open/closed; Escape handled in React |
|
|
131
|
-
| `toggleMachine` | `machines/toggle.ts` | Switch + Checkbox |
|
|
132
|
-
| `tabsMachine` | `machines/tabs.ts` | Single active value |
|
|
133
|
-
| `accordionMachine` | `machines/accordion.ts` | Multiple items |
|
|
134
|
-
| `popoverMachine` | `machines/popover.ts` | Menu, Popover, Select |
|
|
135
|
-
| `toastMachine` | `machines/toast.ts` | Auto-dismiss input |
|
|
136
|
-
|
|
137
|
-
Inspect each file for full transition tables. Diagrams: `apps/demo/src/aeonDiagrams.ts`.
|
|
138
|
-
|
|
139
|
-
---
|
|
140
|
-
|
|
141
|
-
## Combobox / Tooltip
|
|
142
|
-
|
|
143
|
-
**No primitives machine.** Open/query/highlight live in:
|
|
144
|
-
|
|
145
|
-
- `packages/react/src/combobox/index.tsx` (item registry for filtering)
|
|
146
|
-
- `packages/react/src/tooltip/index.tsx`
|
|
147
|
-
|
|
148
|
-
**Select** reuses `popoverMachine` for open/closed; option order comes from DOM (`role="option"` ids), not a registry.
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
## Scroll
|
|
153
|
-
|
|
154
|
-
```ts
|
|
155
|
-
import { getScrollSnapshot } from '@aeon-ui/core'
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
Hook: `packages/react/src/scroll/use-scroll-state.ts` — listens to scroll/resize on viewport.
|
|
159
|
-
|
|
160
|
-
States: `idle`, `overflow-y`, `overflow-x`, `overflow-both`, `at-start-y`, `between-y`, `at-end-y`, … (see [STATES.md](../STATES.md) §5).
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
## Diagram sync
|
|
165
|
-
|
|
166
|
-
When you change transitions in `packages/primitives/src/machines/`:
|
|
167
|
-
|
|
168
|
-
1. Update matching diagram string in `apps/demo/src/aeonDiagrams.ts`
|
|
169
|
-
2. Mention in PR / commit that diagrams were synced
|
|
170
|
-
|
|
171
|
-
No automated test enforces sync yet — **manual discipline required**.
|
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
# Migrating from Ark UI to Aeon UI
|
|
2
|
-
|
|
3
|
-
Step-by-step guide for apps that use **`@ark-ui/react`** headless primitives with your own CSS (Tailwind, CSS modules, etc.). Chakra-wrapped Ark recipes are out of scope here—migrate the headless boundary first, then revisit styling.
|
|
4
|
-
|
|
5
|
-
**Reference consumers**
|
|
6
|
-
|
|
7
|
-
| App | Stack | Notes |
|
|
8
|
-
|-----|-------|--------|
|
|
9
|
-
| [spidercorp.xyz](https://github.com/GenericCPU/spydercorp.xyz) | Vite + React | Field, Select, Dialog, Toast |
|
|
10
|
-
| [soundbase.click](https://github.com/GenericCPU/soundbase) | Next.js + React | Avatar (Chakra remains for other widgets) |
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## 1. Inventory Ark usage
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
rg "@ark-ui" --glob '*.{ts,tsx}' .
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
Group imports by component (`dialog`, `select`, `field`, `avatar`, `toast`, `portal`, `collection`, …). Migrate in this order when possible:
|
|
21
|
-
|
|
22
|
-
1. **Leaf primitives** with a direct Aeon equivalent (Avatar, Switch, Checkbox).
|
|
23
|
-
2. **Forms** — Field, Select, Combobox.
|
|
24
|
-
3. **Overlays** — Dialog, Menu, Popover, Tooltip.
|
|
25
|
-
4. **Toasts** — replace `createToaster` with `Toast.Provider` + `useToast()`.
|
|
26
|
-
|
|
27
|
-
Components with **no** Aeon headless yet (Slider, Number Input, Steps, …): keep Ark for that file until Aeon ships them, or replace with a minimal local implementation.
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## 2. Install Aeon runtime deps
|
|
32
|
-
|
|
33
|
-
Aeon headless peers:
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
npm install @xstate/react xstate
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
When `@aeon-ui/*` is on npm:
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
npm install @aeon-ui/react @aeon-ui/core @aeon-ui/primitives
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Until publish, point the bundler at a built clone (sections 3–4).
|
|
46
|
-
|
|
47
|
-
---
|
|
48
|
-
|
|
49
|
-
## 3. Build Aeon once
|
|
50
|
-
|
|
51
|
-
From the Aeon UI repo:
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
pnpm run build:headless
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
In the consumer `package.json`:
|
|
58
|
-
|
|
59
|
-
```json
|
|
60
|
-
{
|
|
61
|
-
"scripts": {
|
|
62
|
-
"aeon:ensure": "node <path-to-AeonUI>/scripts/ensure-headless-built.mjs",
|
|
63
|
-
"prebuild": "npm run aeon:ensure"
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
CI: set `AEON_UI_ROOT` to the checkout path.
|
|
69
|
-
|
|
70
|
-
---
|
|
71
|
-
|
|
72
|
-
## 4. Wire the bundler
|
|
73
|
-
|
|
74
|
-
### Vite
|
|
75
|
-
|
|
76
|
-
See [VITE_CONSUMER.md](./VITE_CONSUMER.md) for full `resolve.alias`, `optimizeDeps.include`, and TypeScript `paths`.
|
|
77
|
-
|
|
78
|
-
### Next.js (recommended: `file:` links)
|
|
79
|
-
|
|
80
|
-
Turbopack often **cannot** import files outside the app root via `resolveAlias`. Link the built packages into `node_modules` instead:
|
|
81
|
-
|
|
82
|
-
```json
|
|
83
|
-
{
|
|
84
|
-
"dependencies": {
|
|
85
|
-
"@aeon-ui/core": "file:../../../AeonUI/packages/core",
|
|
86
|
-
"@aeon-ui/primitives": "file:../../../AeonUI/packages/primitives",
|
|
87
|
-
"@aeon-ui/react": "file:../../../AeonUI/packages/react",
|
|
88
|
-
"@xstate/react": "^5.0.3",
|
|
89
|
-
"xstate": "^5.19.2"
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Run `npm install` after `pnpm run build:headless` in Aeon UI (or `npm run aeon:ensure` in the consumer).
|
|
95
|
-
|
|
96
|
-
`next.config.mjs`:
|
|
97
|
-
|
|
98
|
-
```js
|
|
99
|
-
export default {
|
|
100
|
-
transpilePackages: ['@aeon-ui/react', '@aeon-ui/core', '@aeon-ui/primitives'],
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
Optional: `next build --webpack` if you hit Turbopack edge cases.
|
|
105
|
-
|
|
106
|
-
`tsconfig.json` paths (optional; `file:` installs usually typecheck via package `types`):
|
|
107
|
-
|
|
108
|
-
```json
|
|
109
|
-
{
|
|
110
|
-
"compilerOptions": {
|
|
111
|
-
"paths": {
|
|
112
|
-
"@aeon-ui/core": ["../../../AeonUI/packages/core/dist/index.d.ts"],
|
|
113
|
-
"@aeon-ui/primitives": ["../../../AeonUI/packages/primitives/dist/index.d.ts"],
|
|
114
|
-
"@aeon-ui/react": ["../../../AeonUI/packages/react/dist/index.d.ts"]
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
Mark client boundaries: `'use client'` on files that import `@aeon-ui/react`.
|
|
121
|
-
|
|
122
|
-
---
|
|
123
|
-
|
|
124
|
-
## 5. API mapping (Ark → Aeon)
|
|
125
|
-
|
|
126
|
-
| Ark | Aeon |
|
|
127
|
-
|-----|------|
|
|
128
|
-
| `data-scope` / `data-part` | `data-aeon-scope` / `data-aeon-part` |
|
|
129
|
-
| `data-state` | `data-aeon-state` |
|
|
130
|
-
| `Field.Input` | `Field.Control` |
|
|
131
|
-
| `Field.Textarea` | `Field.Textarea` |
|
|
132
|
-
| `createListCollection` + `Select.*` | `Select.Root` → `Trigger` + `ValueText` → `Positioner` → `Content` → `Item` |
|
|
133
|
-
| `Portal` + `Dialog.*` | `Dialog.Portal` wraps `Backdrop`, `Positioner`, `Content` |
|
|
134
|
-
| `Avatar.Root` / `Image` / `Fallback` | Same part names under `@aeon-ui/react` |
|
|
135
|
-
| `createToaster` + `Toaster` | `Toast.Provider`, `Toast.Viewport`, `useToast().publish()` |
|
|
136
|
-
|
|
137
|
-
### Select (required structure)
|
|
138
|
-
|
|
139
|
-
```tsx
|
|
140
|
-
import { Select } from '@aeon-ui/react'
|
|
141
|
-
|
|
142
|
-
<Select.Root value={value} onValueChange={setValue}>
|
|
143
|
-
<Select.Trigger>
|
|
144
|
-
<Select.ValueText>{label}</Select.ValueText>
|
|
145
|
-
</Select.Trigger>
|
|
146
|
-
<Select.Positioner>
|
|
147
|
-
<Select.Content>
|
|
148
|
-
<Select.Item value="a">Option A</Select.Item>
|
|
149
|
-
</Select.Content>
|
|
150
|
-
</Select.Positioner>
|
|
151
|
-
</Select.Root>
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
Use `portalled={false}` in tests or fixed-height containers.
|
|
155
|
-
|
|
156
|
-
### Dialog
|
|
157
|
-
|
|
158
|
-
```tsx
|
|
159
|
-
<Dialog.Root open={open} onOpenChange={setOpen}>
|
|
160
|
-
<Dialog.Trigger>Open</Dialog.Trigger>
|
|
161
|
-
<Dialog.Portal>
|
|
162
|
-
<Dialog.Backdrop />
|
|
163
|
-
<Dialog.Positioner>
|
|
164
|
-
<Dialog.Content>
|
|
165
|
-
<Dialog.Title>Title</Dialog.Title>
|
|
166
|
-
<Dialog.CloseTrigger>Close</Dialog.CloseTrigger>
|
|
167
|
-
</Dialog.Content>
|
|
168
|
-
</Dialog.Positioner>
|
|
169
|
-
</Dialog.Portal>
|
|
170
|
-
</Dialog.Root>
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
### Avatar (shadcn-style wrapper)
|
|
174
|
-
|
|
175
|
-
Keep your `components/ui/avatar.tsx` as a thin Tailwind wrapper over Aeon parts so call sites stay unchanged:
|
|
176
|
-
|
|
177
|
-
```tsx
|
|
178
|
-
import { Avatar as AeonAvatar } from '@aeon-ui/react'
|
|
179
|
-
|
|
180
|
-
function Avatar({ className, ...props }) {
|
|
181
|
-
return <AeonAvatar.Root className={cn('relative flex size-8 ...', className)} {...props} />
|
|
182
|
-
}
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
Aeon does not force width/height recipes—Tailwind `size-*` on `Root` works.
|
|
186
|
-
|
|
187
|
-
### Toasts
|
|
188
|
-
|
|
189
|
-
```tsx
|
|
190
|
-
// layout.tsx
|
|
191
|
-
import { Toast } from '@aeon-ui/react'
|
|
192
|
-
|
|
193
|
-
<Toast.Provider>
|
|
194
|
-
{children}
|
|
195
|
-
<Toast.Viewport placement="bottom-end" />
|
|
196
|
-
</Toast.Provider>
|
|
197
|
-
|
|
198
|
-
// anywhere
|
|
199
|
-
const { publish } = useToast()
|
|
200
|
-
publish({ title: 'Saved' })
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
---
|
|
204
|
-
|
|
205
|
-
## 6. Restyle with `data-aeon-*`
|
|
206
|
-
|
|
207
|
-
Ark CSS often targets `[data-part=trigger]`. Rename selectors:
|
|
208
|
-
|
|
209
|
-
```css
|
|
210
|
-
/* was: [data-scope='select'][data-part='content'] */
|
|
211
|
-
[data-aeon-scope='select'][data-aeon-part='content'] {
|
|
212
|
-
/* ... */
|
|
213
|
-
}
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
State:
|
|
217
|
-
|
|
218
|
-
```css
|
|
219
|
-
[data-aeon-scope='select'][data-aeon-part='item'][data-aeon-state='highlighted'] {
|
|
220
|
-
background: var(--accent);
|
|
221
|
-
}
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
See [STATES.md](../STATES.md) and [INTEGRATION.md](../INTEGRATION.md).
|
|
225
|
-
|
|
226
|
-
---
|
|
227
|
-
|
|
228
|
-
## 7. Remove Ark
|
|
229
|
-
|
|
230
|
-
```bash
|
|
231
|
-
npm uninstall @ark-ui/react
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
Re-run build and grep for stragglers:
|
|
235
|
-
|
|
236
|
-
```bash
|
|
237
|
-
rg "@ark-ui" .
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
---
|
|
241
|
-
|
|
242
|
-
## 8. Verify
|
|
243
|
-
|
|
244
|
-
| Check | Command / action |
|
|
245
|
-
|-------|------------------|
|
|
246
|
-
| Aeon tests | `cd AeonUI && pnpm test:headless` |
|
|
247
|
-
| Consumer build | `npm run build` / `vite build` |
|
|
248
|
-
| Select | Open list, keyboard, pick option, Escape |
|
|
249
|
-
| Dialog | Open, focus trap, Escape, scroll lock |
|
|
250
|
-
| Avatar | Image load, fallback when missing/broken |
|
|
251
|
-
| Toasts | `publish` after provider mount |
|
|
252
|
-
|
|
253
|
-
---
|
|
254
|
-
|
|
255
|
-
## 9. Chakra + Ark stacks
|
|
256
|
-
|
|
257
|
-
Many HandCash templates use **Chakra v3 recipes** built on Ark. Migrating “off Ark” does not require removing Chakra in one pass:
|
|
258
|
-
|
|
259
|
-
1. Replace **direct** `@ark-ui/react` imports (e.g. `components/ui/avatar.tsx`).
|
|
260
|
-
2. Later, swap Chakra widgets for `@aeon-ui/react` or `@aeon-ui/ui` per component.
|
|
261
|
-
|
|
262
|
-
soundbase.click keeps Chakra for Button, Dialog, Select, etc.; only Avatar uses Aeon headless today.
|
|
263
|
-
|
|
264
|
-
---
|
|
265
|
-
|
|
266
|
-
## Light / dark transitions (Tailwind + next-themes)
|
|
267
|
-
|
|
268
|
-
Import Aeon’s transition stylesheet and bridge `data-aeon-mode` to your existing theme toggle:
|
|
269
|
-
|
|
270
|
-
```css
|
|
271
|
-
/* globals.css */
|
|
272
|
-
@import '@aeon-ui/core/theme-transition.css';
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
```tsx
|
|
276
|
-
import { AeonThemeBridge } from '@/components/aeon-theme-bridge' // syncs next-themes → data-aeon-mode
|
|
277
|
-
import { runAeonThemeTransition } from '@aeon-ui/core'
|
|
278
|
-
|
|
279
|
-
// layout: inside <ThemeProvider>
|
|
280
|
-
<AeonThemeBridge />
|
|
281
|
-
|
|
282
|
-
// theme menu
|
|
283
|
-
runAeonThemeTransition(() => setTheme('dark'))
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
Reference: **soundbase.click** (`components/aeon-theme-bridge.tsx`).
|
|
287
|
-
|
|
288
|
-
---
|
|
289
|
-
|
|
290
|
-
## Related docs
|
|
291
|
-
|
|
292
|
-
- [VITE_CONSUMER.md](./VITE_CONSUMER.md) — Vite-specific aliases
|
|
293
|
-
- [MACHINES.md](./MACHINES.md) — which components use XState
|
|
294
|
-
- [ACCESSIBILITY.md](../ACCESSIBILITY.md) — roles and keyboard
|
|
295
|
-
- [API_PATTERNS.md](./API_PATTERNS.md) — copy-paste snippets
|