aeon-ui-engine 1.0.0 → 1.0.2

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.
Files changed (88) hide show
  1. package/.changeset/config.json +2 -1
  2. package/CHANGELOG.md +38 -0
  3. package/COMPONENTS.md +7 -2
  4. package/INTEGRATION.md +14 -0
  5. package/PUBLISHING.md +20 -0
  6. package/README.md +8 -4
  7. package/apps/demo/og.config.mjs +8 -3
  8. package/apps/demo/public/og-aeon-ui-engine.png +0 -0
  9. package/apps/demo/public/og.svg +2 -2
  10. package/apps/demo/scripts/generate-og-png.mjs +4 -2
  11. package/apps/demo/src/App.tsx +2 -2
  12. package/apps/demo/src/aeonDiagrams.ts +17 -75
  13. package/apps/demo/src/components/DiagramsSection.tsx +8 -9
  14. package/apps/demo/src/components/HeroInstall.tsx +7 -4
  15. package/apps/demo/src/components/LandingFaq.tsx +1 -1
  16. package/apps/demo/src/components/LandingFooter.tsx +8 -0
  17. package/apps/demo/src/components/LandingHeader.tsx +4 -2
  18. package/apps/demo/src/components/LayersSection.tsx +6 -10
  19. package/apps/demo/src/components/MermaidDiagram.tsx +5 -3
  20. package/apps/demo/src/components/PhilosophySection.tsx +0 -2
  21. package/apps/demo/src/components/PlaygroundBlock.tsx +3 -8
  22. package/apps/demo/src/components/PlaygroundControlsDemo.tsx +63 -0
  23. package/apps/demo/src/components/PlaygroundSection.tsx +7 -91
  24. package/apps/demo/src/components/ThemeSwitcher.tsx +13 -10
  25. package/apps/demo/src/demo-layout.css +29 -0
  26. package/apps/demo/src/landing.css +138 -89
  27. package/apps/demo/src/nav.ts +3 -3
  28. package/apps/demo/src/site.ts +99 -228
  29. package/docs/MACHINES.md +6 -1
  30. package/package.json +1 -1
  31. package/packages/core/src/anatomy.ts +23 -0
  32. package/packages/panda/panda.config.ts +19 -1
  33. package/packages/panda/src/recipes/button.ts +4 -4
  34. package/packages/panda/src/recipes/combobox.ts +1 -1
  35. package/packages/panda/src/recipes/index.ts +5 -0
  36. package/packages/panda/src/recipes/pin-input.ts +29 -0
  37. package/packages/panda/src/recipes/progress.ts +44 -0
  38. package/packages/panda/src/recipes/radio-group.ts +51 -0
  39. package/packages/panda/src/recipes/select.ts +3 -1
  40. package/packages/panda/src/recipes/separator.ts +35 -0
  41. package/packages/panda/src/recipes/slider.ts +57 -0
  42. package/packages/panda/src/theme/tokens.ts +5 -0
  43. package/packages/panda/styled-system/recipes/aeon-pin-input.d.ts +28 -0
  44. package/packages/panda/styled-system/recipes/aeon-pin-input.mjs +36 -0
  45. package/packages/panda/styled-system/recipes/aeon-progress.d.ts +28 -0
  46. package/packages/panda/styled-system/recipes/aeon-progress.mjs +44 -0
  47. package/packages/panda/styled-system/recipes/aeon-radio-group.d.ts +28 -0
  48. package/packages/panda/styled-system/recipes/aeon-radio-group.mjs +48 -0
  49. package/packages/panda/styled-system/recipes/aeon-separator.d.ts +31 -0
  50. package/packages/panda/styled-system/recipes/aeon-separator.mjs +41 -0
  51. package/packages/panda/styled-system/recipes/aeon-slider.d.ts +28 -0
  52. package/packages/panda/styled-system/recipes/aeon-slider.mjs +48 -0
  53. package/packages/panda/styled-system/recipes/index.d.ts +5 -0
  54. package/packages/panda/styled-system/recipes/index.mjs +5 -0
  55. package/packages/panda/styled-system/styles.css +233 -52
  56. package/packages/panda/styled-system/tokens/index.mjs +32 -0
  57. package/packages/panda/styled-system/tokens/tokens.d.ts +2 -2
  58. package/packages/panda/styled-system/types/conditions.d.ts +1 -1
  59. package/packages/primitives/src/index.ts +1 -0
  60. package/packages/primitives/src/machines/slider.ts +93 -0
  61. package/packages/react/src/checkbox/index.tsx +14 -0
  62. package/packages/react/src/floating/FloatingPositioner.tsx +59 -0
  63. package/packages/react/src/hooks/use-anchor-position.ts +33 -5
  64. package/packages/react/src/hooks/use-prefers-hover.ts +16 -0
  65. package/packages/react/src/index.ts +5 -0
  66. package/packages/react/src/menu/index.tsx +38 -11
  67. package/packages/react/src/pin-input/index.tsx +223 -0
  68. package/packages/react/src/popover/index.tsx +38 -11
  69. package/packages/react/src/progress/index.tsx +110 -0
  70. package/packages/react/src/radio-group/index.tsx +199 -0
  71. package/packages/react/src/select/index.tsx +51 -28
  72. package/packages/react/src/select/select.test.tsx +21 -0
  73. package/packages/react/src/separator/index.tsx +23 -0
  74. package/packages/react/src/slider/index.tsx +264 -0
  75. package/packages/react/src/toast/primitives.tsx +8 -1
  76. package/packages/react/src/toast/toast.test.tsx +40 -0
  77. package/packages/react/src/tooltip/index.tsx +124 -20
  78. package/packages/react/src/tooltip/tooltip.test.tsx +114 -0
  79. package/packages/site/README.md +1 -1
  80. package/packages/ui/src/index.ts +5 -0
  81. package/packages/ui/src/pin-input.tsx +16 -0
  82. package/packages/ui/src/progress.tsx +21 -0
  83. package/packages/ui/src/radio-group.tsx +24 -0
  84. package/packages/ui/src/select.tsx +3 -4
  85. package/packages/ui/src/separator.tsx +15 -0
  86. package/packages/ui/src/slider.tsx +24 -0
  87. package/apps/demo/public/og.png +0 -0
  88. package/apps/demo/src/components/SceneSection.tsx +0 -157
@@ -3,12 +3,23 @@ export const siteOrigin =
3
3
  (import.meta.env.VITE_SITE_ORIGIN as string | undefined)?.replace(/\/$/, '') ??
4
4
  'https://aeon-ui.com'
5
5
 
6
+ /** Published npm package (monorepo root). */
7
+ export const npmPackage = 'aeon-ui-engine' as const
8
+
9
+ export const npmInstallCommand = `npm install ${npmPackage}` as const
10
+
11
+ /** User-facing code sample — install line plus usage (no internal workspace import paths). */
12
+ export const withInstall = (usage: string) => `${npmInstallCommand}\n\n${usage.trim()}`
13
+
6
14
  export const site = {
7
15
  brand: 'Aeon UI',
8
16
  tagline: 'Future-proof UI infrastructure for the web',
9
17
  url: 'https://github.com/GenericCPU/aeon-ui',
10
18
  siteOrigin,
11
19
  repoCloneUrl: 'https://github.com/GenericCPU/aeon-ui.git',
20
+ npmPackage,
21
+ npmRegistryUrl: `https://www.npmjs.com/package/${npmPackage}`,
22
+ npmInstallCommand,
12
23
  studio: 'SpiderCorp',
13
24
  studioUrl: 'https://spidercorp.xyz',
14
25
 
@@ -19,10 +30,12 @@ export const site = {
19
30
  'Statechart-driven primitives and a styled layer your team controls — dependable behavior today, room to grow tomorrow.',
20
31
  heroSub:
21
32
  'Grounded in proven state theory and production-ready patterns for compound components, anatomy, and styling.',
22
- heroCtaPrimary: 'See components',
33
+ heroCtaPrimary: 'Try components',
23
34
  heroCtaSecondary: 'View architecture',
24
- heroInstallLabel: 'Early access',
25
- heroInstallAccess: 'DM FOR EARLY ACCESS',
35
+ headerCtaShort: 'Try',
36
+ heroInstallLabel: 'npm',
37
+ heroInstallCommand: npmInstallCommand,
38
+ heroInstallHint: `${npmPackage} on npm — core, React bindings, styled components, and themes in one install.`,
26
39
  stateCardLabel: 'Example: fetch region',
27
40
  stateCardHint: 'Five states on the rail. One active. Click Run.',
28
41
 
@@ -40,115 +53,101 @@ export const site = {
40
53
  {
41
54
  label: 'Styled',
42
55
  detail: 'Panda CSS slot recipes with thoughtful defaults you can extend',
43
- example: "import { Button } from '@aeon-ui/ui'",
56
+ example: `${npmInstallCommand}\nimport { Button } from '@aeon-ui/ui'`,
44
57
  },
45
58
  ],
46
59
 
47
- layersLabel: 'Architecture',
48
- layersTitle: 'Three layers. One coherent system.',
60
+ philosophyLabel: 'Foundation',
61
+ philosophyTitle: 'What Aeon is',
62
+ philosophyLead:
63
+ 'Not a grab bag of widgets — a discipline for interfaces that stay legible as requirements accumulate. Grounded in statechart theory and the conviction that the UI must represent every condition the model allows, and imply none that it does not.',
64
+ thesisUrl:
65
+ 'https://www.theseus.fi/bitstream/handle/10024/334118/Thanh_Nguyen.pdf?sequence=2&isAllowed=y',
66
+ thesisCta: 'Read the thesis (PDF)',
67
+
68
+ layersLabel: 'Shape',
69
+ layersTitle: 'Separation on purpose',
49
70
  layersLead:
50
- 'Headless behavior, explicit anatomy, and intentional styling — composed so your product can evolve without rewriting its foundations.',
71
+ 'Three layers so behavior, contract, and brand can evolve independently — without forking the mental model.',
51
72
 
52
73
  diagramsLabel: 'Statecharts',
53
- diagramsTitle: 'Diagrams from Aeon',
74
+ diagramsTitle: 'A map the whole team can read',
54
75
  diagramsLead:
55
- 'Executable XState models in @aeon-ui/primitives — rendered here as statecharts you can compare to live demos in the playground.',
76
+ 'Executable models you can review with design and engineering before complexity lands in production — the same chart the playground runs.',
56
77
  statesDocUrl: 'https://github.com/GenericCPU/aeon-ui/blob/main/STATES.md',
57
78
 
58
- playgroundLabel: 'Live',
59
- playgroundTitle: 'Experience the components',
79
+ playgroundLabel: 'Proof',
80
+ playgroundTitle: 'See the discipline in motion',
60
81
  playgroundLead:
61
- 'Every control runs on an executable XState model. Interact below and see predictable, reviewable behavior in action.',
62
-
63
- philosophyLabel: 'Foundation',
64
- philosophyTitle: 'Built on statecharts',
65
- philosophyLead:
66
- 'Thanh Nguyen’s research on statecharts for web applications informs how Aeon models interaction — clear states, clear transitions, lasting maintainability.',
67
- thesisUrl:
68
- 'https://www.theseus.fi/bitstream/handle/10024/334118/Thanh_Nguyen.pdf?sequence=2&isAllowed=y',
69
- thesisCta: 'Read the thesis (PDF)',
82
+ 'Each block is a real machine. Interact to feel what it means when the interface and the chart stay in lockstep.',
70
83
 
71
84
  faqLabel: 'FAQ',
72
- faqTitle: 'Common questions',
73
- faqLead: 'Straight answers for teams evaluating durable UI infrastructure.',
85
+ faqTitle: 'Questions teams ask',
86
+ faqLead: 'Identity and adoption — not a feature matrix.',
74
87
  } as const
75
88
 
76
- export const layers = [
77
- {
78
- id: 'core',
79
- title: '@aeon-ui/core',
80
- description: 'Philosophy, anatomy, and attributes your theme can target with confidence.',
81
- highlights: ['asyncAnatomy, buttonAnatomy, …', 'stateToAttr(snapshot)', 'PHILOSOPHY.md + STATES.md'],
82
- example: `import { partAttrs, asyncAnatomy } from '@aeon-ui/core'
83
-
84
- partAttrs(asyncAnatomy.scope, asyncAnatomy.readout, {
85
- state: 'loading',
86
- })`,
87
- },
88
- {
89
- id: 'react',
90
- title: '@aeon-ui/react',
91
- description: 'Headless React bindings where the interface reflects the machine snapshot.',
92
- highlights: ['Async, Field, Dialog, Tabs', 'useAeonMachine(machine)', 'Compound parts'],
93
- example: `import { Async, useAsyncContext } from '@aeon-ui/react'
94
-
95
- <Async.Root>
96
- <Async.Track />
97
- <Async.Readout>{status}</Async.Readout>
98
- </Async.Root>`,
99
- },
100
- {
101
- id: 'ui',
102
- title: '@aeon-ui/ui',
103
- description: 'A polished styled layer — ship as-is or make it yours.',
104
- highlights: ['Buttons, badges, panels', 'Panda slot recipes', 'Themes: light/dark × preset'],
105
- example: `import { Async, Button } from '@aeon-ui/ui'
106
-
107
- <Button.Root status="pending">Saving…</Button.Root>`,
108
- },
109
- ] as const
110
-
89
+ /** Why we built the stack this way — sits above the layer cards. */
111
90
  export const commitments = [
112
91
  {
113
- id: 'exclusive',
114
- title: 'Clear states, clear UI',
92
+ id: 'coherent',
93
+ title: 'One condition at a time',
115
94
  description:
116
- 'Each region holds one stable condition at a time — interfaces stay coherent as features grow.',
95
+ 'Regions hold a single stable state the team can name, test, and extend — so interfaces stay coherent when flows multiply.',
117
96
  },
118
97
  {
119
- id: 'diagram',
120
- title: 'Diagrammable by design',
98
+ id: 'shared',
99
+ title: 'A shared map',
121
100
  description:
122
- 'Behavior you can review, extend, and visualize alongside your team before adding complexity.',
101
+ 'Statecharts give designers and engineers the same picture of behavior before edge cases become tribal knowledge.',
123
102
  },
124
103
  {
125
- id: 'defaults',
126
- title: 'Your design system, fully yours',
104
+ id: 'sovereign',
105
+ title: 'Sovereign surfaces',
127
106
  description:
128
- 'Unstyled primitives plus intentional tokens — every surface reflects your brand and your standards.',
107
+ 'Unstyled primitives with a clear anatomy contract. Defaults when you want speed — full control when you do not.',
129
108
  },
130
109
  ] as const
131
110
 
132
111
  export const philosophyTenets = [
133
112
  {
134
113
  title: 'Write the chart first',
135
- body: 'Decide every stable state and transition before you pick colors.',
136
- example: `states: { idle: {}, loading: {}, success: {}, failure: {} }`,
114
+ body: 'List every stable state and transition before you choose type scales. The model is the brief; the UI is its faithful projection.',
137
115
  },
138
116
  {
139
117
  title: 'UI = f(state)',
140
- body: 'Components read one snapshot. Labels, disabled flags, and CSS all agree.',
141
- example: `data-aeon-state="loading"`,
118
+ body: 'The snapshot decides what renders — labels, disabled affordances, and data-aeon-state together. Not parallel React flags that drift out of sync.',
142
119
  },
143
120
  {
144
121
  title: 'Show the full map',
145
- body: 'Users see every state the machine allows — especially for async and forms.',
146
- example: `<Async.Track /> {/* idle · loading · success · … */}`,
122
+ body: 'Users and developers see the same complete lifecycle. Async empty, button failure, field validation — nothing left implicit off-screen.',
147
123
  },
148
124
  {
149
125
  title: 'Scale with regions',
150
- body: 'Split interaction, validation, and submission instead of one overloaded “error”.',
151
- example: `interaction:dirty validation:invalid submission:pending`,
126
+ body: 'When complexity arrives, split orthogonal concerns — interaction, validation, submission — instead of one overloaded “error” boolean.',
127
+ },
128
+ ] as const
129
+
130
+ export const layers = [
131
+ {
132
+ id: 'core',
133
+ title: 'Core',
134
+ description:
135
+ 'The contract between machine and markup — scope, part, and state attributes your theme can target for years.',
136
+ highlights: ['North star & anatomy', 'PHILOSOPHY.md · STATES.md'],
137
+ },
138
+ {
139
+ id: 'react',
140
+ title: 'React',
141
+ description:
142
+ 'Headless bindings where the DOM reflects the snapshot — compound parts, accessibility, and predictable structure.',
143
+ highlights: ['Snapshot-driven parts', 'useAeonMachine for app logic'],
144
+ },
145
+ {
146
+ id: 'ui',
147
+ title: 'Styled UI',
148
+ description:
149
+ 'An optional face with intentional tokens — ship quickly or treat it as a reference implementation for your system.',
150
+ highlights: ['Panda recipes on anatomy', 'Themes: your palette, your modes'],
152
151
  },
153
152
  ] as const
154
153
 
@@ -156,192 +155,64 @@ export const playgroundBlocks = {
156
155
  async: {
157
156
  title: 'Async region',
158
157
  description:
159
- 'A fetch flow with five states. The rail always shows the full lifecycle; the readout and buttons match the active one.',
160
- example: `import { Async, Button } from '@aeon-ui/ui'
161
-
162
- <Async.Root>
163
- <Async.Track />
164
- <Async.Readout>Ready</Async.Readout>
165
- <Async.Actions>
166
- <Button.Root onClick={() => send({ type: 'FETCH' })}>
167
- Fetch
168
- </Button.Root>
169
- </Async.Actions>
170
- </Async.Root>`,
158
+ 'Fetch flows as a named lifecycle — idle through empty — so “loading” is never the only story the UI tells.',
171
159
  },
172
160
  buttonLifecycle: {
173
161
  title: 'Button lifecycle',
174
162
  description:
175
- 'Submit buttons need more than disabled. pending, success, and failure are real states — styled and announced.',
176
- example: `<Button.Root status="pending" aria-busy>
177
- Submitting…
178
- </Button.Root>`,
179
- },
180
- scroll: {
181
- title: 'Scroll regions',
182
- description:
183
- 'Theme-aware scrollbars on vertical, horizontal, and 2D overflow. Scroll position and axis show up on data-aeon-state (e.g. between-y, at-start-x).',
184
- example: `import { Scroll } from '@aeon-ui/ui'
185
-
186
- <Scroll.Root axis="both" maxH="md">
187
- <Scroll.Viewport>
188
- <Scroll.Content>{children}</Scroll.Content>
189
- </Scroll.Viewport>
190
- <Scroll.StateReadout />
191
- </Scroll.Root>`,
163
+ 'Submission is more than disabled: pending, success, and failure are states worth showing, not hidden side effects.',
192
164
  },
193
165
  field: {
194
- title: 'Field regions',
166
+ title: 'Field',
195
167
  description:
196
- 'Three parallel regions: touched or not, valid or not, submitting or not. The root carries all three in data-aeon-state.',
197
- example: `<Field.Root>
198
- <Field.Label htmlFor="email">Email</Field.Label>
199
- <Field.Control id="email" validate={isEmail} />
200
- <Field.Message>Invalid email.</Field.Message>
201
- </Field.Root>`,
202
- },
203
- buttons: {
204
- title: 'Buttons',
205
- description: 'Variants and sizes. Wrap groups in Button.Group for spacing.',
206
- example: `<Button.Group>
207
- <Button.Root>Primary</Button.Root>
208
- <Button.Root variant="outline">Outline</Button.Root>
209
- <Button.Root variant="ghost">Ghost</Button.Root>
210
- </Button.Group>`,
168
+ 'Interaction, validation, and submission as parallel regions — complexity without a single tangled error flag.',
211
169
  },
212
- toggle: {
213
- title: 'Switch & checkbox',
214
- description: 'Checked state lives in the machine. Pressed state is tracked for the thumb.',
215
- example: `<Switch.Root defaultChecked>
216
- <Switch.Control><Switch.Thumb /></Switch.Control>
217
- <Switch.Label>Notifications</Switch.Label>
218
- </Switch.Root>`,
170
+ controls: {
171
+ title: 'Inputs & buttons',
172
+ description:
173
+ 'Toggles, radio group, slider, and pin input — explicit states on every control surface.',
219
174
  },
220
175
  dialog: {
221
176
  title: 'Dialog',
222
- description: 'open and closed are explicit. Escape and backdrop clicks are defined transitions.',
223
- example: `<Dialog.Root>
224
- <Dialog.Trigger>Open</Dialog.Trigger>
225
- <Dialog.Content>
226
- <Dialog.Title>Title</Dialog.Title>
227
- <Dialog.CloseTrigger>Close</Dialog.CloseTrigger>
228
- </Dialog.Content>
229
- </Dialog.Root>`,
230
- },
231
- combobox: {
232
- title: 'Combobox',
233
177
  description:
234
- 'Typeahead select built on the same listbox model as Select — filter options, arrow keys, Enter to commit.',
235
- example: `import { Combobox } from '@aeon-ui/ui'
236
-
237
- <Combobox.Root value={value} onValueChange={setValue}>
238
- <Combobox.Input placeholder="Search…" aria-label="Country" />
239
- <Combobox.Content>
240
- <Combobox.Item value="us">United States</Combobox.Item>
241
- <Combobox.Empty />
242
- </Combobox.Content>
243
- </Combobox.Root>`,
178
+ 'Open and closed are first-class. Dismissal paths are transitions, not surprises.',
244
179
  },
245
180
  overlay: {
246
- title: 'Menu, popover, tooltip & toast',
181
+ title: 'Overlays',
247
182
  description:
248
- 'Floating surfaces share an open/closed machine. Menu uses menu roles; toast uses polite live region.',
249
- example: `import { Menu, Tooltip, Toast, useToast } from '@aeon-ui/ui'
250
-
251
- // App root:
252
- <Toast.Provider>
253
- <App />
254
- <Toast.Viewport placement="bottom-end" />
255
- </Toast.Provider>
256
-
257
- // Anywhere inside the provider:
258
- const { publish } = useToast()
259
- publish({ title: 'Saved', description: 'Changes applied.', durationMs: 4000 })`,
260
- },
261
- tabsAccordion: {
262
- title: 'Tabs & accordion',
263
- description: 'One active tab. Multiple accordion items can be open — each item has its own state.',
264
- example: `<Tabs.Root defaultValue="a">
265
- <Tabs.List>
266
- <Tabs.Trigger value="a">Tab A</Tabs.Trigger>
267
- </Tabs.List>
268
- <Tabs.Content value="a">…</Tabs.Content>
269
- </Tabs.Root>`,
270
- },
271
- } as const
272
-
273
- export const tabsExamples = {
274
- philosophy: {
275
- label: 'Idea',
276
- content: 'Define states in a chart. UI only shows conditions the chart allows.',
277
- example: 'UI = f(snapshot)',
278
- },
279
- machines: {
280
- label: 'Machines',
281
- content: 'XState runs in the browser. Same model you sketch on a whiteboard.',
282
- example: "import { createAsyncMachine } from '@aeon-ui/primitives'",
283
- },
284
- anatomy: {
285
- label: 'Anatomy',
286
- content: 'Style with data attributes — stable across refactors.',
287
- example: '[data-aeon-scope="button"][data-aeon-state="pending"]',
288
- },
289
- } as const
290
-
291
- export const accordionExamples = {
292
- what: {
293
- trigger: 'What is Aeon UI?',
294
- content: 'Headless statechart components for React, plus an optional styled layer.',
295
- example: 'git clone https://github.com/GenericCPU/aeon-ui.git',
296
- },
297
- why: {
298
- trigger: 'Why statecharts?',
299
- content: 'So async, forms, and modals stay explicit as products grow.',
300
- example: 'See STATES.md in the repo for states traditional UI skips.',
183
+ 'Floating surfaces with the same open/closed discipline — anchored behavior you can reason about.',
301
184
  },
302
185
  } as const
303
186
 
304
187
  export const faq = [
305
188
  {
306
- id: 'totality',
307
- question: 'What does “UI = f(state)” mean?',
189
+ id: 'what',
190
+ question: 'What is Aeon UI?',
308
191
  answer:
309
- 'The machine snapshot picks what renders. If the chart has loading, you build loading UI — not a boolean you forgot to clear.',
310
- example: `const status = snapshot.value // "loading"
311
- <Async.Readout>{copy[status]}</Async.Readout>`,
192
+ 'Infrastructure for the web where behavior is modeled explicitly and the interface expresses that model completely. Headless primitives, React bindings, and an optional styled layer — published as aeon-ui-engine.',
312
193
  },
313
194
  {
314
- id: 'structure',
315
- question: 'How do components compose?',
195
+ id: 'why',
196
+ question: 'Why statecharts?',
316
197
  answer:
317
- 'Compound parts (Root, Track, Label, …) with data-aeon-scope and data-aeon-part. Behavior comes from XState machines in @aeon-ui/primitives.',
318
- example: `<Checkbox.Root>
319
- <Checkbox.Control />
320
- <Checkbox.Label>Email me</Checkbox.Label>
321
- </Checkbox.Root>`,
198
+ 'Because products outgrow boolean soup. Statecharts name conditions, separate regions, and stay diagrammable as requirements grow — the thesis case for modern web UI is documented in our primary reference (linked in Foundation).',
322
199
  },
323
200
  {
324
- id: 'styling',
325
- question: 'How do I style things?',
201
+ id: 'totality',
202
+ question: 'What does “UI = f(state)” mean?',
326
203
  answer:
327
- 'Target data-aeon-* in your CSS, or use @aeon-ui/ui recipes. Themes swap palettes on html[data-aeon-theme][data-aeon-mode].',
328
- example: `import '@aeon-ui/panda/styles.css'
329
- import { applyAeonTheme } from '@aeon-ui/panda/themes'
330
-
331
- applyAeonTheme('signal', 'dark')`,
204
+ 'The machine snapshot picks what renders. If the chart includes loading, you build loading UI — and you do not imply success, failure, or empty unless the chart allows them.',
332
205
  },
333
206
  {
334
207
  id: 'headless',
335
- question: 'Headless only?',
208
+ question: 'Do I have to use your styles?',
336
209
  answer:
337
- 'Yes. Import @aeon-ui/react, bring your own CSS, and keep the same anatomy attributes.',
338
- example: `import { Dialog } from '@aeon-ui/react'`,
210
+ 'No. The headless layer is the point: same anatomy and states with your CSS. The styled package is a convenience, not a cage.',
339
211
  },
340
212
  {
341
- id: 'roadmap',
342
- question: 'What ships today?',
343
- answer:
344
- '14 headless primitives including Menu, Popover, Tooltip, and Toast; Panda themes; styled @aeon-ui/ui. See COMPONENTS.md for the full matrix.',
345
- example: 'pnpm dev → localhost:5173',
213
+ id: 'install',
214
+ question: 'How do I adopt it?',
215
+ answer: `Install ${npmPackage} from npm. Core, React, and styled UI ship together; import the layer that matches where you are in the stack.`,
216
+ example: npmInstallCommand,
346
217
  },
347
218
  ] as const
package/docs/MACHINES.md CHANGED
@@ -21,8 +21,13 @@
21
21
  | **Toast** (item) | `toastMachine` | `useAeonMachine` | visible / hidden |
22
22
  | **Select** | `popoverMachine` | `useAeonMachine` | `open` / `closed`; highlight in React |
23
23
  | **Combobox** | — | **`useState`** (open, query, highlight) | `open` / `closed` + highlight attrs |
24
- | **Tooltip** | — | **`useState(open)`** | `open` / `closed` |
24
+ | **Tooltip** | — | **`useState(open)`** | `open` / `closed`; touch tap toggle |
25
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 |
26
31
 
27
32
  ---
28
33
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aeon-ui-engine",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "build": "pnpm --filter @aeon-ui/panda run codegen && pnpm -r run build",
@@ -142,3 +142,26 @@ export const toastAnatomy = anatomy('toast', [
142
142
 
143
143
  /** Profile image — loaded / loading / error on root `data-aeon-state`. */
144
144
  export const avatarAnatomy = anatomy('avatar', ['root', 'image', 'fallback'] as const)
145
+
146
+ export const separatorAnatomy = anatomy('separator', ['root'] as const)
147
+
148
+ /** Progress bar — idle / loading / complete on root `data-aeon-state`. */
149
+ export const progressAnatomy = anatomy('progress', ['root', 'track', 'range', 'label'] as const)
150
+
151
+ export const radioGroupAnatomy = anatomy('radioGroup', [
152
+ 'root',
153
+ 'item',
154
+ 'itemControl',
155
+ 'itemIndicator',
156
+ 'itemLabel',
157
+ ] as const)
158
+
159
+ export const sliderAnatomy = anatomy('slider', [
160
+ 'root',
161
+ 'track',
162
+ 'range',
163
+ 'thumb',
164
+ 'valueText',
165
+ ] as const)
166
+
167
+ export const pinInputAnatomy = anatomy('pinInput', ['root', 'input'] as const)
@@ -13,6 +13,11 @@ import {
13
13
  dialogRecipe,
14
14
  switchRecipe,
15
15
  tabsRecipe,
16
+ separatorRecipe,
17
+ progressRecipe,
18
+ radioGroupRecipe,
19
+ sliderRecipe,
20
+ pinInputRecipe,
16
21
  menuRecipe,
17
22
  popoverRecipe,
18
23
  tooltipRecipe,
@@ -42,7 +47,7 @@ export default defineConfig({
42
47
  conditions: {
43
48
  extend: {
44
49
  aeonSelected: '&[data-aeon-state=selected]',
45
- aeonChecked: '&[data-aeon-state*=checked]',
50
+ aeonChecked: '&[data-aeon-state~="checked"]',
46
51
  aeonOpen: '&[data-aeon-state=open]',
47
52
  aeonClosed: '&[data-aeon-state=closed]',
48
53
  aeonPending: '&[data-aeon-state=pending]',
@@ -64,6 +69,11 @@ export default defineConfig({
64
69
  aeonAccordion: accordionRecipe,
65
70
  aeonDialog: dialogRecipe,
66
71
  aeonTabs: tabsRecipe,
72
+ aeonSeparator: separatorRecipe,
73
+ aeonProgress: progressRecipe,
74
+ aeonRadioGroup: radioGroupRecipe,
75
+ aeonSlider: sliderRecipe,
76
+ aeonPinInput: pinInputRecipe,
67
77
  aeonSwitch: switchRecipe,
68
78
  aeonCheckbox: checkboxRecipe,
69
79
  aeonAsync: asyncRegionRecipe,
@@ -225,5 +235,13 @@ export default defineConfig({
225
235
  opacity: '0.7',
226
236
  pointerEvents: 'none',
227
237
  },
238
+ '@keyframes aeonProgressIndeterminate': {
239
+ '0%': { transform: 'translateX(-100%)' },
240
+ '100%': { transform: 'translateX(250%)' },
241
+ },
242
+ '[data-aeon-scope="radioGroup"] [data-aeon-part="item"]': {
243
+ display: 'inline-flex',
244
+ alignItems: 'center',
245
+ },
228
246
  },
229
247
  })
@@ -59,28 +59,28 @@ export const buttonRecipe = defineRecipe({
59
59
  borderRadius: 'sm',
60
60
  py: 'controlYSm',
61
61
  px: 'controlXSm',
62
- minH: '1.75rem',
62
+ minH: 'controlMinHXs',
63
63
  },
64
64
  sm: {
65
65
  fontSize: '0.8125rem',
66
66
  borderRadius: 'sm',
67
67
  py: 'controlY',
68
68
  px: 'controlX',
69
- minH: '2rem',
69
+ minH: 'controlMinHSm',
70
70
  },
71
71
  md: {
72
72
  fontSize: '0.875rem',
73
73
  borderRadius: 'md',
74
74
  py: '0.4375rem',
75
75
  px: '0.875rem',
76
- minH: '2.25rem',
76
+ minH: 'controlMinHMd',
77
77
  },
78
78
  lg: {
79
79
  fontSize: '0.9375rem',
80
80
  borderRadius: 'md',
81
81
  py: 'insetYSm',
82
82
  px: 'insetXSm',
83
- minH: '2.5rem',
83
+ minH: 'controlMinHLg',
84
84
  },
85
85
  },
86
86
  },
@@ -16,7 +16,7 @@ export const comboboxRecipe = defineSlotRecipe({
16
16
  input: {
17
17
  display: 'block',
18
18
  width: '100%',
19
- minH: '2rem',
19
+ minH: 'controlMinHSm',
20
20
  py: 'controlY',
21
21
  pl: 'controlX',
22
22
  pr: '2rem',
@@ -11,4 +11,9 @@ export { checkboxRecipe } from './checkbox.js'
11
11
  export { dialogRecipe } from './dialog.js'
12
12
  export { switchRecipe } from './switch.js'
13
13
  export { tabsRecipe } from './tabs.js'
14
+ export { separatorRecipe } from './separator.js'
15
+ export { progressRecipe } from './progress.js'
16
+ export { radioGroupRecipe } from './radio-group.js'
17
+ export { sliderRecipe } from './slider.js'
18
+ export { pinInputRecipe } from './pin-input.js'
14
19
  export { menuRecipe, popoverRecipe, toastRecipe, tooltipRecipe } from './floating.js'
@@ -0,0 +1,29 @@
1
+ import { defineSlotRecipe } from '@pandacss/dev'
2
+
3
+ export const pinInputRecipe = defineSlotRecipe({
4
+ className: 'aeonPinInput',
5
+ slots: ['root', 'input'],
6
+ base: {
7
+ root: {
8
+ display: 'inline-flex',
9
+ gap: 'gapSm',
10
+ },
11
+ input: {
12
+ width: '2.5rem',
13
+ height: '2.75rem',
14
+ textAlign: 'center',
15
+ fontSize: 'lg',
16
+ fontFamily: 'ui',
17
+ fontWeight: 'semibold',
18
+ borderRadius: 'md',
19
+ borderWidth: '1px',
20
+ borderStyle: 'solid',
21
+ borderColor: 'border',
22
+ bg: 'surface',
23
+ color: 'fg',
24
+ outline: 'none',
25
+ _focusVisible: { boxShadow: 'focusRing', borderColor: 'accent' },
26
+ '&[data-aeon-state=filled]': { borderColor: 'accent' },
27
+ },
28
+ },
29
+ })