@easemate/web-kit 0.1.4 → 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.
Files changed (162) hide show
  1. package/README.md +360 -168
  2. package/build/components/code/index.cjs +3 -3
  3. package/build/components/code/index.js +3 -3
  4. package/build/components/curve/canvas-controls.cjs +3 -3
  5. package/build/components/curve/canvas-controls.js +3 -3
  6. package/build/components/curve/canvas.cjs +4 -4
  7. package/build/components/curve/canvas.js +4 -4
  8. package/build/components/curve/controls.cjs +6 -6
  9. package/build/components/curve/controls.d.cts +4 -4
  10. package/build/components/curve/controls.d.ts +4 -4
  11. package/build/components/curve/controls.js +6 -6
  12. package/build/components/curve/index.cjs +3 -3
  13. package/build/components/curve/index.d.cts +1 -1
  14. package/build/components/curve/index.d.ts +1 -1
  15. package/build/components/curve/index.js +3 -3
  16. package/build/components/curve/output.cjs +3 -3
  17. package/build/components/curve/output.d.cts +1 -1
  18. package/build/components/curve/output.d.ts +1 -1
  19. package/build/components/curve/output.js +3 -3
  20. package/build/components/curve/toolbar.cjs +7 -7
  21. package/build/components/curve/toolbar.d.cts +4 -4
  22. package/build/components/curve/toolbar.d.ts +4 -4
  23. package/build/components/curve/toolbar.js +7 -7
  24. package/build/decorators/OutsideClick.cjs +2 -2
  25. package/build/decorators/OutsideClick.d.cts +2 -2
  26. package/build/decorators/OutsideClick.d.ts +2 -2
  27. package/build/decorators/OutsideClick.js +2 -2
  28. package/build/elements/button/index.cjs +2 -2
  29. package/build/elements/button/index.js +2 -2
  30. package/build/elements/checkbox/index.cjs +4 -4
  31. package/build/elements/checkbox/index.js +4 -4
  32. package/build/elements/color/index.cjs +4 -4
  33. package/build/elements/color/index.d.cts +1 -1
  34. package/build/elements/color/index.d.ts +1 -1
  35. package/build/elements/color/index.js +4 -4
  36. package/build/elements/color/picker.cjs +4 -4
  37. package/build/elements/color/picker.js +4 -4
  38. package/build/elements/dropdown/index.cjs +4 -4
  39. package/build/elements/dropdown/index.d.cts +1 -1
  40. package/build/elements/dropdown/index.d.ts +1 -1
  41. package/build/elements/dropdown/index.js +4 -4
  42. package/build/elements/field/index.cjs +2 -2
  43. package/build/elements/field/index.js +2 -2
  44. package/build/elements/icons/animation/chevron.cjs +2 -2
  45. package/build/elements/icons/animation/chevron.js +2 -2
  46. package/build/elements/icons/animation/clear.cjs +1 -1
  47. package/build/elements/icons/animation/clear.js +1 -1
  48. package/build/elements/icons/animation/grid.cjs +2 -2
  49. package/build/elements/icons/animation/grid.js +2 -2
  50. package/build/elements/icons/animation/loading.cjs +1 -1
  51. package/build/elements/icons/animation/loading.js +1 -1
  52. package/build/elements/icons/animation/snap.cjs +2 -2
  53. package/build/elements/icons/animation/snap.js +2 -2
  54. package/build/elements/icons/interface/anchor-add.cjs +1 -1
  55. package/build/elements/icons/interface/anchor-add.js +1 -1
  56. package/build/elements/icons/interface/anchor-remove.cjs +1 -1
  57. package/build/elements/icons/interface/anchor-remove.js +1 -1
  58. package/build/elements/icons/interface/arrow-up.cjs +1 -1
  59. package/build/elements/icons/interface/arrow-up.js +1 -1
  60. package/build/elements/icons/interface/arrows-vertical.cjs +1 -1
  61. package/build/elements/icons/interface/arrows-vertical.js +1 -1
  62. package/build/elements/icons/interface/bezier-angle.cjs +1 -1
  63. package/build/elements/icons/interface/bezier-angle.js +1 -1
  64. package/build/elements/icons/interface/bezier-distribute.cjs +1 -1
  65. package/build/elements/icons/interface/bezier-distribute.js +1 -1
  66. package/build/elements/icons/interface/bezier-length.cjs +1 -1
  67. package/build/elements/icons/interface/bezier-length.js +1 -1
  68. package/build/elements/icons/interface/bezier-mirror.cjs +1 -1
  69. package/build/elements/icons/interface/bezier-mirror.js +1 -1
  70. package/build/elements/icons/interface/bezier.cjs +1 -1
  71. package/build/elements/icons/interface/bezier.js +1 -1
  72. package/build/elements/icons/interface/check.cjs +1 -1
  73. package/build/elements/icons/interface/check.js +1 -1
  74. package/build/elements/icons/interface/circle-arrow-left.cjs +1 -1
  75. package/build/elements/icons/interface/circle-arrow-left.js +1 -1
  76. package/build/elements/icons/interface/circle-arrow-right.cjs +1 -1
  77. package/build/elements/icons/interface/circle-arrow-right.js +1 -1
  78. package/build/elements/icons/interface/code.cjs +1 -1
  79. package/build/elements/icons/interface/code.js +1 -1
  80. package/build/elements/icons/interface/dots.cjs +1 -1
  81. package/build/elements/icons/interface/dots.js +1 -1
  82. package/build/elements/icons/interface/mention.cjs +1 -1
  83. package/build/elements/icons/interface/mention.js +1 -1
  84. package/build/elements/icons/interface/minus.cjs +1 -1
  85. package/build/elements/icons/interface/minus.js +1 -1
  86. package/build/elements/icons/interface/picker.cjs +1 -1
  87. package/build/elements/icons/interface/picker.js +1 -1
  88. package/build/elements/icons/interface/plus.cjs +1 -1
  89. package/build/elements/icons/interface/plus.js +1 -1
  90. package/build/elements/icons/interface/settings.cjs +1 -1
  91. package/build/elements/icons/interface/settings.js +1 -1
  92. package/build/elements/index.cjs +5 -2
  93. package/build/elements/index.d.cts +2 -1
  94. package/build/elements/index.d.ts +2 -1
  95. package/build/elements/index.js +2 -1
  96. package/build/elements/input/index.cjs +4 -4
  97. package/build/elements/input/index.js +4 -4
  98. package/build/elements/logo/index.cjs +2 -2
  99. package/build/elements/logo/index.js +2 -2
  100. package/build/elements/monitor/fps.cjs +3 -3
  101. package/build/elements/monitor/fps.js +3 -3
  102. package/build/elements/monitor/index.cjs +4 -4
  103. package/build/elements/monitor/index.js +4 -4
  104. package/build/elements/number/index.cjs +4 -4
  105. package/build/elements/number/index.js +4 -4
  106. package/build/elements/origin/index.cjs +4 -4
  107. package/build/elements/origin/index.js +4 -4
  108. package/build/elements/panel/index.cjs +496 -0
  109. package/build/elements/panel/index.d.cts +67 -0
  110. package/build/elements/panel/index.d.ts +67 -0
  111. package/build/elements/panel/index.js +492 -0
  112. package/build/elements/popover/index.cjs +2 -2
  113. package/build/elements/popover/index.js +2 -2
  114. package/build/elements/radio/index.cjs +3 -3
  115. package/build/elements/radio/index.js +3 -3
  116. package/build/elements/radio/input.cjs +4 -4
  117. package/build/elements/radio/input.js +4 -4
  118. package/build/elements/slider/index.cjs +4 -4
  119. package/build/elements/slider/index.js +4 -4
  120. package/build/elements/state/index.cjs +61 -468
  121. package/build/elements/state/index.d.cts +34 -25
  122. package/build/elements/state/index.d.ts +34 -25
  123. package/build/elements/state/index.js +63 -470
  124. package/build/elements/toggle/index.cjs +4 -4
  125. package/build/elements/toggle/index.js +4 -4
  126. package/build/elements/tooltip/index.cjs +4 -4
  127. package/build/elements/tooltip/index.d.cts +1 -1
  128. package/build/elements/tooltip/index.d.ts +1 -1
  129. package/build/elements/tooltip/index.js +4 -4
  130. package/build/internal/component-loaders.cjs +2 -0
  131. package/build/internal/component-loaders.d.cts +2 -2
  132. package/build/internal/component-loaders.d.ts +2 -2
  133. package/build/internal/component-loaders.js +2 -0
  134. package/build/react/events.cjs +25 -0
  135. package/build/react/events.d.cts +39 -0
  136. package/build/react/events.d.ts +39 -0
  137. package/build/react/events.js +22 -0
  138. package/build/react/index.cjs +19 -0
  139. package/build/react/index.d.cts +13 -0
  140. package/build/react/index.d.ts +13 -0
  141. package/build/react/index.js +12 -0
  142. package/build/react/provider.cjs +134 -0
  143. package/build/react/provider.d.cts +81 -0
  144. package/build/react/provider.d.ts +81 -0
  145. package/build/react/provider.js +98 -0
  146. package/build/react/types.cjs +8 -0
  147. package/build/react/types.d.cts +55 -0
  148. package/build/react/types.d.ts +55 -0
  149. package/build/react/types.js +7 -0
  150. package/build/react/use-ease-state.cjs +129 -0
  151. package/build/react/use-ease-state.d.cts +95 -0
  152. package/build/react/use-ease-state.d.ts +95 -0
  153. package/build/react/use-ease-state.js +126 -0
  154. package/build/react/use-web-kit.cjs +150 -0
  155. package/build/react/use-web-kit.d.cts +80 -0
  156. package/build/react/use-web-kit.d.ts +80 -0
  157. package/build/react/use-web-kit.js +114 -0
  158. package/build/register.cjs +1 -0
  159. package/build/register.d.cts +1 -0
  160. package/build/register.d.ts +1 -0
  161. package/build/register.js +1 -0
  162. package/package.json +16 -2
package/README.md CHANGED
@@ -1,7 +1,22 @@
1
- # @easemate/web-kit
1
+ <h1>@easemate/web-kit</h1>
2
+
3
+ <div align="center">
4
+ <img src="https://easemate.app/easemate-web-kit-header.png" alt="@easemate/web-kit" />
5
+ </div>
6
+
7
+ <br />
2
8
 
3
9
  A modern, framework-agnostic UI kit of web components for building animation control panels.
4
10
 
11
+ <div>
12
+
13
+ [![npm version](https://img.shields.io/npm/v/@easemate/web-kit.svg?style=flat-square)](https://www.npmjs.com/package/@easemate/web-kit)
14
+ ![npm package minimized gzipped size](https://img.shields.io/bundlejs/size/%40easemate%2Fweb-kit?format=minzip)
15
+ [![npm downloads](https://img.shields.io/npm/dm/@easemate/web-kit.svg?style=flat-square)](https://www.npmjs.com/package/@easemate/web-kit)
16
+ [![license](https://img.shields.io/npm/l/@easemate/web-kit.svg?style=flat-square)](https://github.com/easemate/web-kit/blob/main/LICENSE)
17
+
18
+ </div>
19
+
5
20
  ## Table of Contents
6
21
 
7
22
  - [Features](#features)
@@ -10,6 +25,11 @@ A modern, framework-agnostic UI kit of web components for building animation con
10
25
  - [Basic Usage](#basic-usage)
11
26
  - [Selective Loading](#selective-loading)
12
27
  - [Theme Switching](#theme-switching)
28
+ - [React & Next.js](#react--nextjs)
29
+ - [Basic Setup](#basic-setup)
30
+ - [Next.js App Router](#nextjs-app-router)
31
+ - [useEaseState Hook](#useeasestate-hook)
32
+ - [WebKit Provider](#webkit-provider)
13
33
  - [Components](#components)
14
34
  - [Controls](#controls)
15
35
  - [Layout & Display](#layout--display)
@@ -17,13 +37,10 @@ A modern, framework-agnostic UI kit of web components for building animation con
17
37
  - [Icons](#icons)
18
38
  - [Usage Examples](#usage-examples)
19
39
  - [Basic Controls](#basic-controls)
20
- - [State Panel](#state-panel)
21
- - [Header Actions](#header-actions)
22
- - [Tabs](#tabs)
23
- - [Tabs with Actions](#tabs-with-actions)
24
- - [Footer](#footer)
40
+ - [Panel Component](#panel-component)
41
+ - [State Component](#state-component)
42
+ - [Combined Panel + State](#combined-panel--state)
25
43
  - [JavaScript Integration](#javascript-integration)
26
- - [Tab Control](#tab-control)
27
44
  - [Event Handling](#event-handling)
28
45
  - [Configuration](#configuration)
29
46
  - [initWebKit Options](#initwebkit-options)
@@ -38,7 +55,8 @@ A modern, framework-agnostic UI kit of web components for building animation con
38
55
  - [API Reference](#api-reference)
39
56
  - [Controller API](#controller-api)
40
57
  - [Package Exports](#package-exports)
41
- - [State Panel API](#state-panel-api)
58
+ - [Panel API](#panel-api)
59
+ - [State API](#state-api)
42
60
  - [Accessibility](#accessibility)
43
61
  - [SSR Support](#ssr-support)
44
62
  - [License](#license)
@@ -47,15 +65,17 @@ A modern, framework-agnostic UI kit of web components for building animation con
47
65
 
48
66
  ## Features
49
67
 
50
- - 🎨 **Rich Component Library** Sliders, toggles, color pickers, dropdowns, curve editors, and more
51
- - 🌙 **Dark Theme by Default** Beautiful dark UI with OKLAB color palette
52
- - 🔌 **Framework Agnostic** Works with vanilla JS, React, Vue, Svelte, or any framework
53
- - 📦 **Tree-Shakeable** Import only what you need
54
- - 🎯 **TypeScript First** Full type definitions included
55
- - **Accessible** ARIA attributes and keyboard navigation
56
- - 🎭 **Customizable** CSS custom properties and `::part` selectors for styling
57
- - 📡 **State Aggregation** Control panel state management with `<ease-state>`
58
- - 🚀 **No CSS Import Required** `initWebKit()` handles everything programmatically
68
+ - **Rich Component Library** - Sliders, toggles, color pickers, dropdowns, curve editors, and more
69
+ - **Dark Theme by Default** - Beautiful dark UI with OKLAB color palette
70
+ - **Framework Agnostic** - Works with vanilla JS, React, Vue, Svelte, or any framework
71
+ - **React/Next.js Ready** - First-class React integration with hooks and SSR support
72
+ - **Tree-Shakeable** - Import only what you need
73
+ - **TypeScript First** - Full type definitions included
74
+ - **Accessible** - ARIA attributes and keyboard navigation
75
+ - **Customizable** - CSS custom properties and `::part` selectors for styling
76
+ - **State Aggregation** - Control panel state management with `<ease-state>`
77
+ - **Flexible Layout** - Separate `<ease-panel>` and `<ease-state>` for maximum flexibility
78
+ - **No CSS Import Required** - `initWebKit()` handles everything programmatically
59
79
 
60
80
  ---
61
81
 
@@ -153,6 +173,156 @@ kit.theme?.set('light');
153
173
 
154
174
  ---
155
175
 
176
+ ## React & Next.js
177
+
178
+ The library provides first-class React integration via `@easemate/web-kit/react`.
179
+
180
+ ### Basic Setup
181
+
182
+ ```tsx
183
+ // app/providers.tsx
184
+ 'use client';
185
+
186
+ import { useEffect, useState, useRef } from 'react';
187
+ import { initWebKit, type WebKitController } from '@easemate/web-kit';
188
+
189
+ export function Providers({ children }: { children: React.ReactNode }) {
190
+ const [ready, setReady] = useState(false);
191
+ const controllerRef = useRef<WebKitController | null>(null);
192
+
193
+ useEffect(() => {
194
+ const controller = initWebKit({
195
+ theme: 'default',
196
+ styles: 'main',
197
+ fonts: 'default'
198
+ });
199
+ controllerRef.current = controller;
200
+ controller.ready.then(() => setReady(true));
201
+
202
+ return () => controller.dispose();
203
+ }, []);
204
+
205
+ return <>{children}</>;
206
+ }
207
+ ```
208
+
209
+ ### Next.js App Router
210
+
211
+ For Next.js 13+ with App Router, create a client component wrapper:
212
+
213
+ ```tsx
214
+ // app/layout.tsx
215
+ import { Providers } from './providers';
216
+
217
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
218
+ return (
219
+ <html lang="en">
220
+ <body>
221
+ <Providers>{children}</Providers>
222
+ </body>
223
+ </html>
224
+ );
225
+ }
226
+ ```
227
+
228
+ ### useEaseState Hook
229
+
230
+ The `useEaseState` hook provides reactive state management for controls:
231
+
232
+ ```tsx
233
+ 'use client';
234
+
235
+ import { useState, useCallback, useRef } from 'react';
236
+ import { useEaseState } from '@easemate/web-kit/react';
237
+
238
+ interface AnimationState {
239
+ duration: number;
240
+ easing: string;
241
+ loop: boolean;
242
+ }
243
+
244
+ function AnimationControls() {
245
+ const { stateRef, panelRef, state, set, reset } = useEaseState<AnimationState>(
246
+ {
247
+ onChange: ({ name, value }) => {
248
+ console.log(`${name} changed to ${value}`);
249
+ },
250
+ onTabChange: ({ index }) => {
251
+ console.log(`Switched to tab ${index}`);
252
+ }
253
+ },
254
+ { useState, useCallback, useRef }
255
+ );
256
+
257
+ return (
258
+ <ease-panel ref={panelRef}>
259
+ <span slot="headline">Animation</span>
260
+ <ease-state ref={stateRef}>
261
+ <ease-field label="Duration">
262
+ <ease-slider name="duration" value="1" min="0" max="5" step="0.1" />
263
+ </ease-field>
264
+ <ease-field label="Easing">
265
+ <ease-dropdown name="easing" value="ease-out">
266
+ <button slot="content" value="linear">Linear</button>
267
+ <button slot="content" value="ease-out">Ease Out</button>
268
+ </ease-dropdown>
269
+ </ease-field>
270
+ <ease-field label="Loop">
271
+ <ease-toggle name="loop" />
272
+ </ease-field>
273
+ </ease-state>
274
+ <div slot="footer">
275
+ <ease-button onClick={() => reset()}>Reset</ease-button>
276
+ </div>
277
+ </ease-panel>
278
+ );
279
+ }
280
+ ```
281
+
282
+ ### WebKit Provider
283
+
284
+ For more complex apps, use `createWebKitProvider` to create a context:
285
+
286
+ ```tsx
287
+ // providers.tsx
288
+ 'use client';
289
+
290
+ import * as React from 'react';
291
+ import { createWebKitProvider } from '@easemate/web-kit/react';
292
+
293
+ const { WebKitProvider, useWebKitContext } = createWebKitProvider(React);
294
+
295
+ export { WebKitProvider, useWebKitContext };
296
+ ```
297
+
298
+ ```tsx
299
+ // layout.tsx
300
+ import { WebKitProvider } from './providers';
301
+
302
+ export default function Layout({ children }: { children: React.ReactNode }) {
303
+ return (
304
+ <WebKitProvider options={{ theme: 'default', styles: 'main', fonts: 'default' }}>
305
+ {children}
306
+ </WebKitProvider>
307
+ );
308
+ }
309
+ ```
310
+
311
+ ```tsx
312
+ // component.tsx
313
+ import { useWebKitContext } from './providers';
314
+
315
+ function MyComponent() {
316
+ const { ready, theme } = useWebKitContext();
317
+
318
+ if (!ready) return <div>Loading...</div>;
319
+
320
+ return <ease-slider name="value" value="0.5" />;
321
+ }
322
+ ```
323
+
324
+ ---
325
+
156
326
  ## Components
157
327
 
158
328
  ### Controls
@@ -175,7 +345,8 @@ kit.theme?.set('light');
175
345
 
176
346
  | Component | Tag | Description |
177
347
  |-----------|-----|-------------|
178
- | State | `<ease-state>` | State aggregation panel |
348
+ | Panel | `<ease-panel>` | Visual container with tabs, header, and footer |
349
+ | State | `<ease-state>` | State aggregator for controls (no visual styling) |
179
350
  | Field | `<ease-field>` | Label + control wrapper |
180
351
  | Button | `<ease-button>` | Action button |
181
352
  | Tooltip | `<ease-tooltip>` | Tooltip wrapper |
@@ -215,163 +386,143 @@ All icon components follow the pattern `<ease-icon-*>`:
215
386
  <ease-number-input name="count" value="42" min="0" max="100"></ease-number-input>
216
387
  ```
217
388
 
218
- ### State Panel
389
+ ### Panel Component
219
390
 
220
- Basic panel with headline and controls:
391
+ The `<ease-panel>` component provides the visual container with optional tabs, header actions, and footer. It does NOT manage state - use `<ease-state>` for that.
221
392
 
222
393
  ```html
223
- <ease-state>
224
- <span slot="headline">Animation Controls</span>
225
- <div slot="entry">
226
- <ease-field label="Duration">
227
- <ease-slider name="duration" value="1" min="0" max="5" step="0.1"></ease-slider>
228
- </ease-field>
229
- <ease-field label="Easing">
230
- <ease-dropdown name="easing" value="ease-out">
231
- <button slot="content" value="linear">Linear</button>
232
- <button slot="content" value="ease-in">Ease In</button>
233
- <button slot="content" value="ease-out">Ease Out</button>
234
- <button slot="content" value="ease-in-out">Ease In-Out</button>
235
- </ease-dropdown>
236
- </ease-field>
237
- <ease-field label="Loop">
238
- <ease-toggle name="loop"></ease-toggle>
239
- </ease-field>
394
+ <!-- Simple panel with headline -->
395
+ <ease-panel>
396
+ <span slot="headline">Settings</span>
397
+ <div>
398
+ <!-- Your content here -->
240
399
  </div>
241
- </ease-state>
242
- ```
243
-
244
- #### Header Actions
400
+ </ease-panel>
245
401
 
246
- Add action buttons, links, or dropdowns to the panel header using the `actions` slot:
402
+ <!-- Panel with tabs -->
403
+ <ease-panel active-tab="0">
404
+ <div slot="tab-general" data-tab-label="General">
405
+ <!-- General settings -->
406
+ </div>
407
+ <div slot="tab-advanced" data-tab-label="Advanced">
408
+ <!-- Advanced settings -->
409
+ </div>
410
+ </ease-panel>
247
411
 
248
- ```html
249
- <ease-state>
250
- <span slot="headline">Settings</span>
251
-
252
- <!-- Action buttons -->
412
+ <!-- Panel with header actions -->
413
+ <ease-panel>
414
+ <span slot="headline">Controls</span>
253
415
  <button slot="actions" title="Settings">
254
416
  <ease-icon-settings></ease-icon-settings>
255
417
  </button>
256
- <a slot="actions" href="/docs" title="Documentation">
257
- <ease-icon-code></ease-icon-code>
258
- </a>
259
-
260
- <!-- Dropdown menu in actions -->
261
- <ease-dropdown slot="actions">
262
- <ease-icon-dots slot="trigger"></ease-icon-dots>
263
- <button slot="content" value="export">Export</button>
264
- <button slot="content" value="import">Import</button>
265
- <button slot="content" value="reset">Reset</button>
266
- </ease-dropdown>
267
-
268
- <div slot="entry">
269
- <!-- controls -->
418
+ <div>
419
+ <!-- Content -->
270
420
  </div>
271
- </ease-state>
421
+ <div slot="footer">
422
+ <ease-button>Save</ease-button>
423
+ </div>
424
+ </ease-panel>
272
425
  ```
273
426
 
274
- Action elements are automatically styled with hover states and proper spacing. Supported elements:
275
- - `<button>` — Action button with icon
276
- - `<a>` — Link with icon
277
- - `<ease-dropdown>` — Dropdown menu (auto-positioned to bottom-end)
427
+ ### State Component
278
428
 
279
- #### Tabs
429
+ The `<ease-state>` component aggregates state from child controls. It can be used standalone (no panel styling) or inside a panel.
280
430
 
281
- Organize controls into tabbed sections (maximum 3 tabs). When tabs are present, the headline is automatically hidden.
431
+ ```html
432
+ <!-- Standalone state (no panel) - useful for embedding in your own UI -->
433
+ <ease-state>
434
+ <ease-field label="Duration">
435
+ <ease-slider name="duration" value="1" min="0" max="5" step="0.1"></ease-slider>
436
+ </ease-field>
437
+ <ease-field label="Easing">
438
+ <ease-dropdown name="easing" value="ease-out">
439
+ <button slot="content" value="linear">Linear</button>
440
+ <button slot="content" value="ease-out">Ease Out</button>
441
+ </ease-dropdown>
442
+ </ease-field>
443
+ <ease-field label="Loop">
444
+ <ease-toggle name="loop"></ease-toggle>
445
+ </ease-field>
446
+ </ease-state>
447
+ ```
448
+
449
+ ### Combined Panel + State
450
+
451
+ For a complete control panel experience, combine `<ease-panel>` with `<ease-state>`:
282
452
 
283
453
  ```html
284
- <ease-state active-tab="0">
285
- <!-- Tab content uses slot="tab-{id}" pattern -->
286
- <!-- Tab label comes from data-tab-label attribute -->
287
-
288
- <div slot="tab-transform" data-tab-label="Transform">
289
- <ease-field label="X">
290
- <ease-number-input name="x" value="0"></ease-number-input>
291
- </ease-field>
292
- <ease-field label="Y">
293
- <ease-number-input name="y" value="0"></ease-number-input>
294
- </ease-field>
295
- <ease-field label="Rotation">
296
- <ease-slider name="rotation" value="0" min="0" max="360"></ease-slider>
297
- </ease-field>
298
- </div>
454
+ <ease-panel>
455
+ <span slot="headline">Animation Controls</span>
299
456
 
300
- <div slot="tab-style" data-tab-label="Style">
301
- <ease-field label="Opacity">
302
- <ease-slider name="opacity" value="1" min="0" max="1" step="0.01"></ease-slider>
303
- </ease-field>
304
- <ease-field label="Color">
305
- <ease-color-input name="color" value="#3b82f6"></ease-color-input>
306
- </ease-field>
307
- </div>
457
+ <button slot="actions" title="Reset">
458
+ <ease-icon-minus></ease-icon-minus>
459
+ </button>
308
460
 
309
- <div slot="tab-animation" data-tab-label="Animation">
461
+ <ease-state>
310
462
  <ease-field label="Duration">
311
463
  <ease-slider name="duration" value="1" min="0" max="5" step="0.1"></ease-slider>
312
464
  </ease-field>
313
- <ease-field label="Delay">
314
- <ease-slider name="delay" value="0" min="0" max="2" step="0.1"></ease-slider>
465
+ <ease-field label="Easing">
466
+ <ease-dropdown name="easing" value="ease-out">
467
+ <button slot="content" value="linear">Linear</button>
468
+ <button slot="content" value="ease-in">Ease In</button>
469
+ <button slot="content" value="ease-out">Ease Out</button>
470
+ <button slot="content" value="ease-in-out">Ease In-Out</button>
471
+ </ease-dropdown>
315
472
  </ease-field>
473
+ <ease-field label="Loop">
474
+ <ease-toggle name="loop"></ease-toggle>
475
+ </ease-field>
476
+ </ease-state>
477
+
478
+ <div slot="footer">
479
+ <ease-button>Apply</ease-button>
480
+ <ease-button variant="secondary">Cancel</ease-button>
316
481
  </div>
317
- </ease-state>
482
+ </ease-panel>
318
483
  ```
319
484
 
320
- **Tab Attributes:**
485
+ #### Panel with Tabs + State
321
486
 
322
- | Attribute | Description |
323
- |-----------|-------------|
324
- | `slot="tab-{id}"` | Assigns content to a tab. The `id` is used internally and for events. |
325
- | `data-tab-label` | Display label for the tab button. Falls back to `id` if not provided. |
326
- | `active-tab` | (on `<ease-state>`) Zero-based index of the initially active tab. |
327
-
328
- **Tab Behavior:**
329
- - Tabs are detected automatically from slotted elements with `slot="tab-*"` pattern
330
- - Maximum of 3 tabs supported
331
- - Switching tabs triggers a smooth crossfade animation with height transition
332
- - Keyboard navigation: Arrow keys, Home, End
333
- - State is tracked per-tab (only active tab's controls are in the state object)
334
-
335
- #### Tabs with Actions
336
-
337
- Combine tabs and header actions:
487
+ When using tabs with state, place the `<ease-state>` inside each tab:
338
488
 
339
489
  ```html
340
- <ease-state active-tab="0">
341
- <!-- Actions appear to the right of tabs -->
490
+ <ease-panel active-tab="0">
342
491
  <button slot="actions" title="Reset">
343
492
  <ease-icon-minus></ease-icon-minus>
344
493
  </button>
345
494
 
346
- <div slot="tab-basic" data-tab-label="Basic">
347
- <!-- controls -->
348
- </div>
349
- <div slot="tab-advanced" data-tab-label="Advanced">
350
- <!-- controls -->
351
- </div>
352
- </ease-state>
353
- ```
354
-
355
- #### Footer
356
-
357
- Add footer content that appears below all tab panels:
358
-
359
- ```html
360
- <ease-state>
361
- <span slot="headline">Controls</span>
362
- <div slot="entry">
363
- <!-- controls -->
495
+ <div slot="tab-transform" data-tab-label="Transform">
496
+ <ease-state>
497
+ <ease-field label="X">
498
+ <ease-number-input name="x" value="0"></ease-number-input>
499
+ </ease-field>
500
+ <ease-field label="Y">
501
+ <ease-number-input name="y" value="0"></ease-number-input>
502
+ </ease-field>
503
+ <ease-field label="Rotation">
504
+ <ease-slider name="rotation" value="0" min="0" max="360"></ease-slider>
505
+ </ease-field>
506
+ </ease-state>
364
507
  </div>
365
- <div slot="footer">
366
- <ease-button>Apply</ease-button>
367
- <ease-button variant="secondary">Cancel</ease-button>
508
+
509
+ <div slot="tab-style" data-tab-label="Style">
510
+ <ease-state>
511
+ <ease-field label="Opacity">
512
+ <ease-slider name="opacity" value="1" min="0" max="1" step="0.01"></ease-slider>
513
+ </ease-field>
514
+ <ease-field label="Color">
515
+ <ease-color-input name="color" value="#3b82f6"></ease-color-input>
516
+ </ease-field>
517
+ </ease-state>
368
518
  </div>
369
- </ease-state>
519
+ </ease-panel>
370
520
  ```
371
521
 
372
522
  ### JavaScript Integration
373
523
 
374
524
  ```typescript
525
+ // Working with ease-state
375
526
  const state = document.querySelector('ease-state');
376
527
 
377
528
  // Get current state
@@ -400,19 +551,18 @@ state.reset();
400
551
  sub.unsubscribe();
401
552
  ```
402
553
 
403
- #### Tab Control
404
-
405
554
  ```typescript
406
- const state = document.querySelector('ease-state');
555
+ // Working with ease-panel
556
+ const panel = document.querySelector('ease-panel');
407
557
 
408
558
  // Get current active tab index
409
- console.log(state.activeTab); // 0
559
+ console.log(panel.activeTab); // 0
410
560
 
411
561
  // Switch to a specific tab programmatically
412
- state.setTab(1); // Switch to second tab (0-indexed)
562
+ panel.setTab(1); // Switch to second tab (0-indexed)
413
563
 
414
564
  // Or set directly via property
415
- state.activeTab = 2;
565
+ panel.activeTab = 2;
416
566
  ```
417
567
 
418
568
  ### Logo Loader
@@ -497,8 +647,8 @@ state.addEventListener('state-change', (e: CustomEvent) => {
497
647
  console.log('Full state:', state);
498
648
  });
499
649
 
500
- // Tab change event
501
- state.addEventListener('tab-change', (e: CustomEvent) => {
650
+ // Panel tab change event
651
+ panel.addEventListener('tab-change', (e: CustomEvent) => {
502
652
  const { index, id, event } = e.detail;
503
653
  console.log(`Switched to tab ${id} (index: ${index})`);
504
654
  });
@@ -506,11 +656,11 @@ state.addEventListener('tab-change', (e: CustomEvent) => {
506
656
 
507
657
  #### Event Types
508
658
 
509
- | Event | Detail | Description |
510
- |-------|--------|-------------|
511
- | `control-change` | `{ name, value, event }` | Fired by individual controls when value changes |
512
- | `state-change` | `{ name, value, state, event }` | Fired by `<ease-state>` when any control changes |
513
- | `tab-change` | `{ index, id, event }` | Fired by `<ease-state>` when active tab changes |
659
+ | Event | Component | Detail | Description |
660
+ |-------|-----------|--------|-------------|
661
+ | `control-change` | Controls | `{ name, value, event }` | Fired when value changes |
662
+ | `state-change` | `<ease-state>` | `{ name, value, state, event }` | Fired when any control changes |
663
+ | `tab-change` | `<ease-panel>` | `{ index, id, event }` | Fired when active tab changes |
514
664
 
515
665
  ---
516
666
 
@@ -687,7 +837,7 @@ setThemeName('light', { colorScheme: 'light' });
687
837
  | Panel Tabs | `--ease-panel-tab-font-size`, `--ease-panel-tab-font-weight`, `--ease-panel-tab-line-height`, `--ease-panel-tab-color`, `--ease-panel-tab-color-hover`, `--ease-panel-tab-color-active`, `--ease-panel-tab-background-active`, `--ease-panel-tab-radius` |
688
838
  | Panel Actions | `--ease-panel-action-icon-size` |
689
839
  | Panel Footer | `--ease-panel-footer-padding` |
690
- | State Transitions | `--ease-state-transition-duration`, `--ease-state-transition-easing` |
840
+ | Panel Transitions | `--ease-panel-transition-duration`, `--ease-panel-transition-easing` |
691
841
  | Field | `--ease-field-label-width`, `--ease-field-column-gap`, `--ease-field-row-gap` |
692
842
  | Controls | Each control exposes `--ease-<component>-*` tokens |
693
843
 
@@ -715,6 +865,7 @@ interface WebKitController {
715
865
  | Export | Description |
716
866
  |--------|-------------|
717
867
  | `@easemate/web-kit` | Main entry (initWebKit + theme + types) |
868
+ | `@easemate/web-kit/react` | React hooks and utilities |
718
869
  | `@easemate/web-kit/register` | Side-effect registration (all components) |
719
870
  | `@easemate/web-kit/elements` | UI components only |
720
871
  | `@easemate/web-kit/decorators` | Component decorators |
@@ -722,27 +873,20 @@ interface WebKitController {
722
873
  | `@easemate/web-kit/utils` | Utility functions |
723
874
  | `@easemate/web-kit/styles/*` | CSS assets (optional) |
724
875
 
725
- ### State Panel API
876
+ ### Panel API
726
877
 
727
- The `<ease-state>` component provides a complete API for state management.
878
+ The `<ease-panel>` component provides the visual container.
728
879
 
729
880
  #### Properties
730
881
 
731
882
  | Property | Type | Default | Description |
732
883
  |----------|------|---------|-------------|
733
- | `value` | `string \| null` | `null` | Legacy: reflects the last changed control's value |
734
884
  | `activeTab` | `number` | `0` | Zero-based index of the active tab |
735
- | `state` | `Record<string, unknown>` | `{}` | Read-only object containing all control values |
736
885
 
737
886
  #### Methods
738
887
 
739
888
  | Method | Signature | Description |
740
889
  |--------|-----------|-------------|
741
- | `get` | `(name: string) => unknown` | Get a specific control value by name |
742
- | `set` | `(name: string, value: unknown) => void` | Set a control value programmatically |
743
- | `subscribe` | `(callback: (value, name) => void) => { unsubscribe }` | Subscribe to all state changes |
744
- | `subscribe` | `(name: string, callback: (value, name) => void) => { unsubscribe }` | Subscribe to a specific control |
745
- | `reset` | `() => void` | Reset all controls to their initial values |
746
890
  | `setTab` | `(index: number) => void` | Switch to a specific tab by index |
747
891
 
748
892
  #### Slots
@@ -751,7 +895,7 @@ The `<ease-state>` component provides a complete API for state management.
751
895
  |------|-------------|
752
896
  | `headline` | Panel title text (hidden when tabs are present) |
753
897
  | `actions` | Header action buttons, links, or dropdowns |
754
- | `entry` | Main content area (used when no tabs) |
898
+ | (default) | Main content area (used when no tabs) |
755
899
  | `tab-{id}` | Tab panel content (use `data-tab-label` for display name) |
756
900
  | `footer` | Footer content below all panels |
757
901
 
@@ -766,7 +910,7 @@ The `<ease-state>` component provides a complete API for state management.
766
910
  | `tab` | Individual tab button |
767
911
  | `actions` | Actions container |
768
912
  | `content` | Content wrapper (handles height animations) |
769
- | `form` | Inner form container |
913
+ | `body` | Inner body container |
770
914
  | `tab-panel` | Individual tab panel |
771
915
  | `footer` | Footer container |
772
916
 
@@ -774,9 +918,55 @@ The `<ease-state>` component provides a complete API for state management.
774
918
 
775
919
  | Event | Detail Type | Description |
776
920
  |-------|-------------|-------------|
777
- | `state-change` | `StateChangeEventDetail` | Fired when any control value changes |
778
921
  | `tab-change` | `TabChangeEventDetail` | Fired when the active tab changes |
779
922
 
923
+ ```typescript
924
+ interface TabChangeEventDetail {
925
+ index: number; // Tab index (0-based)
926
+ id: string; // Tab id from slot name
927
+ event: Event; // Original event
928
+ }
929
+ ```
930
+
931
+ ### State API
932
+
933
+ The `<ease-state>` component provides state management for controls.
934
+
935
+ #### Properties
936
+
937
+ | Property | Type | Default | Description |
938
+ |----------|------|---------|-------------|
939
+ | `value` | `string \| null` | `null` | Reflects the last changed control's value |
940
+ | `state` | `Record<string, unknown>` | `{}` | Read-only object containing all control values |
941
+
942
+ #### Methods
943
+
944
+ | Method | Signature | Description |
945
+ |--------|-----------|-------------|
946
+ | `get` | `(name: string) => unknown` | Get a specific control value by name |
947
+ | `set` | `(name: string, value: unknown) => void` | Set a control value programmatically |
948
+ | `subscribe` | `(callback: (value, name) => void) => { unsubscribe }` | Subscribe to all state changes |
949
+ | `subscribe` | `(name: string, callback: (value, name) => void) => { unsubscribe }` | Subscribe to a specific control |
950
+ | `reset` | `() => void` | Reset all controls to their initial values |
951
+
952
+ #### Slots
953
+
954
+ | Slot | Description |
955
+ |------|-------------|
956
+ | (default) | Controls to aggregate state from |
957
+
958
+ #### CSS Parts
959
+
960
+ | Part | Description |
961
+ |------|-------------|
962
+ | `container` | Inner container wrapping controls |
963
+
964
+ #### Events
965
+
966
+ | Event | Detail Type | Description |
967
+ |-------|-------------|-------------|
968
+ | `state-change` | `StateChangeEventDetail` | Fired when any control value changes |
969
+
780
970
  ```typescript
781
971
  interface StateChangeEventDetail {
782
972
  name: string; // Control name
@@ -784,12 +974,6 @@ interface StateChangeEventDetail {
784
974
  state: Record<string, unknown>; // Complete state object
785
975
  event: Event; // Original event
786
976
  }
787
-
788
- interface TabChangeEventDetail {
789
- index: number; // Tab index (0-based)
790
- id: string; // Tab id from slot name
791
- event: Event; // Original event
792
- }
793
977
  ```
794
978
 
795
979
  ---
@@ -817,6 +1001,14 @@ const kit = initWebKit({ theme: 'default' });
817
1001
  await kit.ready; // Resolves immediately on server
818
1002
  ```
819
1003
 
1004
+ For React/Next.js, all hooks check for browser environment:
1005
+
1006
+ ```tsx
1007
+ // This is safe to call on the server
1008
+ const { ready, theme } = useWebKitContext();
1009
+ // ready will be false on server, true after hydration
1010
+ ```
1011
+
820
1012
  ---
821
1013
 
822
1014
  ## License