@enonic/ui 1.1.1 → 1.2.1

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/README.md CHANGED
@@ -1,16 +1,22 @@
1
1
  # @enonic/ui
2
2
 
3
- A modern UI component library built with Preact/React, TypeScript, and Tailwind CSS.
3
+ [![npm](https://img.shields.io/npm/v/@enonic/ui.svg)](https://www.npmjs.com/package/@enonic/ui)
4
+ [![CI](https://github.com/enonic/npm-enonic-ui/actions/workflows/ci.yml/badge.svg)](https://github.com/enonic/npm-enonic-ui/actions/workflows/ci.yml)
5
+ [![License](https://img.shields.io/npm/l/@enonic/ui.svg)](LICENSE)
4
6
 
5
- ## Installation
7
+ UI components for Preact and React, styled with Tailwind CSS v4. Components ship with keyboard navigation, focus management, and ARIA wiring; theming runs on CSS custom properties with a light and a dark palette.
6
8
 
7
- #### With React
9
+ **[Browse the components →](https://enonic.github.io/npm-enonic-ui/)** Every component and variant, live, with props tables and accessibility checks.
10
+
11
+ ## Quick start
12
+
13
+ **1. Install** — with React:
8
14
 
9
15
  ```bash
10
16
  pnpm add @enonic/ui react react-dom @radix-ui/react-slot
11
17
  ```
12
18
 
13
- #### With Preact
19
+ …or with Preact:
14
20
 
15
21
  ```bash
16
22
  pnpm add @enonic/ui preact @radix-ui/react-slot
@@ -19,39 +25,51 @@ pnpm add @enonic/ui preact @radix-ui/react-slot
19
25
  <details>
20
26
  <summary><b>npm</b></summary>
21
27
 
22
- #### With React
23
-
24
28
  ```bash
25
- npm install @enonic/ui react react-dom @radix-ui/react-slot
29
+ npm install @enonic/ui react react-dom @radix-ui/react-slot # React
30
+ npm install @enonic/ui preact @radix-ui/react-slot # Preact
26
31
  ```
27
32
 
28
- #### With Preact
33
+ </details>
29
34
 
30
- ```bash
31
- npm install @enonic/ui preact @radix-ui/react-slot
35
+ **2. Load the styles.** Components use Tailwind utility classes and render unstyled until a stylesheet is present. The prebuilt one needs no configuration:
36
+
37
+ ```css
38
+ /* your app's entry CSS */
39
+ @import '@enonic/ui/style.css';
32
40
  ```
33
41
 
34
- </details>
42
+ Already running Tailwind v4? Use the [Tailwind setup](#tailwind-v4-setup) instead — it shares one utility build with your app and lets your theme overrides through.
35
43
 
36
- > Some components and styling paths need extra packages — `focus-trap-react` (dialogs and overlays), `react-virtuoso` (virtualized lists), and `tw-animate-css` (the Tailwind styling path). See [Peer Dependencies](#peer-dependencies).
44
+ **3. Render something.**
37
45
 
38
- ## Usage
46
+ ```tsx
47
+ import { Button, Input } from '@enonic/ui';
39
48
 
40
- ### Styles
49
+ function App() {
50
+ return (
51
+ <>
52
+ <Input label='Project' placeholder='Search projects' />
53
+ <Button variant='filled' size='md'>
54
+ Search
55
+ </Button>
56
+ </>
57
+ );
58
+ }
59
+ ```
41
60
 
42
- > **Important**: Components are styled with Tailwind CSS utility classes and render unstyled until you load the CSS. Pick one of the two paths below.
61
+ ## Styles
43
62
 
44
- #### Option A — No Tailwind (prebuilt stylesheet)
63
+ Two paths. Pick by whether your app already runs Tailwind:
45
64
 
46
- Import the fully compiled stylesheet. It bundles Tailwind's output, the design tokens, and component animations — nothing else to configure:
65
+ | Your app | Import | Trade-off |
66
+ | ----------------------------- | ----------------------- | ----------------------------------------------------------------- |
67
+ | No Tailwind | `@enonic/ui/style.css` | Zero config, ships Tailwind's output for the components |
68
+ | Tailwind CSS v4 (recommended) | `@enonic/ui/preset.css` | One shared utility build, your theme overrides apply, smaller CSS |
47
69
 
48
- ```css
49
- @import '@enonic/ui/style.css';
50
- ```
70
+ ### Tailwind v4 setup
51
71
 
52
- #### Option B — Your own Tailwind CSS (recommended)
53
-
54
- If your project already runs **Tailwind CSS v4**, integrate the design tokens through `preset.css` so components share your Tailwind build — one set of utilities, your theme overrides apply, and a smaller bundle. A working setup needs all of the following:
72
+ Import the tokens through `preset.css` and point Tailwind at the package. All four pieces are needed:
55
73
 
56
74
  ```css
57
75
  /* Declare layer order first so your own overrides win over component styles */
@@ -60,19 +78,19 @@ If your project already runs **Tailwind CSS v4**, integrate the design tokens th
60
78
  @import 'tailwindcss/theme.css' layer(theme);
61
79
  @import 'tailwindcss/preflight.css' layer(base);
62
80
  @import '@enonic/ui/preset.css'; /* design tokens, dark variant, base + custom utilities */
63
- @import 'tw-animate-css'; /* component enter/exit animations (required — see below) */
81
+ @import 'tw-animate-css'; /* component enter/exit animations */
64
82
  @import 'tailwindcss/utilities.css' layer(utilities);
65
83
 
66
- /* Let Tailwind scan @enonic/ui's compiled files for the classes its components use.
67
- The path is relative to THIS file — adjust the number of ../ to reach node_modules. */
84
+ /* Relative to THIS file — adjust the ../ depth to reach node_modules */
68
85
  @source '../node_modules/@enonic/ui';
69
86
  ```
70
87
 
71
- > **`@source` is mandatory.** Tailwind v4 only generates the utilities it finds in scanned sources, and it ignores `node_modules` by default. The classes used inside `@enonic/ui` components live in its compiled output, so without `@source` pointing at the package those utilities are never generated and components render unstyled.
88
+ > [!IMPORTANT]
89
+ > `@source` is mandatory. Tailwind v4 generates only the utilities it finds in scanned sources, and it skips `node_modules` by default. Without this line the component classes are never generated and everything renders unstyled.
72
90
 
73
- > **`tw-animate-css` is required on this path.** Components such as `Dialog`, `Toast`, and `Tooltip` emit `animate-in` / `fade-*` / `zoom-*` classes from [`tw-animate-css`](https://www.npmjs.com/package/tw-animate-css), which `preset.css` does not include. Install it (`pnpm add tw-animate-css`) and import it as shown. The prebuilt `style.css` already inlines these, so Option A doesn't need it.
91
+ `tw-animate-css` is also required on this path: `Dialog`, `Toast`, and `Tooltip` emit `animate-in` / `fade-*` / `zoom-*` classes that `preset.css` does not include. Install it with `pnpm add tw-animate-css`. The prebuilt `style.css` already inlines them.
74
92
 
75
- If you don't need explicit layer control, the shorthand also works:
93
+ Without explicit layer control, the shorthand works too:
76
94
 
77
95
  ```css
78
96
  @import 'tailwindcss';
@@ -81,36 +99,19 @@ If you don't need explicit layer control, the shorthand also works:
81
99
  @source '../node_modules/@enonic/ui';
82
100
  ```
83
101
 
84
- #### CSS exports
102
+ ### CSS exports
85
103
 
86
- | Export | Contents | Use when |
87
- | -------------------------- | --------------------------------------------------------------------- | --------------------------------- |
88
- | `@enonic/ui/style.css` | Fully compiled — Tailwind output + tokens + animations | You don't run Tailwind (Option A) |
89
- | `@enonic/ui/preset.css` | Design tokens (`@theme`), dark variant, base styles, custom utilities | You run Tailwind (Option B) |
90
- | `@enonic/ui/tokens.css` | Design tokens + dark variant only | Tokens without base/utilities |
91
- | `@enonic/ui/base.css` | Root font size + base text color | Granular composition |
92
- | `@enonic/ui/utilities.css` | Custom `@utility` definitions (shimmer, `scrollbar-none`, …) | Granular composition |
104
+ | Export | Contents | Use when |
105
+ | -------------------------- | --------------------------------------------------------------------- | ----------------------------- |
106
+ | `@enonic/ui/style.css` | Fully compiled — Tailwind output + tokens + animations | You don't run Tailwind |
107
+ | `@enonic/ui/preset.css` | Design tokens (`@theme`), dark variant, base styles, custom utilities | You run Tailwind v4 |
108
+ | `@enonic/ui/tokens.css` | Design tokens + dark variant only | Tokens without base/utilities |
109
+ | `@enonic/ui/base.css` | Root font size + base text color | Granular composition |
110
+ | `@enonic/ui/utilities.css` | Custom `@utility` definitions (shimmer, `scrollbar-none`, …) | Granular composition |
93
111
 
94
- ### Import Components
112
+ ## Dark mode
95
113
 
96
- ```tsx
97
- import { Button, Input } from '@enonic/ui';
98
-
99
- function App() {
100
- return (
101
- <div>
102
- <Button variant='primary' size='md'>
103
- Click Me
104
- </Button>
105
- <Input placeholder='Enter text...' />
106
- </div>
107
- );
108
- }
109
- ```
110
-
111
- ### Dark Mode
112
-
113
- The library ships light and dark themes as CSS variables, switched by a class. Add `dark` to a root element (or a shadow-DOM host) and every token updates:
114
+ Both themes ship as CSS variables, switched by a class. Add `dark` to a root element and every token updates:
114
115
 
115
116
  ```html
116
117
  <html class="dark">
@@ -119,48 +120,62 @@ The library ships light and dark themes as CSS variables, switched by a class. A
119
120
  ```
120
121
 
121
122
  ```ts
122
- // Toggle at runtime
123
123
  document.documentElement.classList.toggle('dark', isDark);
124
124
  ```
125
125
 
126
- The variant is defined as `@custom-variant dark (&:where(.dark, .dark *))` and also matches `:host(.dark)` for web-component hosts. Use the `dark:` variant in your own markup as usual.
126
+ The variant is `@custom-variant dark (&:where(.dark, .dark *))`, so `dark:` matches `.dark` and anything inside it. Use it in your own markup as usual.
127
+
128
+ Inside a shadow root the document's theme does not reach in, even though custom properties cross the boundary: the adopted sheet declares the light tokens on `:host`, and a declaration on the host beats a value inherited into it. Mount through `AppRoot` and let it switch — `<AppRoot theme='dark' stylesheets={sheets}>`. `theme='light'` emits nothing, being the state a root is already in. A `dark` class on the host works too, since `:host(.dark)` outranks `:host`, but it leaves your own `dark:` utilities unmatched because `.dark *` cannot see an ancestor in the outer tree; the class `AppRoot` places sits inside the root, and it mirrors it onto the portal layer so overlays follow. `stylesheets` expects constructed sheets: on Tailwind v4 your own compiled CSS, which already carries the package's tokens and utilities via `preset.css` and `@source`; without Tailwind the prebuilt sheet from `@enonic/ui/style` (`getStyleSheet()`, or `styleText` for the raw text). Not both, and never `tokens.css`/`utilities.css`, which are Tailwind source entries.
127
129
 
128
- ### Icons
130
+ ## Icons
129
131
 
130
- Icons used internally by components are bundled — no icon library install is required for components to render.
132
+ Icons used inside components are bundled — no icon package is needed for components to render.
131
133
 
132
- You only need a Lucide package when you pass a custom icon to a component that accepts one (e.g. `Tab`, `Link`, and `VirtualizedTreeList` expose an `icon` prop):
134
+ A Lucide package is needed only when you pass your own icon to a component that takes one, such as the `icon` prop on `Tab`, `Link`, or `VirtualizedTreeList`:
133
135
 
134
136
  ```tsx
135
137
  import { Tab } from '@enonic/ui';
136
- import { Star } from 'lucide-react'; // React
137
- // import { Star } from 'lucide-preact'; // Preact
138
+ import { Star } from 'lucide-react'; // lucide-preact in Preact projects
138
139
 
139
140
  <Tab icon={Star} label='Favorites' />;
140
141
  ```
141
142
 
142
- Use `lucide-react` in React projects and `lucide-preact` in Preact projects — the `icon` prop accepts either.
143
+ The `icon` prop accepts either package.
143
144
 
144
- ## Peer Dependencies
145
+ ## Requirements
145
146
 
146
- Installing `@enonic/ui` surfaces any unmet peers with the exact ranges it needs (defined in `package.json` — the source of truth).
147
+ `package.json` holds the exact ranges — installing surfaces any unmet peer.
147
148
 
148
- **Required**
149
+ | Requirement | Notes |
150
+ | ----------------------------------------------- | ----------------------------------- |
151
+ | `@radix-ui/react-slot` | Required — backs the `asChild` prop |
152
+ | `react` + `react-dom` v19, **or** `preact` v10+ | Pick one framework |
153
+ | Tailwind CSS v4 | Only for the `preset.css` path |
149
154
 
150
- - `@radix-ui/react-slot` — backs the `asChild` prop used across components
155
+ Optional peers, needed per feature:
151
156
 
152
- …plus one framework:
157
+ | Package | Needed for |
158
+ | ------------------ | ------------------------------------------------- |
159
+ | `focus-trap-react` | Focus management in `Dialog` and other overlays |
160
+ | `react-virtuoso` | `VirtualizedTreeList` and other virtualized lists |
161
+ | `tw-animate-css` | Component animations on the `preset.css` path |
153
162
 
154
- | React | Preact |
155
- | --------------------------- | --------------- |
156
- | `react` + `react-dom` (v19) | `preact` (v10+) |
163
+ ## What's exported
157
164
 
158
- **Optional** — add only if you use the related feature:
165
+ Beyond the components, the package entry re-exports the hooks that drive them (roving tabindex, type-ahead, floating position, scroll lock, and more), the bundled icon set, context providers, shared types, and the `cn` class-merge helper.
159
166
 
160
- - `focus-trap-react` — focus management for `Dialog` and other overlay components
161
- - `react-virtuoso` — required by `VirtualizedTreeList` and other virtualized lists
162
- - `tw-animate-css` — component animations when integrating through `preset.css` (see [Styles](#styles))
167
+ ```tsx
168
+ import { Button, cn, useControlledState } from '@enonic/ui';
169
+ ```
170
+
171
+ ## Development
172
+
173
+ ```bash
174
+ pnpm dev # Storybook on :6006
175
+ pnpm check:fix # format + lint, then typecheck
176
+ pnpm build # library + CSS into dist/
177
+ ```
163
178
 
164
179
  ## License
165
180
 
166
- MIT
181
+ Apache-2.0 — see [LICENSE](LICENSE).