luxen-ui 0.1.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 (201) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -0
  3. package/dist/css/elements/avatar.css +20 -0
  4. package/dist/css/elements/badge.css +159 -0
  5. package/dist/css/elements/button.css +171 -0
  6. package/dist/css/elements/close-button/circle.css +66 -0
  7. package/dist/css/elements/close-button/ring.css +71 -0
  8. package/dist/css/elements/close-button/square.css +70 -0
  9. package/dist/css/elements/disclosure.css +137 -0
  10. package/dist/css/elements/divider.css +75 -0
  11. package/dist/css/elements/input-otp.css +164 -0
  12. package/dist/css/elements/input-stepper/default.css +245 -0
  13. package/dist/css/elements/input-stepper/rounded.css +238 -0
  14. package/dist/css/elements/kbd.css +21 -0
  15. package/dist/css/elements/progress.css +114 -0
  16. package/dist/css/elements/select.css +71 -0
  17. package/dist/css/elements/skeleton.css +89 -0
  18. package/dist/css/elements/tabs/enclosed.css +148 -0
  19. package/dist/css/elements/tabs/line.css +138 -0
  20. package/dist/css/elements/toast.css +260 -0
  21. package/dist/css/index.css +885 -0
  22. package/dist/custom-elements.json +14424 -0
  23. package/dist/define.d.ts +9 -0
  24. package/dist/define.d.ts.map +1 -0
  25. package/dist/define.js +16 -0
  26. package/dist/elements/avatar/avatar.css +128 -0
  27. package/dist/elements/avatar/avatar.d.ts +21 -0
  28. package/dist/elements/avatar/avatar.d.ts.map +1 -0
  29. package/dist/elements/avatar/avatar.js +106 -0
  30. package/dist/elements/avatar/index.d.ts +8 -0
  31. package/dist/elements/avatar/index.d.ts.map +1 -0
  32. package/dist/elements/avatar/index.js +4 -0
  33. package/dist/elements/badge/badge.d.ts +17 -0
  34. package/dist/elements/badge/badge.d.ts.map +1 -0
  35. package/dist/elements/badge/badge.js +34 -0
  36. package/dist/elements/badge/index.d.ts +8 -0
  37. package/dist/elements/badge/index.d.ts.map +1 -0
  38. package/dist/elements/badge/index.js +4 -0
  39. package/dist/elements/carousel/carousel.css +205 -0
  40. package/dist/elements/carousel/carousel.d.ts +148 -0
  41. package/dist/elements/carousel/carousel.d.ts.map +1 -0
  42. package/dist/elements/carousel/carousel.js +473 -0
  43. package/dist/elements/carousel/index.d.ts +8 -0
  44. package/dist/elements/carousel/index.d.ts.map +1 -0
  45. package/dist/elements/carousel/index.js +4 -0
  46. package/dist/elements/carousel-item/carousel-item.css +11 -0
  47. package/dist/elements/carousel-item/carousel-item.d.ts +13 -0
  48. package/dist/elements/carousel-item/carousel-item.d.ts.map +1 -0
  49. package/dist/elements/carousel-item/carousel-item.js +20 -0
  50. package/dist/elements/carousel-item/index.d.ts +8 -0
  51. package/dist/elements/carousel-item/index.d.ts.map +1 -0
  52. package/dist/elements/carousel-item/index.js +4 -0
  53. package/dist/elements/dialog/dialog.css +92 -0
  54. package/dist/elements/dialog/dialog.d.ts +56 -0
  55. package/dist/elements/dialog/dialog.d.ts.map +1 -0
  56. package/dist/elements/dialog/dialog.js +204 -0
  57. package/dist/elements/dialog/dialog.styles.d.ts +8 -0
  58. package/dist/elements/dialog/dialog.styles.d.ts.map +1 -0
  59. package/dist/elements/dialog/dialog.styles.js +8 -0
  60. package/dist/elements/dialog/index.d.ts +8 -0
  61. package/dist/elements/dialog/index.d.ts.map +1 -0
  62. package/dist/elements/dialog/index.js +4 -0
  63. package/dist/elements/divider/divider.d.ts +23 -0
  64. package/dist/elements/divider/divider.d.ts.map +1 -0
  65. package/dist/elements/divider/divider.js +49 -0
  66. package/dist/elements/divider/index.d.ts +8 -0
  67. package/dist/elements/divider/index.d.ts.map +1 -0
  68. package/dist/elements/divider/index.js +4 -0
  69. package/dist/elements/drawer/drawer.css +66 -0
  70. package/dist/elements/drawer/drawer.d.ts +34 -0
  71. package/dist/elements/drawer/drawer.d.ts.map +1 -0
  72. package/dist/elements/drawer/drawer.js +46 -0
  73. package/dist/elements/drawer/index.d.ts +8 -0
  74. package/dist/elements/drawer/index.d.ts.map +1 -0
  75. package/dist/elements/drawer/index.js +4 -0
  76. package/dist/elements/dropdown/dropdown.css +31 -0
  77. package/dist/elements/dropdown/dropdown.d.ts +64 -0
  78. package/dist/elements/dropdown/dropdown.d.ts.map +1 -0
  79. package/dist/elements/dropdown/dropdown.js +322 -0
  80. package/dist/elements/dropdown/index.d.ts +8 -0
  81. package/dist/elements/dropdown/index.d.ts.map +1 -0
  82. package/dist/elements/dropdown/index.js +4 -0
  83. package/dist/elements/dropdown-item/dropdown-item.css +51 -0
  84. package/dist/elements/dropdown-item/dropdown-item.d.ts +25 -0
  85. package/dist/elements/dropdown-item/dropdown-item.d.ts.map +1 -0
  86. package/dist/elements/dropdown-item/dropdown-item.js +110 -0
  87. package/dist/elements/dropdown-item/index.d.ts +8 -0
  88. package/dist/elements/dropdown-item/index.d.ts.map +1 -0
  89. package/dist/elements/dropdown-item/index.js +4 -0
  90. package/dist/elements/icon/icon.css +10 -0
  91. package/dist/elements/icon/icon.d.ts +19 -0
  92. package/dist/elements/icon/icon.d.ts.map +1 -0
  93. package/dist/elements/icon/icon.js +53 -0
  94. package/dist/elements/icon/index.d.ts +8 -0
  95. package/dist/elements/icon/index.d.ts.map +1 -0
  96. package/dist/elements/icon/index.js +4 -0
  97. package/dist/elements/input-otp/index.d.ts +8 -0
  98. package/dist/elements/input-otp/index.d.ts.map +1 -0
  99. package/dist/elements/input-otp/index.js +4 -0
  100. package/dist/elements/input-otp/input-otp.d.ts +31 -0
  101. package/dist/elements/input-otp/input-otp.d.ts.map +1 -0
  102. package/dist/elements/input-otp/input-otp.js +139 -0
  103. package/dist/elements/input-stepper/index.d.ts +8 -0
  104. package/dist/elements/input-stepper/index.d.ts.map +1 -0
  105. package/dist/elements/input-stepper/index.js +4 -0
  106. package/dist/elements/input-stepper/input-stepper.d.ts +63 -0
  107. package/dist/elements/input-stepper/input-stepper.d.ts.map +1 -0
  108. package/dist/elements/input-stepper/input-stepper.js +249 -0
  109. package/dist/elements/popover/index.d.ts +8 -0
  110. package/dist/elements/popover/index.d.ts.map +1 -0
  111. package/dist/elements/popover/index.js +4 -0
  112. package/dist/elements/popover/popover.css +61 -0
  113. package/dist/elements/popover/popover.d.ts +62 -0
  114. package/dist/elements/popover/popover.d.ts.map +1 -0
  115. package/dist/elements/popover/popover.js +244 -0
  116. package/dist/elements/rating/index.d.ts +8 -0
  117. package/dist/elements/rating/index.d.ts.map +1 -0
  118. package/dist/elements/rating/index.js +4 -0
  119. package/dist/elements/rating/rating.css +102 -0
  120. package/dist/elements/rating/rating.d.ts +38 -0
  121. package/dist/elements/rating/rating.d.ts.map +1 -0
  122. package/dist/elements/rating/rating.js +193 -0
  123. package/dist/elements/skeleton/index.d.ts +8 -0
  124. package/dist/elements/skeleton/index.d.ts.map +1 -0
  125. package/dist/elements/skeleton/index.js +4 -0
  126. package/dist/elements/skeleton/skeleton.d.ts +12 -0
  127. package/dist/elements/skeleton/skeleton.d.ts.map +1 -0
  128. package/dist/elements/skeleton/skeleton.js +13 -0
  129. package/dist/elements/spinner/index.d.ts +8 -0
  130. package/dist/elements/spinner/index.d.ts.map +1 -0
  131. package/dist/elements/spinner/index.js +4 -0
  132. package/dist/elements/spinner/spinner.css +28 -0
  133. package/dist/elements/spinner/spinner.d.ts +16 -0
  134. package/dist/elements/spinner/spinner.d.ts.map +1 -0
  135. package/dist/elements/spinner/spinner.js +37 -0
  136. package/dist/elements/tabs/index.d.ts +8 -0
  137. package/dist/elements/tabs/index.d.ts.map +1 -0
  138. package/dist/elements/tabs/index.js +4 -0
  139. package/dist/elements/tabs/tabs.d.ts +48 -0
  140. package/dist/elements/tabs/tabs.d.ts.map +1 -0
  141. package/dist/elements/tabs/tabs.js +210 -0
  142. package/dist/elements/toast/index.d.ts +9 -0
  143. package/dist/elements/toast/index.d.ts.map +1 -0
  144. package/dist/elements/toast/index.js +5 -0
  145. package/dist/elements/toast/toast.d.ts +72 -0
  146. package/dist/elements/toast/toast.d.ts.map +1 -0
  147. package/dist/elements/toast/toast.js +375 -0
  148. package/dist/elements/tooltip/index.d.ts +8 -0
  149. package/dist/elements/tooltip/index.d.ts.map +1 -0
  150. package/dist/elements/tooltip/index.js +4 -0
  151. package/dist/elements/tooltip/tooltip.css +37 -0
  152. package/dist/elements/tooltip/tooltip.d.ts +59 -0
  153. package/dist/elements/tooltip/tooltip.d.ts.map +1 -0
  154. package/dist/elements/tooltip/tooltip.js +231 -0
  155. package/dist/elements/tree/index.d.ts +8 -0
  156. package/dist/elements/tree/index.d.ts.map +1 -0
  157. package/dist/elements/tree/index.js +4 -0
  158. package/dist/elements/tree/tree.css +26 -0
  159. package/dist/elements/tree/tree.d.ts +76 -0
  160. package/dist/elements/tree/tree.d.ts.map +1 -0
  161. package/dist/elements/tree/tree.js +432 -0
  162. package/dist/elements/tree-item/index.d.ts +8 -0
  163. package/dist/elements/tree-item/index.d.ts.map +1 -0
  164. package/dist/elements/tree-item/index.js +4 -0
  165. package/dist/elements/tree-item/tree-item.css +172 -0
  166. package/dist/elements/tree-item/tree-item.d.ts +74 -0
  167. package/dist/elements/tree-item/tree-item.d.ts.map +1 -0
  168. package/dist/elements/tree-item/tree-item.js +301 -0
  169. package/dist/index.d.ts +6 -0
  170. package/dist/index.d.ts.map +1 -0
  171. package/dist/index.js +4 -0
  172. package/dist/registry.d.ts +22 -0
  173. package/dist/registry.d.ts.map +1 -0
  174. package/dist/registry.js +33 -0
  175. package/dist/shared/controllers/popover.d.ts +44 -0
  176. package/dist/shared/controllers/popover.d.ts.map +1 -0
  177. package/dist/shared/controllers/popover.js +359 -0
  178. package/dist/shared/luxen-element.d.ts +20 -0
  179. package/dist/shared/luxen-element.d.ts.map +1 -0
  180. package/dist/shared/luxen-element.js +23 -0
  181. package/dist/shared/luxen-form-associated-element.d.ts +49 -0
  182. package/dist/shared/luxen-form-associated-element.d.ts.map +1 -0
  183. package/dist/shared/luxen-form-associated-element.js +123 -0
  184. package/dist/shared/styles/host.css +13 -0
  185. package/dist/shared/styles/host.styles.d.ts +9 -0
  186. package/dist/shared/styles/host.styles.d.ts.map +1 -0
  187. package/dist/shared/styles/host.styles.js +9 -0
  188. package/dist/skills/luxen-ui/SKILL.md +82 -0
  189. package/dist/skills/luxen-ui/references/avatar.md +259 -0
  190. package/dist/skills/luxen-ui/references/badge.md +289 -0
  191. package/dist/skills/luxen-ui/references/button.md +309 -0
  192. package/dist/skills/luxen-ui/references/close-button.md +104 -0
  193. package/dist/skills/luxen-ui/references/dialog.md +435 -0
  194. package/dist/skills/luxen-ui/references/drawer.md +400 -0
  195. package/dist/skills/luxen-ui/references/progress.md +133 -0
  196. package/dist/skills/luxen-ui/references/select.md +100 -0
  197. package/dist/skills/luxen-ui/references/toast.md +396 -0
  198. package/dist/skills/luxen-ui/references/tree.md +359 -0
  199. package/package.json +116 -0
  200. package/postcss-plugin-prefix.js +63 -0
  201. package/vite-plugin.ts +29 -0
@@ -0,0 +1,359 @@
1
+ ---
2
+ outline: deep
3
+ ---
4
+
5
+ # Tree
6
+
7
+ Tree views present hierarchical data like file explorers, navigation menus, and taxonomies. Each node expands/collapses to reveal nested items and can be selected in several ways.
8
+
9
+ <ElementSpec
10
+ tag="l-tree"
11
+ type="shadow"
12
+ />
13
+
14
+ ## Options
15
+
16
+ ### Basic
17
+
18
+ Wrap `<l-tree-item>` nodes inside `<l-tree>`. Nested `<l-tree-item>` children become sub-nodes automatically.
19
+
20
+ <ComponentWrapper vertical :html="treeBasic" />
21
+
22
+ ```html
23
+ <l-tree>
24
+ <l-tree-item>
25
+ Documents
26
+ <l-tree-item>
27
+ Photos
28
+ <l-tree-item>beach.jpg</l-tree-item>
29
+ <l-tree-item>mountain.jpg</l-tree-item>
30
+ </l-tree-item>
31
+ <l-tree-item>
32
+ Invoices
33
+ <l-tree-item>january.pdf</l-tree-item>
34
+ <l-tree-item>february.pdf</l-tree-item>
35
+ </l-tree-item>
36
+ </l-tree-item>
37
+ <l-tree-item>
38
+ Projects
39
+ <l-tree-item>luxen-ui</l-tree-item>
40
+ <l-tree-item>website</l-tree-item>
41
+ </l-tree-item>
42
+ <l-tree-item>Downloads</l-tree-item>
43
+ </l-tree>
44
+ ```
45
+
46
+ ### Custom icons
47
+
48
+ Place any element in the `prefix` slot to render a leading icon before the label. Icons inherit the current text color.
49
+
50
+ <ComponentWrapper vertical :html="treeIcons" />
51
+
52
+ ```html
53
+ <l-tree>
54
+ <l-tree-item expanded>
55
+ <l-icon
56
+ slot="prefix"
57
+ name="lucide:folder-open"
58
+ ></l-icon>
59
+ src
60
+ <l-tree-item>
61
+ <l-icon
62
+ slot="prefix"
63
+ name="lucide:folder"
64
+ ></l-icon>
65
+ components
66
+ <l-tree-item>
67
+ <l-icon
68
+ slot="prefix"
69
+ name="lucide:file-code"
70
+ ></l-icon>
71
+ Button.ts
72
+ </l-tree-item>
73
+ <l-tree-item>
74
+ <l-icon
75
+ slot="prefix"
76
+ name="lucide:file-code"
77
+ ></l-icon>
78
+ Tree.ts
79
+ </l-tree-item>
80
+ </l-tree-item>
81
+ <l-tree-item>
82
+ <l-icon
83
+ slot="prefix"
84
+ name="lucide:file-json"
85
+ ></l-icon>
86
+ package.json
87
+ </l-tree-item>
88
+ <l-tree-item>
89
+ <l-icon
90
+ slot="prefix"
91
+ name="lucide:file-text"
92
+ ></l-icon>
93
+ README.md
94
+ </l-tree-item>
95
+ </l-tree-item>
96
+ <l-tree-item>
97
+ <l-icon
98
+ slot="prefix"
99
+ name="lucide:folder"
100
+ ></l-icon>
101
+ public
102
+ <l-tree-item>
103
+ <l-icon
104
+ slot="prefix"
105
+ name="lucide:image"
106
+ ></l-icon>
107
+ logo.svg
108
+ </l-tree-item>
109
+ </l-tree-item>
110
+ </l-tree>
111
+ ```
112
+
113
+ ### Custom expand icons
114
+
115
+ Override the `expand-icon` and `collapse-icon` slots to show a different icon per state — e.g. a closed folder when the branch is collapsed and an open folder when expanded. Leaves keep the `prefix` slot for their own icon.
116
+
117
+ <ComponentWrapper vertical :html="treeExpandIcons" />
118
+
119
+ ```html
120
+ <l-tree>
121
+ <l-tree-item expanded>
122
+ <l-icon
123
+ slot="expand-icon"
124
+ name="lucide:folder"
125
+ ></l-icon>
126
+ <l-icon
127
+ slot="collapse-icon"
128
+ name="lucide:folder-open"
129
+ ></l-icon>
130
+ src
131
+ <l-tree-item>
132
+ <l-icon
133
+ slot="expand-icon"
134
+ name="lucide:folder"
135
+ ></l-icon>
136
+ <l-icon
137
+ slot="collapse-icon"
138
+ name="lucide:folder-open"
139
+ ></l-icon>
140
+ components
141
+ <l-tree-item>
142
+ <l-icon
143
+ slot="prefix"
144
+ name="lucide:file-code"
145
+ ></l-icon>
146
+ Button.ts
147
+ </l-tree-item>
148
+ <l-tree-item>
149
+ <l-icon
150
+ slot="prefix"
151
+ name="lucide:file-code"
152
+ ></l-icon>
153
+ Tree.ts
154
+ </l-tree-item>
155
+ </l-tree-item>
156
+ <l-tree-item>
157
+ <l-icon
158
+ slot="prefix"
159
+ name="lucide:file-json"
160
+ ></l-icon>
161
+ package.json
162
+ </l-tree-item>
163
+ <l-tree-item>
164
+ <l-icon
165
+ slot="prefix"
166
+ name="lucide:file-text"
167
+ ></l-icon>
168
+ README.md
169
+ </l-tree-item>
170
+ </l-tree-item>
171
+ </l-tree>
172
+ ```
173
+
174
+ ### Row actions
175
+
176
+ Place any interactive element inside a `<l-tree-item>` to expose per-row actions (e.g. an `<l-dropdown>` menu, a button, a link). Clicks on `<button>`, `<a>`, `<input>` and elements with `role="button"` or `role="menuitem"` never toggle the row's selection or expansion.
177
+
178
+ This demo is controlled from Vue state: the yellow `…` trigger is rendered only on the selected row via `v-if`, so clicking another row moves the button there without duplicating it in the DOM.
179
+
180
+ <TreeActionsDemo />
181
+
182
+ ### Multiple selection
183
+
184
+ Set `selection="multiple"` to render a native checkbox on every item. Toggling a parent cascades the selection to its descendants and sets the indeterminate state when only some children are selected.
185
+
186
+ <ComponentWrapper vertical :html="treeMultiple" />
187
+
188
+ ```html
189
+ <l-tree selection="multiple">
190
+ <l-tree-item expanded>
191
+ Fruits
192
+ <l-tree-item>Apple</l-tree-item>
193
+ <l-tree-item selected>Banana</l-tree-item>
194
+ <l-tree-item>Cherry</l-tree-item>
195
+ </l-tree-item>
196
+ <l-tree-item>
197
+ Vegetables
198
+ <l-tree-item>Carrot</l-tree-item>
199
+ <l-tree-item>Lettuce</l-tree-item>
200
+ </l-tree-item>
201
+ </l-tree>
202
+ ```
203
+
204
+ ### Independent selection
205
+
206
+ Add `independent` to decouple parents and children: a parent can be selected without ticking any of its descendants and vice-versa. Useful when nodes represent independent concepts (categories, tags, permissions) rather than aggregations.
207
+
208
+ <ComponentWrapper vertical :html="treeIndependent" />
209
+
210
+ ```html
211
+ <l-tree
212
+ selection="multiple"
213
+ independent
214
+ >
215
+ <l-tree-item expanded>
216
+ Region: Europe
217
+ <l-tree-item>France</l-tree-item>
218
+ <l-tree-item>Germany</l-tree-item>
219
+ <l-tree-item>Italy</l-tree-item>
220
+ </l-tree-item>
221
+ <l-tree-item expanded>
222
+ Region: Americas
223
+ <l-tree-item>Brazil</l-tree-item>
224
+ <l-tree-item>United States</l-tree-item>
225
+ </l-tree-item>
226
+ </l-tree>
227
+ ```
228
+
229
+ ### Leaf-only selection
230
+
231
+ Set `selection="leaf"` when only terminal nodes represent selectable values. Clicking a branch only toggles its expansion.
232
+
233
+ <ComponentWrapper vertical :html="treeLeaf" />
234
+
235
+ ```html
236
+ <l-tree selection="leaf">
237
+ <l-tree-item expanded>
238
+ Components
239
+ <l-tree-item>Button</l-tree-item>
240
+ <l-tree-item>Dropdown</l-tree-item>
241
+ <l-tree-item>Tree</l-tree-item>
242
+ </l-tree-item>
243
+ <l-tree-item>
244
+ Tokens
245
+ <l-tree-item>Colors</l-tree-item>
246
+ <l-tree-item>Spacing</l-tree-item>
247
+ </l-tree-item>
248
+ </l-tree>
249
+ ```
250
+
251
+ ### Disabled items
252
+
253
+ Add `disabled` to any item to prevent selection and interaction. The item remains visible and part of the structure.
254
+
255
+ <ComponentWrapper vertical :html="treeDisabled" />
256
+
257
+ ```html
258
+ <l-tree selection="multiple">
259
+ <l-tree-item expanded>
260
+ Settings
261
+ <l-tree-item>Profile</l-tree-item>
262
+ <l-tree-item disabled>Billing (locked)</l-tree-item>
263
+ <l-tree-item>Notifications</l-tree-item>
264
+ </l-tree-item>
265
+ </l-tree>
266
+ ```
267
+
268
+ ### Lazy loading
269
+
270
+ Add `lazy` to an item whose children will be fetched on first expand. The component emits `lazy-load`; set `loading` to render a spinner in place of the chevron, then append children and remove `lazy`.
271
+
272
+ <TreeLazyDemo />
273
+
274
+ ## Examples
275
+
276
+ ### Comment thread
277
+
278
+ A discussion tree styled like a Reddit comment thread. Demonstrates stacking `prefix` (avatar), the default slot (multi-line content + action buttons), and swapped `expand-icon` / `collapse-icon` slots (`lucide:circle-plus` / `lucide:circle-minus`). The `::part(base)` and `::part(label)` are overridden to align content to the top and allow wrapping; `--indent-size` and `--indent-guide-width` are tuned for the denser look.
279
+
280
+ <TreeRedditDemo />
281
+
282
+ ## Accessibility
283
+
284
+ ### Criteria
285
+
286
+ | Check | Description |
287
+ |-------|-------------|
288
+ | Role | Container has `role="tree"`, items have `role="treeitem"`, groups have `role="group"` |
289
+ | Expanded state | Branches expose `aria-expanded` reflecting open state. Leaf nodes omit the attribute. |
290
+ | Selected state | Selected items expose `aria-selected="true"` |
291
+ | Multi-selectable | Container sets `aria-multiselectable="true"` when `selection="multiple"` |
292
+ | Disabled state | Disabled items expose `aria-disabled="true"` and stay in the DOM for discoverability |
293
+ | Focus management | Roving tabindex: only one item is in the tab order at a time; arrow keys move focus within the tree |
294
+ | Motion | Chevron rotation and spinner respect `prefers-reduced-motion` |
295
+
296
+ ### Rules
297
+ - Every item needs visible text content for its accessible name
298
+ - Use `selection="leaf"` when branches are never valid values — this prevents keyboard users from accidentally selecting aggregations
299
+ - Prefer `selection="multiple" independent` for forms where parent selection is a distinct choice from the children
300
+
301
+ ### Keyboard interactions
302
+
303
+ | Key | Description |
304
+ |-----|-------------|
305
+ | ArrowDown | Moves focus to the next visible item |
306
+ | ArrowUp | Moves focus to the previous visible item |
307
+ | ArrowRight | Expands a collapsed branch, or moves to the first child when already expanded |
308
+ | ArrowLeft | Collapses an expanded branch, or moves to the parent item |
309
+ | Home | Moves focus to the first visible item |
310
+ | End | Moves focus to the last visible item |
311
+ | Enter | Activates the item (selects or toggles expansion depending on mode) |
312
+ | Space | Activates the item (selects or toggles expansion depending on mode) |
313
+ | * | Expands all sibling branches of the focused item |
314
+
315
+ ## API reference
316
+
317
+ ### Importing
318
+
319
+ ```js
320
+ import 'luxen-ui/tree';
321
+ import 'luxen-ui/tree-item';
322
+ ```
323
+
324
+ ### Attributes & Properties
325
+
326
+ <ApiTable :data="[
327
+ { Attribute: 'selection', Description: 'Selection mode: `single` (default), `multiple`, `leaf`, or `none`' },
328
+ { Attribute: 'independent', Description: 'When set with `selection=&quot;multiple&quot;`, parent and descendants selection are decoupled (no cascade, no indeterminate)' },
329
+ ]" />
330
+
331
+ ### Methods
332
+
333
+ <ApiTable :data="[
334
+ { Method: 'getAllItems()', Description: 'Returns every `<l-tree-item>` in document order (including nested)' },
335
+ { Method: 'getSelection()', Description: 'Returns the currently selected items' },
336
+ { Method: 'expandAll()', Description: 'Expands every branch' },
337
+ { Method: 'collapseAll()', Description: 'Collapses every item' },
338
+ ]" />
339
+
340
+ ### Events
341
+
342
+ <ApiTable :data="[
343
+ { Event: 'selection-change', Description: 'Fired when the selection changes. Detail: `{ selection }`' },
344
+ ]" />
345
+
346
+ ### CSS custom properties
347
+
348
+ <ApiTable :data="[
349
+ { Name: '--indent-size', Description: 'Horizontal indent per depth level. Default `1rem`' },
350
+ { Name: '--indent-guide-width', Description: 'Thickness of the vertical guide line between a parent and its children. Default `1px`. Set to `0` to hide guides' },
351
+ { Name: '--indent-guide-style', Description: 'Line style of the guide: `solid` (default), `dashed`, `dotted`, `double`' },
352
+ { Name: '--indent-guide-color', Description: 'Color of the guide line' },
353
+ { Name: '--row-height', Description: 'Minimum row height. Default `1.75rem`' },
354
+ { Name: '--row-padding-inline', Description: 'Inner inline padding of the row; also drives the content slot left indent and the guide column. Default `0.25rem`' },
355
+ { Name: '--chevron-size', Description: 'Size of the expand/collapse chevron/avatar box. Default `1.125rem`' },
356
+ { Name: '--item-gap', Description: 'Horizontal gap between chevron, prefix, label and suffix on the row; also drives the content slot left indent. Default `0.375rem`' },
357
+ ]" />
358
+
359
+ See [`<l-tree-item>`](/elements/tree-item) for the per-item API.
package/package.json ADDED
@@ -0,0 +1,116 @@
1
+ {
2
+ "name": "luxen-ui",
3
+ "version": "0.1.0",
4
+ "description": "Modern web components and CSS-first UI library built with Lit. Framework-agnostic, customizable prefix, design tokens.",
5
+ "keywords": [
6
+ "custom-elements",
7
+ "design-system",
8
+ "lit",
9
+ "ui-library",
10
+ "web-components"
11
+ ],
12
+ "homepage": "https://luxen-ui.com",
13
+ "bugs": {
14
+ "url": "https://github.com/luxen-ui/luxen-ui/issues"
15
+ },
16
+ "license": "MIT",
17
+ "author": {
18
+ "name": "Luxen UI",
19
+ "url": "https://luxen-ui.com"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/luxen-ui/luxen-ui.git",
24
+ "directory": "packages/ui"
25
+ },
26
+ "files": [
27
+ "dist/",
28
+ "vite-plugin.ts",
29
+ "postcss-plugin-prefix.js",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
33
+ "type": "module",
34
+ "sideEffects": [
35
+ "*.css",
36
+ "**/define.js",
37
+ "**/registry.js"
38
+ ],
39
+ "main": "./dist/index.js",
40
+ "module": "./dist/index.js",
41
+ "types": "./dist/index.d.ts",
42
+ "exports": {
43
+ ".": {
44
+ "types": "./dist/index.d.ts",
45
+ "import": "./dist/index.js"
46
+ },
47
+ "./css": {
48
+ "style": "./dist/css/index.css",
49
+ "import": "./dist/css/index.css"
50
+ },
51
+ "./css/base": {
52
+ "style": "./dist/css/index.css",
53
+ "import": "./dist/css/index.css"
54
+ },
55
+ "./css/*": {
56
+ "style": "./dist/css/elements/*.css",
57
+ "import": "./dist/css/elements/*.css"
58
+ },
59
+ "./dist/custom-elements.json": "./dist/custom-elements.json",
60
+ "./dist/elements/*": "./dist/elements/*",
61
+ "./dist/css/*": "./dist/css/*",
62
+ "./vite-plugin": "./vite-plugin.ts",
63
+ "./postcss-plugin-prefix": "./postcss-plugin-prefix.js",
64
+ "./*/element": {
65
+ "types": "./dist/elements/*/*.d.ts",
66
+ "import": "./dist/elements/*/*.js"
67
+ },
68
+ "./luxen-element": {
69
+ "types": "./dist/shared/luxen-element.d.ts",
70
+ "import": "./dist/shared/luxen-element.js"
71
+ },
72
+ "./luxen-form-associated-element": {
73
+ "types": "./dist/shared/luxen-form-associated-element.d.ts",
74
+ "import": "./dist/shared/luxen-form-associated-element.js"
75
+ },
76
+ "./*": {
77
+ "types": "./dist/elements/*/index.d.ts",
78
+ "import": "./dist/elements/*/index.js"
79
+ }
80
+ },
81
+ "publishConfig": {
82
+ "access": "public",
83
+ "provenance": false
84
+ },
85
+ "scripts": {
86
+ "dev": "WATCH=true DEV=true vp build --config vite.config.css.ts",
87
+ "dev:elements": "vp dev",
88
+ "build": "vp build --config vite.config.css.ts && tsc && node scripts/copy-styles.mjs && vp build && pnpm run manifest && node scripts/generate-skill.mjs",
89
+ "build:css": "vp build --config vite.config.css.ts",
90
+ "build:skill": "node scripts/generate-skill.mjs",
91
+ "manifest": "cem analyze --litelement --outdir dist",
92
+ "preview": "vp preview"
93
+ },
94
+ "dependencies": {
95
+ "@floating-ui/dom": "catalog:",
96
+ "embla-carousel": "catalog:",
97
+ "embla-carousel-autoplay": "catalog:",
98
+ "iconify-icon": "catalog:",
99
+ "lit": "catalog:"
100
+ },
101
+ "devDependencies": {
102
+ "@custom-elements-manifest/analyzer": "catalog:",
103
+ "@custom-elements-manifest/to-markdown": "catalog:",
104
+ "@types/node": "catalog:",
105
+ "autoprefixer": "catalog:",
106
+ "css-extras": "catalog:",
107
+ "postcss": "catalog:",
108
+ "postcss-import": "catalog:",
109
+ "tinyglobby": "catalog:",
110
+ "typescript": "catalog:",
111
+ "vite": "catalog:",
112
+ "vite-plugin-dts": "catalog:",
113
+ "vite-plus": "catalog:"
114
+ },
115
+ "customElements": "dist/custom-elements.json"
116
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * PostCSS plugin that rewrites all `l-` prefixed identifiers in CSS.
3
+ *
4
+ * Runs after `postcss-import` inlines all `@import`s, so imported files
5
+ * like `_tokens.css` are also rewritten.
6
+ *
7
+ * Supports separate element and CSS prefixes:
8
+ * - Element prefix: type selectors (`l-toast` → `{elementPrefix}-toast`)
9
+ * - CSS prefix: classes, custom properties, keyframes, animation references
10
+ *
11
+ * @param {{ elementPrefix?: string, cssPrefix?: string }} opts
12
+ */
13
+ const plugin = (opts = {}) => {
14
+ const elementPrefix = opts.elementPrefix || 'l';
15
+ const cssPrefix = opts.cssPrefix || 'l';
16
+ if (elementPrefix === 'l' && cssPrefix === 'l') return { postcssPlugin: 'luxen-prefix' };
17
+
18
+ return {
19
+ postcssPlugin: 'luxen-prefix',
20
+
21
+ Once(root) {
22
+ root.walk((node) => {
23
+ if (node.type === 'atrule') {
24
+ if (node.name === 'keyframes' && node.params.startsWith('l-')) {
25
+ node.params = node.params.replace(/^l-/, `${cssPrefix}-`);
26
+ }
27
+ }
28
+
29
+ if (node.type === 'rule') {
30
+ node.selector = rewriteSelector(node.selector, elementPrefix, cssPrefix);
31
+ }
32
+
33
+ if (node.type === 'decl') {
34
+ if (node.prop.startsWith('--l-')) {
35
+ node.prop = node.prop.replace(/^--l-/, `--${cssPrefix}-`);
36
+ }
37
+
38
+ if (node.value.includes('l-')) {
39
+ node.value = rewriteValue(node.value, cssPrefix);
40
+ }
41
+ }
42
+ });
43
+ },
44
+ };
45
+ };
46
+
47
+ plugin.postcss = true;
48
+
49
+ function rewriteSelector(selector, elementPrefix, cssPrefix) {
50
+ let result = selector;
51
+ result = result.replace(/\.l-/g, `.${cssPrefix}-`);
52
+ result = result.replace(/(^|[\s,>+~(])l-/gm, `$1${elementPrefix}-`);
53
+ return result;
54
+ }
55
+
56
+ function rewriteValue(value, cssPrefix) {
57
+ let result = value;
58
+ result = result.replaceAll('--l-', `--${cssPrefix}-`);
59
+ result = result.replace(/(?<![-\w])l-/g, `${cssPrefix}-`);
60
+ return result;
61
+ }
62
+
63
+ export default plugin;
package/vite-plugin.ts ADDED
@@ -0,0 +1,29 @@
1
+ import type { Plugin } from 'vite-plus';
2
+ import postcssPrefix from './postcss-plugin-prefix.js';
3
+
4
+ export interface LuxenOptions {
5
+ /** Custom element tag name prefix. Default `'l'`. */
6
+ elementPrefix?: string;
7
+ /** CSS class/token/keyframe prefix. Default `'l'`. */
8
+ cssPrefix?: string;
9
+ }
10
+
11
+ export default function luxen(options: LuxenOptions = {}): Plugin {
12
+ const elementPrefix = options.elementPrefix || 'l';
13
+ const cssPrefix = options.cssPrefix || 'l';
14
+
15
+ return {
16
+ name: 'luxen',
17
+
18
+ config() {
19
+ if (elementPrefix === 'l' && cssPrefix === 'l') return;
20
+ return {
21
+ css: {
22
+ postcss: {
23
+ plugins: [postcssPrefix({ elementPrefix, cssPrefix })],
24
+ },
25
+ },
26
+ };
27
+ },
28
+ };
29
+ }