@andersseen/skills 0.0.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.
@@ -0,0 +1,478 @@
1
+ ---
2
+ name: andersseen-web-components
3
+ description:
4
+ 'Use ready-made, styled Andersseen UI custom elements
5
+ (@andersseen/web-components). Load when placing or wiring and-* elements —
6
+ and-button, and-card, and-modal, and-tabs, and-drawer, and-dropdown,
7
+ and-select, and-input, and-navbar, and-sidebar, and-toast, and-tooltip,
8
+ and-accordion, and-alert, and-badge, and-breadcrumb, and-carousel,
9
+ and-pagination — in plain HTML, Angular, React, Vue, or Astro. Trigger
10
+ phrases: and-* component, andersseen web component, stencil custom element,
11
+ shadow dom UI, design tokens.'
12
+ ---
13
+
14
+ # @andersseen/web-components — styled UI custom elements
15
+
16
+ Stencil custom-elements package. All tags share the `and-*` prefix and render in
17
+ **Shadow DOM** — never depend on internal shadow markup or classes. Built on top
18
+ of `@andersseen/headless-core` and `@andersseen/icon`. Framework-agnostic: works
19
+ in plain HTML, Angular, React, Vue, Astro.
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ npm i @andersseen/web-components @andersseen/icon
25
+ ```
26
+
27
+ ## Setup (bundled app)
28
+
29
+ ```ts
30
+ // Register all custom elements
31
+ import '@andersseen/web-components/components/all';
32
+ import '@andersseen/web-components/dist/web-components/web-components.css';
33
+
34
+ // Register icons used by internal components (minimum required)
35
+ import { registerIcons, COMPONENT_ICONS } from '@andersseen/icon';
36
+ registerIcons(COMPONENT_ICONS);
37
+
38
+ // Optional: enable CSS transitions across all components
39
+ import { enableAnimations } from '@andersseen/web-components';
40
+ enableAnimations();
41
+ ```
42
+
43
+ ## Design tokens
44
+
45
+ All tokens use HSL and are applied via CSS variables on `:root`. Dark mode: add
46
+ class `dark` (or `data-theme="dark"`) to `<html>` or any ancestor.
47
+
48
+ Key semantic tokens:
49
+
50
+ ```
51
+ --primary / --primary-foreground --secondary / --secondary-foreground
52
+ --background / --foreground --card / --card-foreground
53
+ --muted / --muted-foreground --accent / --accent-foreground
54
+ --destructive / --destructive-foreground
55
+ --border / --input / --ring
56
+ --success / --warning / --info (each with a -foreground pair)
57
+ ```
58
+
59
+ Prefer `hsl(var(--token))` over hardcoded colors when extending styles.
60
+
61
+ ## Component contracts
62
+
63
+ ### and-button
64
+
65
+ ```
66
+ Props : variant (default|destructive|outline|secondary|ghost|link)
67
+ size (default|sm|lg|icon) type (button|submit|reset)
68
+ disabled loading href target rel customClass
69
+ Events : andButtonClick → MouseEvent
70
+ Slots : [start] label text [end]
71
+ ```
72
+
73
+ ```html
74
+ <and-button variant="default">Save</and-button>
75
+ <and-button variant="outline" size="sm" loading>
76
+ <and-icon slot="start" name="sparkles" size="16"></and-icon>
77
+ Generate
78
+ </and-button>
79
+ ```
80
+
81
+ ### and-card
82
+
83
+ ```
84
+ Props : variant (default|destructive|elevated|outline|ghost) padded customClass
85
+ Sub-components: and-card-header · and-card-title · and-card-description
86
+ and-card-content · and-card-footer
87
+ ```
88
+
89
+ ```html
90
+ <and-card variant="elevated" padded>
91
+ <and-card-header>
92
+ <and-card-title>Title</and-card-title>
93
+ <and-card-description>Subtitle</and-card-description>
94
+ </and-card-header>
95
+ <and-card-content>Body content</and-card-content>
96
+ <and-card-footer>Footer</and-card-footer>
97
+ </and-card>
98
+ ```
99
+
100
+ ### and-accordion
101
+
102
+ ```
103
+ Props : allowMultiple (bool) defaultValue (string[]) orientation disabled
104
+ Sub-components: and-accordion-item (value, disabled)
105
+ and-accordion-trigger (default slot → label)
106
+ and-accordion-content (default slot → body)
107
+ ```
108
+
109
+ ```html
110
+ <and-accordion allow-multiple>
111
+ <and-accordion-item value="q1">
112
+ <and-accordion-trigger>Question 1</and-accordion-trigger>
113
+ <and-accordion-content>Answer 1</and-accordion-content>
114
+ </and-accordion-item>
115
+ </and-accordion>
116
+ ```
117
+
118
+ ### and-alert
119
+
120
+ ```
121
+ Props : variant (default|destructive|success|warning|info) dismissible
122
+ Events : andDismiss → void
123
+ Slots : [icon] body text
124
+ ```
125
+
126
+ ```html
127
+ <and-alert variant="success" dismissible>
128
+ <and-icon slot="icon" name="check-circle" size="16"></and-icon>
129
+ Profile saved successfully.
130
+ </and-alert>
131
+ ```
132
+
133
+ ### and-badge
134
+
135
+ ```
136
+ Props : variant (default|secondary|destructive|outline) customClass
137
+ ```
138
+
139
+ ```html
140
+ <and-badge variant="secondary">Beta</and-badge>
141
+ ```
142
+
143
+ ### and-breadcrumb + and-breadcrumb-item
144
+
145
+ ```
146
+ Props (container) : size (sm|md|lg) navLabel customClass
147
+ Props (item) : href current (bool) size hideSeparator customClass
148
+ Events (item) : andBreadcrumbNavigate → string (href)
149
+ ```
150
+
151
+ ```html
152
+ <and-breadcrumb>
153
+ <and-breadcrumb-item href="/">Home</and-breadcrumb-item>
154
+ <and-breadcrumb-item href="/products">Products</and-breadcrumb-item>
155
+ <and-breadcrumb-item current>Laptop</and-breadcrumb-item>
156
+ </and-breadcrumb>
157
+ ```
158
+
159
+ ### and-carousel + and-carousel-item
160
+
161
+ ```
162
+ Props (container) : autoplay (bool) interval (ms) label (ARIA)
163
+ Events : andSlideChange → number (0-based index)
164
+ Props (item) : label (ARIA slide label)
165
+ Keyboard : ArrowLeft / ArrowRight
166
+ ```
167
+
168
+ ```html
169
+ <and-carousel autoplay interval="5000">
170
+ <and-carousel-item label="Slide 1"
171
+ ><img src="..." alt=""
172
+ /></and-carousel-item>
173
+ <and-carousel-item label="Slide 2"
174
+ ><img src="..." alt=""
175
+ /></and-carousel-item>
176
+ </and-carousel>
177
+ ```
178
+
179
+ ### and-context-menu
180
+
181
+ ```
182
+ Props : open (mutable bool) customClass
183
+ Events : andContextMenuOpenChange → boolean
184
+ Slots : [trigger] (right-click target) default (menu items)
185
+ ```
186
+
187
+ ```html
188
+ <and-context-menu>
189
+ <div slot="trigger">Right-click me</div>
190
+ <and-menu-item value="copy">Copy</and-menu-item>
191
+ <and-menu-item value="delete" intent="destructive">Delete</and-menu-item>
192
+ </and-context-menu>
193
+ ```
194
+
195
+ ### and-drawer
196
+
197
+ ```
198
+ Props : open (mutable bool) placement (left|right|top|bottom) showClose (bool)
199
+ Events : andDrawerOpen · andDrawerClose
200
+ Slots : [header] [close-icon] default (body) [footer]
201
+ ```
202
+
203
+ ```html
204
+ <and-drawer id="nav" placement="right">
205
+ <span slot="header">Settings</span>
206
+ <p>Drawer body content</p>
207
+ </and-drawer>
208
+ <and-button onclick="document.getElementById('nav').open = true"
209
+ >Open</and-button
210
+ >
211
+ ```
212
+
213
+ ### and-dropdown
214
+
215
+ ```
216
+ Props : items (DropdownItem[]) variant label placement (top|bottom|left|right) closeOnSelect
217
+ DropdownItem: { text: string; value: string; disabled?: boolean }
218
+ Events : andDropdownSelect → string (value) andDropdownOpenChange → boolean
219
+ Slots : [trigger] (custom trigger element)
220
+ ```
221
+
222
+ ```html
223
+ <and-dropdown id="theme" label="Theme"></and-dropdown>
224
+ ```
225
+
226
+ ```ts
227
+ const dd = document.querySelector('#theme');
228
+ dd.items = [
229
+ { text: 'Light', value: 'light' },
230
+ { text: 'Dark', value: 'dark' },
231
+ ];
232
+ dd.addEventListener('andDropdownSelect', e => {
233
+ document.documentElement.className = e.detail;
234
+ });
235
+ ```
236
+
237
+ ### and-icon
238
+
239
+ ```
240
+ Props : name (IconName — must be registered) size (default 24) color (default currentColor) strokeWidth (default 2)
241
+ ```
242
+
243
+ ```html
244
+ <and-icon name="home" size="20"></and-icon>
245
+ ```
246
+
247
+ ### and-input
248
+
249
+ Renders in **light DOM** (not Shadow DOM) — the real `<input>` is a descendant
250
+ of any wrapping `<form>`, so `FormData`, native validation, and autofill work
251
+ with no extra wiring. Set `name` for it to show up in `FormData`.
252
+
253
+ ```
254
+ Props : value (mutable string) type name placeholder disabled required hasError label describedBy customClass
255
+ Events : andInputChange → string (current value) andInputBlur → void
256
+ ```
257
+
258
+ ```html
259
+ <and-input
260
+ id="email"
261
+ name="email"
262
+ type="email"
263
+ placeholder="you@example.com"
264
+ required
265
+ ></and-input>
266
+ ```
267
+
268
+ ### and-select
269
+
270
+ Custom `role="combobox"` select — styleable unlike a native `<select>`, full
271
+ keyboard support (arrow keys, Home/End, typeahead). Also renders in light DOM:
272
+ set `name` and its hidden mirror input shows up in `FormData`.
273
+
274
+ ```
275
+ Props : options (array or JSON string of {text,value,disabled?}) value (mutable string) name placeholder
276
+ disabled required hasError menuPlacement (auto|top|bottom) label describedBy customClass
277
+ Events : andSelectChange → string (selected value) andSelectBlur → void
278
+ ```
279
+
280
+ ```html
281
+ <and-select name="country" placeholder="Choose a country"></and-select>
282
+ ```
283
+
284
+ ```ts
285
+ document.querySelector('and-select').options = [{ text: 'Spain', value: 'es' }];
286
+ ```
287
+
288
+ ### and-control
289
+
290
+ Generic form-field wrapper for anything and-input/and-select don't cover — it
291
+ renders no control of its own. Slot in a native `<textarea>`, native `<select>`,
292
+ a third-party widget, or and-input/and-select, and it wires the label's
293
+ `for`/`id` and the message's `aria-describedby` to the first slotted element
294
+ automatically.
295
+
296
+ ```
297
+ Props : label hint error (shown instead of hint, sets aria-invalid on the control) required (visual only)
298
+ ```
299
+
300
+ ```html
301
+ <and-control label="Bio" hint="Max 200 characters">
302
+ <textarea name="bio" maxlength="200"></textarea>
303
+ </and-control>
304
+ ```
305
+
306
+ ### and-code
307
+
308
+ Read-only command/code snippet block with a copy-to-clipboard button. Plain text
309
+ only — no syntax highlighting; `prompt` is just a display hint. Prompt is hidden
310
+ unless `show-prompt` is set.
311
+
312
+ ```
313
+ Props : value prompt (default `$`) showPrompt (default false) copyable (default true) height customClass
314
+ Events : andCodeCopy → { value: string; success: boolean }
315
+ ```
316
+
317
+ ```html
318
+ <and-code show-prompt="true" prompt="$" value="pnpm install"></and-code>
319
+ ```
320
+
321
+ ### and-menu-list + and-menu-item
322
+
323
+ ```
324
+ Props (list) : ariaMenuLabel customClass
325
+ Props (item) : intent (default|destructive) disabled value customClass
326
+ Events (item): andMenuItemSelect → string (value)
327
+ ```
328
+
329
+ ```html
330
+ <and-menu-list>
331
+ <and-menu-item value="edit">Edit</and-menu-item>
332
+ <and-menu-item value="delete" intent="destructive">Delete</and-menu-item>
333
+ </and-menu-list>
334
+ ```
335
+
336
+ ### and-modal
337
+
338
+ ```
339
+ Props : open (mutable bool)
340
+ Events : andClose → void (backdrop, X button, or Escape)
341
+ Slots : default (body — close button auto-rendered top-right)
342
+ ```
343
+
344
+ ```html
345
+ <and-modal id="confirm">
346
+ <h2>Are you sure?</h2>
347
+ <and-button onclick="document.getElementById('confirm').open = false"
348
+ >Cancel</and-button
349
+ >
350
+ </and-modal>
351
+ <and-button onclick="document.getElementById('confirm').open = true"
352
+ >Open</and-button
353
+ >
354
+ ```
355
+
356
+ ### and-navbar
357
+
358
+ ```
359
+ Props : items (NavItem[] | JSON string) activeItem (mutable)
360
+ variant (default|filled|floating|glass) position (static|sticky|fixed)
361
+ itemVariant (default|underline|filled) scrollSpy scrollSpyOffset
362
+ mobileBreakpoint minimalBreakpoint compactBreakpoint
363
+ NavItem : { id, label, icon?, href?, target?, disabled? }
364
+ Events : navItemClick → string navLinkClick → { id, href }
365
+ mobileMenuChange → boolean responsiveStageChange → stage
366
+ Slots : [start/brand] [main/nav] [end/actions] [toggle-icon] [mobile-title] [mobile-nav] [mobile-actions]
367
+ Responsive stages (auto): full → compact → minimal → mobile
368
+ ```
369
+
370
+ ```ts
371
+ const nav = document.getElementById('main-nav');
372
+ nav.items = [
373
+ { id: 'home', label: 'Home', href: '#home' },
374
+ { id: 'docs', label: 'Docs', href: '#docs' },
375
+ ];
376
+ nav.addEventListener('navItemClick', e => (nav.activeItem = e.detail));
377
+ ```
378
+
379
+ ### and-pagination
380
+
381
+ ```
382
+ Props : totalPages (number) currentPage (mutable number)
383
+ Events : andPageChange → number (1-based page)
384
+ ```
385
+
386
+ ```html
387
+ <and-pagination id="pager" total-pages="10" current-page="1"></and-pagination>
388
+ ```
389
+
390
+ ### and-sidebar
391
+
392
+ ```
393
+ Props : items (SidebarItem[] | JSON string) activeItem (mutable) collapsed (mutable bool)
394
+ variant (default|filled|floating|glass) itemVariant (default|underline|filled)
395
+ mobileCollapse mobileBreakpoint expandedWidth ('16rem') collapsedWidth ('4rem')
396
+ SidebarItem: { id, label, icon?, disabled?, section?: 'main'|'bottom' }
397
+ Events : andSidebarItemClick → string andSidebarToggle → boolean (collapsed)
398
+ Slots : [header] [top] [nav] [footer]
399
+ CSS vars : --sidebar-expanded-width, --sidebar-collapsed-width
400
+ ```
401
+
402
+ ```ts
403
+ const sidebar = document.getElementById('sidebar');
404
+ sidebar.items = [
405
+ { id: 'home', label: 'Home', icon: 'home' },
406
+ { id: 'settings', label: 'Settings', icon: 'settings', section: 'bottom' },
407
+ ];
408
+ sidebar.addEventListener(
409
+ 'andSidebarItemClick',
410
+ e => (sidebar.activeItem = e.detail),
411
+ );
412
+ ```
413
+
414
+ ### and-tabs
415
+
416
+ ```
417
+ Props : value (mutable — controlled) defaultValue (uncontrolled)
418
+ orientation (horizontal|vertical) activationMode (automatic|manual)
419
+ Events : andTabChange → string (selected value)
420
+ Sub-components: and-tabs-list → and-tabs-trigger (value, disabled) and-tabs-content (value)
421
+ ```
422
+
423
+ ```html
424
+ <and-tabs default-value="overview">
425
+ <and-tabs-list>
426
+ <and-tabs-trigger value="overview">Overview</and-tabs-trigger>
427
+ <and-tabs-trigger value="settings">Settings</and-tabs-trigger>
428
+ </and-tabs-list>
429
+ <and-tabs-content value="overview">Overview panel content</and-tabs-content>
430
+ <and-tabs-content value="settings">Settings panel content</and-tabs-content>
431
+ </and-tabs>
432
+ ```
433
+
434
+ ### and-toast
435
+
436
+ Programmatic only (no props / slots).
437
+
438
+ ```
439
+ Method : present(message, type?, duration?) → Promise<number>
440
+ type: 'default'|'destructive'|'success'|'error'|'info'|'warning'
441
+ duration: ms (default 3000)
442
+ ```
443
+
444
+ ```html
445
+ <and-toast id="toaster"></and-toast>
446
+ ```
447
+
448
+ ```ts
449
+ const toaster = document.getElementById('toaster');
450
+ await toaster.present('Profile saved', 'success');
451
+ await toaster.present('Connection failed', 'error', 5000);
452
+ ```
453
+
454
+ ### and-tooltip
455
+
456
+ ```
457
+ Props : content (string) placement (top|bottom|left|right) openDelay (ms) closeDelay (ms)
458
+ Slots : default (trigger element) [content] (when no content prop)
459
+ ```
460
+
461
+ ```html
462
+ <and-tooltip content="Copy to clipboard" placement="top">
463
+ <and-button size="icon"
464
+ ><and-icon name="copy" size="16"></and-icon
465
+ ></and-button>
466
+ </and-tooltip>
467
+ ```
468
+
469
+ ## Rules
470
+
471
+ - Use public props, slots, and CustomEvent APIs only. Never target internal
472
+ shadow parts.
473
+ - Prefer CSS token variables (e.g. `hsl(var(--primary))`) over hex/rgb values.
474
+ - Icon `name` must match a registered key; always pair with
475
+ `registerIcons`/`registerAllIcons` (see the `andersseen-icon` skill).
476
+ - For array-type props (`items`, `defaultValue`) set them via JS, not attribute
477
+ strings, when possible.
478
+ - Mutable props marked above can be set directly as element properties.