@pure-ds/core 0.5.55 → 0.5.57

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/.cursorrules CHANGED
@@ -1,512 +1,520 @@
1
- # PDS (Pure Design System) - AI Generation Instructions
2
-
3
- > **CRITICAL**: This workspace uses **Pure Design System (PDS)**. All code generation MUST follow PDS and vanilla Web Platform patterns. Never use 3rd party framework patterns, non-PDS utility classes, inline styles, or hardcoded CSS values.
4
-
5
- ## Philosophy
6
-
7
- PDS follows the [Pure Web Manifesto](https://pureweb.dev/manifesto): "The browser is the framework."
8
-
9
- 1. **Standards-first**: Web Platform APIs only (no framework dependencies)
10
- 2. **Configuration-driven**: `pds.config.js` generates everything
11
- 3. **Progressive Enhancement**: Semantic HTML first, enhance where needed
12
- 4. **Components as Last Resort**: Web Components only when native HTML cannot achieve it
13
-
14
- ### The Three Layers
15
-
16
- **Layer 1 — Styles**: From minimal config, PDS generates complete CSS: tokens, scales, semantics, surfaces, states. Zero specificity via `:where()`.
17
-
18
- **Layer 2 — Enhancements**: Behavior added to semantic HTML via selector-based upgrades (`data-dropdown`, `data-toggle`, etc.).
19
-
20
- **Layer 3 — Web Components**: `<pds-tabstrip>`, `<pds-drawer>`, etc. only when native HTML has no equivalent.
21
-
22
- ---
23
-
24
- ## 🔍 Single Sources of Truth (ALWAYS CONSULT THESE FIRST)
25
-
26
- **Before generating code, read the relevant SSoT file to get accurate class names, tokens, and APIs.**
27
-
28
- | Need | SSoT File | What It Contains |
29
- |------|-----------|------------------|
30
- | **CSS Tokens** | `public/assets/pds/pds.css-data.json` | All `--color-*`, `--spacing-*`, `--radius-*`, `--shadow-*`, `--font-*` |
31
- | **Web Components** | `custom-elements.json` | Complete component APIs, attributes, methods, events, slots |
32
- | **HTML Tags** | `public/assets/pds/vscode-custom-data.json` | Component HTML structure, attribute values |
33
- | **Primitives & Utilities** | `src/js/pds-core/pds-ontology.js` | `.card`, `.badge`, `.btn-*`, `.flex`, `.gap-*`, `.surface-*` |
34
- | **Enhancements** | `src/js/pds-core/pds-enhancers.js` | Enhancement metadata (`defaultPDSEnhancerMetadata`) + runtime (`defaultPDSEnhancers`) |
35
- | **Generator Logic** | `src/js/pds-core/pds-generator.js` | How CSS is generated, token naming conventions |
36
- | **Config** | `pds.config.js` | What's enabled in this workspace |
37
-
38
- **For consuming projects** using `@pure-ds/core`, files are in `node_modules/@pure-ds/core/`:
39
- - `custom-elements.json`
40
- - `public/assets/pds/pds.css-data.json`
41
- - `public/assets/pds/vscode-custom-data.json`
42
- - `src/js/pds-core/pds-ontology.js`
43
-
44
- **Path resolution helper:** When looking up SSoT files:
45
- 1. First check if `node_modules/@pure-ds/core/` exists (consuming project)
46
- 2. Otherwise use workspace root paths (pure-ds development)
47
- 3. Prefer reading actual files over guessing - the data is authoritative
48
-
49
- ---
50
-
51
- ## 📋 pds-form Best Practices
52
-
53
- **When generating pds-form code, ALWAYS follow these patterns:**
54
-
55
- ### 1. Event Handling - Use `pw:submit`, NOT `submit`
56
-
57
- ```javascript
58
- // ✅ CORRECT: Listen to pw:submit custom event
59
- form.addEventListener('pw:submit', async (e) => {
60
- const { json, formData, valid, issues } = e.detail;
61
- if (valid) {
62
- // Handle submission with json or formData
63
- }
64
- });
65
-
66
- // ❌ WRONG: Native submit event
67
- form.addEventListener('submit', (e) => { /* Won't work */ });
68
- ```
69
-
70
- ### 2. Submit Button Progress - Add `btn-working` automatically
71
-
72
- **When user requests a form with async submission, ALWAYS:**
73
- - Add `btn-working` class to submit button during processing
74
- - Remove it when done (PDS automatically shows spinner icon)
75
- - Use a realistic 2-3 second delay for demos
76
-
77
- ```javascript
78
- // ✅ CORRECT: Auto-add progress state
79
- form.addEventListener('pw:submit', async (e) => {
80
- const submitBtn = form.querySelector('button[type="submit"]');
81
- submitBtn?.classList.add('btn-working');
82
-
83
- try {
84
- await simulateSubmit(e.detail.json); // 2-3 second promise
85
- await PDS.toast('Submitted successfully!', { type: 'success' });
86
- } finally {
87
- submitBtn?.classList.remove('btn-working');
88
- }
89
- });
90
-
91
- async function simulateSubmit(data) {
92
- await new Promise(resolve => setTimeout(resolve, 2000));
93
- console.log('Submitted:', data);
94
- }
95
- ```
96
-
97
- ### 3. Adding `data-required` to pds-form generated form: simply add the attribute to the pds-form tag
98
-
99
- ```html
100
- <pds-form data-required id="myForm" hide-actions></pds-form>
101
- ```
102
-
103
- ### 4. Placeholders - ALWAYS include examples
104
-
105
- **Placeholders improve UX significantly. Try to add 'examples' array to schema properties:**
106
-
107
- **Rule: When generating a form, infer appropriate placeholders based on field name/type if not specified.**
108
-
109
- ### 5. Smart Icons - Infer from field semantics
110
-
111
- **When generating forms, automatically add appropriate icons based on field names and semantics.**
112
-
113
- **Sources of truth for available icons:**
114
- - Check `pds.config.js` for project-specific icon configuration
115
- - Consult icon sprite at `public/assets/img/icons/pds-icons.svg` for available icons
116
- - See `public/assets/pds/vscode-custom-data.json` for icon attribute values
117
-
118
- **Use semantic reasoning to match field names to appropriate icons:**
119
-
120
- ```javascript
121
- // ✅ CORRECT: Infer icons based on field semantics
122
- const uiSchema = {
123
- "/email": { 'ui:icon': 'envelope', 'ui:autocomplete': 'email' },
124
- "/phone": { 'ui:icon': 'phone', 'ui:autocomplete': 'tel' },
125
- "/name": { 'ui:icon': 'user', 'ui:autocomplete': 'name' },
126
- "/password": { 'ui:icon': 'lock', 'ui:widget': 'password' },
127
- "/website": { 'ui:icon': 'link' },
128
- "/address": { 'ui:icon': 'map-pin' },
129
- "/date": { 'ui:icon': 'calendar' },
130
- "/message": { 'ui:widget': 'textarea', 'ui:icon': 'message' }
131
- };
132
- ```
133
-
134
- **Rule: When generating forms, analyze field names/types and select semantically appropriate icons from the available icon set.**
135
-
136
- ### 6. Submit Handler Pattern - ALWAYS provide working async handler
137
-
138
- **When generating a pds-form, ALWAYS include a complete, iteration-ready submit handler with:**
139
- - `pw:submit` event (NOT native submit)
140
- - `btn-working` class for loading state
141
- - `PDS.toast()` for user feedback
142
- - Error handling
143
- - Realistic async simulation
144
-
145
- ```javascript
146
- // ✅ CORRECT: Complete submit handler pattern
147
- form.addEventListener('pw:submit', async (e) => {
148
- const submitBtn = form.querySelector('button[type="submit"]');
149
- submitBtn?.classList.add('btn-working');
150
-
151
- try {
152
- // Simulate async operation (replace with real API call)
153
- await new Promise(resolve => setTimeout(resolve, 2000));
154
-
155
- // Log the data for debugging
156
- console.log('Submitted data:', e.detail.json);
157
-
158
- // Show success toast
159
- await PDS.toast('Form submitted successfully!', { type: 'success' });
160
-
161
- // Optionally reset form
162
- form.reset();
163
- } catch (error) {
164
- // Show error toast
165
- await PDS.toast('Submission failed: ' + error.message, { type: 'error' });
166
- } finally {
167
- // Always remove loading state
168
- submitBtn?.classList.remove('btn-working');
169
- }
170
- });
171
-
172
- // ❌ WRONG: Native submit event
173
- form.addEventListener('submit', (e) => { /* Won't work */ });
174
-
175
- // WRONG: No loading state
176
- form.addEventListener('pw:submit', async (e) => {
177
- await fetch('/api'); // No visual feedback!
178
- });
179
-
180
- // ❌ WRONG: Browser dialogs
181
- form.addEventListener('pw:submit', async (e) => {
182
- alert('Submitted!'); // Use PDS.toast() instead
183
- });
184
- ```
185
-
186
- **PDS.toast() is available globally via window.PDS:**
187
-
188
- ```javascript
189
- // All toast types
190
- await PDS.toast('Success message', { type: 'success' });
191
- await PDS.toast('Error occurred', { type: 'error' });
192
- await PDS.toast('Warning message', { type: 'warning' });
193
- await PDS.toast('Info message', { type: 'information' });
194
-
195
- // Custom duration (auto-calculated by default based on message length)
196
- await PDS.toast('Quick message', { type: 'info', duration: 3000 });
197
-
198
- // Persistent (requires manual close)
199
- await PDS.toast('Important notice', { type: 'warning', persistent: true });
200
- ```
201
-
202
- ### 7. Conditional "Other" Fields - Auto-generate ui:visibleWhen
203
-
204
- **When a schema has an "Other" enum option, ALWAYS auto-generate a conditional text field:**
205
-
206
- ```javascript
207
- const schema = {
208
- type: "object",
209
- properties: {
210
- reason: {
211
- type: "string",
212
- title: "How did you hear about us?",
213
- oneOf: [
214
- { const: "search", title: "Search Engine" },
215
- { const: "social", title: "Social Media" },
216
- { const: "friend", title: "Friend Referral" },
217
- { const: "other", title: "Other... (please specify)" }, // ← "Other" option
218
- ],
219
- },
220
- otherReason: { // ← Conditional field for "Other"
221
- type: "string",
222
- title: "Please specify",
223
- examples: ["Tell us more..."],
224
- },
225
- },
226
- };
227
-
228
- const uiSchema = {
229
- // ✅ ALWAYS add these when "other" enum exists
230
- "/otherReason": {
231
- "ui:visibleWhen": { "/reason": "other" },
232
- "ui:requiredWhen": { "/reason": "other" },
233
- },
234
- };
235
- ```
236
-
237
- ### 8. Complete Working Example
238
-
239
- ```javascript
240
- // Schema with examples for placeholders
241
- const contactSchema = {
242
- type: "object",
243
- required: ["name", "email", "message"],
244
- properties: {
245
- name: {
246
- type: "string",
247
- title: "Name",
248
- minLength: 2,
249
- examples: ["John Doe"]
250
- },
251
- email: {
252
- type: "string",
253
- format: "email",
254
- title: "Email",
255
- examples: ["user@example.com"]
256
- },
257
- message: {
258
- type: "string",
259
- title: "Message",
260
- minLength: 10,
261
- examples: ["Your message here..."]
262
- }
263
- }
264
- };
265
-
266
- // UI schema with smart icons
267
- const uiSchema = {
268
- "/name": { 'ui:icon': 'user' },
269
- "/email": { 'ui:icon': 'envelope' },
270
- "/message": {
271
- 'ui:widget': 'textarea',
272
- 'ui:rows': 4,
273
- 'ui:icon': 'message'
274
- }
275
- };
276
-
277
- // Setup with pw:submit and btn-working
278
- const form = document.getElementById('contactForm');
279
- form.jsonSchema = contactSchema;
280
- form.uiSchema = uiSchema;
281
-
282
- form.addEventListener('pw:submit', async (e) => {
283
- const submitBtn = form.querySelector('button[type="submit"]');
284
- submitBtn?.classList.add('btn-working');
285
-
286
- try {
287
- // Simulate 2s async operation
288
- await new Promise(resolve => setTimeout(resolve, 2000));
289
- console.log('Submitted:', e.detail.json);
290
- await PDS.toast('Message sent!', { type: 'success' });
291
- form.reset();
292
- } catch (error) {
293
- await PDS.toast('Failed to send', { type: 'error' });
294
- } finally {
295
- submitBtn?.classList.remove('btn-working');
296
- }
297
- });
298
- ```
299
-
300
- ---
301
-
302
- ## 🚫 Critical Anti-Patterns (NEVER DO THIS)
303
-
304
- ```html
305
- <!-- ❌ NEVER: Inline styles -->
306
- <div style="display: flex; gap: 16px; padding: 20px;">
307
-
308
- <!-- NEVER: Hardcoded colors -->
309
- <button style="background: #007acc; color: white;">
310
-
311
- <!-- ❌ NEVER: Non-semantic HTML -->
312
- <div class="button" onclick="handleClick()">Click me</div>
313
-
314
- <!-- NEVER: Custom CSS when primitives exist -->
315
- <style>.my-card { border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }</style>
316
- ```
317
-
318
- ```javascript
319
- // ❌ NEVER: Browser dialogs - Use PDS.ask() and PDS.toast()
320
- alert("message"); // → await PDS.toast("message", { type: "info" })
321
- confirm("sure?"); // await PDS.ask("sure?", { type: "confirm" })
322
- prompt("name?"); // → await PDS.ask("name?", { type: "prompt" })
323
-
324
- // NEVER: Manual dropdown/modal/tab implementations
325
- // → Use <nav data-dropdown>, PDS.ask(), <pds-tabstrip>
326
-
327
- // NEVER: Access lazy-loaded component APIs before they're defined
328
- const form = document.querySelector('pds-form');
329
- form.getFormData(); // May fail - component not loaded yet
330
-
331
- // NEVER: Use native 'submit' event with pds-form
332
- form.addEventListener('submit', (e) => { }); // → Use 'pw:submit'
333
-
334
- // NEVER: Forget btn-working class for async operations
335
- button.onclick = async () => {
336
- await fetch('/api'); // No loading indicator!
337
- };
338
- // → Add button.classList.add('btn-working') before, remove after
339
-
340
- // NEVER: Hardcode placeholders instead of using schema examples
341
- const schema = {
342
- properties: {
343
- email: { type: "string" } // Missing examples!
344
- }
345
- };
346
- // → Add examples: ["user@example.com"]
347
- ```
348
-
349
- ---
350
-
351
- ## ⚡ Lit Components & Import Maps
352
-
353
- **Components that require Lit:** `pds-form`
354
-
355
- This component uses `import { ... } from "#pds/lit"` and **requires** an import map:
356
-
357
- ```html
358
- <!-- REQUIRED in HTML <head> for Lit components -->
359
- <script type="importmap">
360
- {
361
- "imports": {
362
- "#pds/lit": "/assets/pds/external/lit.js"
363
- }
364
- }
365
- </script>
366
- ```
367
-
368
- **Note:** `#pds/lit` is a convenience bundle that re-exports Lit and adds PDS helpers (`lazyProps`, `msg()`, `loadLocale()`). If a project prefers its own Lit bundle, it must provide equivalents and map `#pds/lit` accordingly.
369
-
370
- **When generating code with lazy-loaded components, ALWAYS wait for definition:**
371
-
372
- ```javascript
373
- // ✅ CORRECT: Wait for component to load
374
- await customElements.whenDefined('pds-form');
375
- const form = document.querySelector('pds-form');
376
- form.getFormData(); // Safe
377
-
378
- // ✅ CORRECT: Alternative pattern
379
- const FormClass = await customElements.get('pds-form');
380
- if (FormClass) {
381
- const form = document.createElement('pds-form');
382
- // ...
383
- }
384
-
385
- // ❌ WRONG: Direct access without waiting
386
- const form = document.querySelector('pds-form');
387
- form.getFormData(); // May throw error
388
- ```
389
-
390
- ---
391
-
392
- ## ✅ Quick Reference Patterns
393
-
394
- ```html
395
- <!-- Buttons: semantic HTML + PDS classes (see pds-ontology.js → primitives) -->
396
- <button class="btn-primary">Save</button>
397
- <button class="btn-secondary">Cancel</button>
398
- <button class="btn-outline">Details</button>
399
- <button class="btn-primary icon-only" aria-label="Settings">
400
- <pds-icon icon="gear"></pds-icon>
401
- </button>
402
-
403
- <!-- Layout: utility classes (see pds-ontology.js → layoutPatterns, utilities) -->
404
- <div class="flex gap-md items-center">
405
- <div class="grid grid-cols-3 gap-lg">
406
- <div class="stack-md">
407
-
408
- <!-- Cards & Surfaces: primitives -->
409
- <article class="card surface-elevated">
410
- <header class="flex justify-between items-center">
411
- <h3>Title</h3>
412
- </header>
413
- <p class="text-muted">Content</p>
414
- </article>
415
-
416
- <!-- Icons: web component (see custom-elements.json) -->
417
- <pds-icon icon="heart" size="sm"></pds-icon>
418
- <pds-icon icon="check" size="lg" color="var(--color-success-500)"></pds-icon>
419
-
420
- <!-- Enhancements: data attributes (see pds-enhancers.js → defaultPDSEnhancerMetadata) -->
421
- <nav data-dropdown>
422
- <button>Menu</button>
423
- <menu><li><a href="#">Item</a></li></menu>
424
- </nav>
425
-
426
- <label data-toggle>
427
- <input type="checkbox">
428
- <span data-label>Enable feature</span>
429
- </label>
430
-
431
- <form data-required>
432
- <label><span>Email</span><input type="email" required></label>
433
- </form>
434
-
435
- <!-- Tabs: web component -->
436
- <pds-tabstrip>
437
- <pds-tabpanel label="Tab 1">
438
- <p>Content for Tab 1</p>
439
- </pds-tabpanel>
440
- <pds-tabpanel label="Tab 2">
441
- <p>Content for Tab 2</p>
442
- </pds-tabpanel>
443
- </pds-tabstrip>
444
- ```
445
-
446
- ```javascript
447
- // Dialogs & Toasts: PDS API
448
- const confirmed = await PDS.ask("Delete this item?", {
449
- type: "confirm",
450
- buttons: { ok: { name: "Delete", variant: "danger" } }
451
- });
452
-
453
- await PDS.toast("Saved successfully!", { type: "success" });
454
-
455
- // Theme management
456
- PDS.theme = 'dark'; // 'light' | 'dark' | 'system'
457
-
458
- // Query the design system
459
- const results = await PDS.query("border gradient classes");
460
- ```
461
-
462
- ---
463
-
464
- ## 📚 Additional Resources
465
-
466
- **For comprehensive pds-form documentation:**
467
- - Read [pds-form-docs.md](../pds-form-docs.md) for complete API reference
468
- - See [packages/pds-storybook/stories/components/PdsForm.stories.js](../packages/pds-storybook/stories/components/PdsForm.stories.js) for real examples
469
- - Check [custom-elements.json](../custom-elements.json) for component API details
470
-
471
- **For toast notifications:**
472
- - Use `PDS.toast()` method (see [src/js/common/toast.js](../src/js/common/toast.js) for implementation)
473
- - Automatically ensures pds-toaster exists and is loaded before displaying
474
- - See pds-toaster component API in [custom-elements.json](../custom-elements.json)
475
-
476
- ---
477
-
478
- ## How to Look Things Up
479
-
480
- | Question | Action |
481
- |----------|--------|
482
- | "What CSS tokens exist?" | Read `pds.css-data.json` |
483
- | "What components are available?" | Read `custom-elements.json` |
484
- | "What utility classes exist?" | Read `pds-ontology.js` → `layoutPatterns`, `utilities` |
485
- | "What primitives exist?" | Read `pds-ontology.js` → `primitives` |
486
- | "How do I enhance HTML?" | Read `pds-enhancers.js` → `defaultPDSEnhancerMetadata` → `demoHtml` |
487
- | "How are tokens named?" | Read `pds-generator.js` or `pds.css-data.json` |
488
-
489
- ---
490
-
491
- ## Summary Checklist
492
-
493
- Before generating code:
494
-
495
- 1. **Consult SSoT files** Don't guess class names or token names
496
- 2. ✅ **No inline styles** — Use CSS tokens via custom properties
497
- 3. ✅ **No hardcoded values** — Colors, spacing, radii all have tokens
498
- 4. ✅ **No alert/confirm/prompt** — Use `PDS.ask()` and `PDS.toast()`
499
- 5. **Use semantic HTML** — `<button>`, `<nav>`, `<article>`, `<label>`, `<details>`
500
- 6. ✅ **Apply enhancements via data-* attributes** — See `pds-enhancers.js` → `defaultPDSEnhancerMetadata`
501
- 7. **Components as last resort** — Only when native HTML can't achieve it
502
- 8. ✅ **Prefer primitives** — `.card`, `.badge`, `.alert` over custom components
503
- 9. ✅ **Wait for lazy components** — Use `await customElements.whenDefined()` before accessing APIs
504
- 10. ✅ **Include import map** — When using `pds-form` or `pds-drawer`, ensure `#pds/lit` is mapped
505
-
506
- **For pds-form specifically:**
507
-
508
- 11. ✅ **Use `pw:submit` event** — NOT native `submit` event
509
- 12. ✅ **Add `btn-working` class** — For async submit operations, add during processing
510
- 13. ✅ **Use `examples` in JSON schema** First example becomes placeholder
511
- 14. ✅ **Add smart icons** — Infer icons based on field names (email→envelope, phone→phone)
512
- 15. ✅ **Wrap in `form[data-required]`** For asterisk enhancement on required fields
1
+ # PDS (Pure Design System) - AI Generation Instructions
2
+
3
+ > **CRITICAL**: This workspace uses **Pure Design System (PDS)**. All code generation MUST follow PDS and vanilla Web Platform patterns. Never use 3rd party framework patterns, non-PDS utility classes, inline styles, or hardcoded CSS values.
4
+
5
+ ## Philosophy
6
+
7
+ PDS follows the [Pure Web Manifesto](https://pureweb.dev/manifesto): "The browser is the framework."
8
+
9
+ 1. **Standards-first**: Web Platform APIs only (no framework dependencies)
10
+ 2. **Configuration-driven**: `pds.config.js` generates everything
11
+ 3. **Progressive Enhancement**: Semantic HTML first, enhance where needed
12
+ 4. **Components as Last Resort**: Web Components only when native HTML cannot achieve it
13
+
14
+ ### The Four Layers
15
+
16
+ **Layer 1 — Styles**: From minimal config, PDS generates complete CSS: tokens, scales, semantics, surfaces, states. Zero specificity via `:where()`.
17
+
18
+ **Layer 2 — Enhancements**: Behavior added to semantic HTML via selector-based upgrades (`data-dropdown`, `data-toggle`, etc.).
19
+
20
+ **Layer 3 — Web Components**: `<pds-tabstrip>`, `<pds-drawer>`, etc. only when native HTML has no equivalent.
21
+
22
+ **Layer 4: LLM Support**: Install `@pure-ds/core` and get instant PDS AI Coding Instrucions at your fingertips (GitHub Copilot & Cursor support built in)
23
+
24
+ ---
25
+
26
+ ## 🔍 Single Sources of Truth (ALWAYS CONSULT THESE FIRST)
27
+
28
+ **Before generating code, read the relevant SSoT file to get accurate class names, tokens, and APIs.**
29
+
30
+ | Need | SSoT File | What It Contains |
31
+ |------|-----------|------------------|
32
+ | **CSS Tokens** | `public/assets/pds/pds.css-data.json` | All `--color-*`, `--spacing-*`, `--radius-*`, `--shadow-*`, `--font-*` |
33
+ | **Web Components** | `custom-elements.json` | Complete component APIs, attributes, methods, events, slots |
34
+ | **HTML Tags** | `public/assets/pds/vscode-custom-data.json` | Component HTML structure, attribute values |
35
+ | **Primitives & Utilities** | `src/js/pds-core/pds-ontology.js` | `.card`, `.badge`, `.btn-*`, `.flex`, `.gap-*`, `.surface-*` |
36
+ | **Enhancements** | `src/js/pds-core/pds-enhancers.js` | Enhancement metadata (`defaultPDSEnhancerMetadata`) + runtime (`defaultPDSEnhancers`) |
37
+ | **Generator Logic** | `src/js/pds-core/pds-generator.js` | How CSS is generated, token naming conventions |
38
+ | **Config** | `pds.config.js` | What's enabled in this workspace |
39
+
40
+ **For consuming projects** using `@pure-ds/core`, files are in `node_modules/@pure-ds/core/`:
41
+ - `custom-elements.json`
42
+ - `public/assets/pds/pds.css-data.json`
43
+ - `public/assets/pds/vscode-custom-data.json`
44
+ - `src/js/pds-core/pds-ontology.js`
45
+
46
+ **Path resolution helper:** When looking up SSoT files:
47
+ 1. First check if `node_modules/@pure-ds/core/` exists (consuming project)
48
+ 2. Otherwise use workspace root paths (pure-ds development)
49
+ 3. Prefer reading actual files over guessing - the data is authoritative
50
+
51
+ ---
52
+
53
+ ## 📋 pds-form Best Practices
54
+
55
+ **When generating pds-form code, ALWAYS follow these patterns:**
56
+
57
+ ### 1. Event Handling - Use `pw:submit`, NOT `submit`
58
+
59
+ ```javascript
60
+ // CORRECT: Listen to pw:submit custom event
61
+ form.addEventListener('pw:submit', async (e) => {
62
+ const { json, formData, valid, issues } = e.detail;
63
+ if (valid) {
64
+ // Handle submission with json or formData
65
+ }
66
+ });
67
+
68
+ // ❌ WRONG: Native submit event
69
+ form.addEventListener('submit', (e) => { /* Won't work */ });
70
+ ```
71
+
72
+ ### 2. Submit Button Progress - Add `btn-working` automatically
73
+
74
+ **When user requests a form with async submission, ALWAYS:**
75
+ - Add `btn-working` class to submit button during processing
76
+ - Remove it when done (PDS automatically shows spinner icon)
77
+ - Use a realistic 2-3 second delay for demos
78
+
79
+ ```javascript
80
+ // CORRECT: Auto-add progress state
81
+ form.addEventListener('pw:submit', async (e) => {
82
+ const submitBtn = form.querySelector('button[type="submit"]');
83
+ submitBtn?.classList.add('btn-working');
84
+
85
+ try {
86
+ await simulateSubmit(e.detail.json); // 2-3 second promise
87
+ await PDS.toast('Submitted successfully!', { type: 'success' });
88
+ } finally {
89
+ submitBtn?.classList.remove('btn-working');
90
+ }
91
+ });
92
+
93
+ async function simulateSubmit(data) {
94
+ await new Promise(resolve => setTimeout(resolve, 2000));
95
+ console.log('Submitted:', data);
96
+ }
97
+ ```
98
+
99
+ ### 3. Adding `data-required` to pds-form generated form: simply add the attribute to the pds-form tag
100
+
101
+ ```html
102
+ <pds-form data-required id="myForm" hide-actions></pds-form>
103
+ ```
104
+
105
+ ### 4. Placeholders - ALWAYS include examples
106
+
107
+ **Placeholders improve UX significantly. Try to add 'examples' array to schema properties:**
108
+
109
+ **Rule: When generating a form, infer appropriate placeholders based on field name/type if not specified.**
110
+
111
+ ### 5. Smart Icons - Infer from field semantics
112
+
113
+ **When generating forms, automatically add appropriate icons based on field names and semantics.**
114
+
115
+ **Sources of truth for available icons:**
116
+ - Check `pds.config.js` for project-specific icon configuration
117
+ - Consult icon sprite at `public/assets/img/icons/pds-icons.svg` for available icons
118
+ - See `public/assets/pds/vscode-custom-data.json` for icon attribute values
119
+
120
+ **Use semantic reasoning to match field names to appropriate icons:**
121
+
122
+ ```javascript
123
+ // CORRECT: Infer icons based on field semantics
124
+ const uiSchema = {
125
+ "/email": { 'ui:icon': 'envelope', 'ui:autocomplete': 'email' },
126
+ "/phone": { 'ui:icon': 'phone', 'ui:autocomplete': 'tel' },
127
+ "/name": { 'ui:icon': 'user', 'ui:autocomplete': 'name' },
128
+ "/password": { 'ui:icon': 'lock', 'ui:widget': 'password' },
129
+ "/website": { 'ui:icon': 'link' },
130
+ "/address": { 'ui:icon': 'map-pin' },
131
+ "/date": { 'ui:icon': 'calendar' },
132
+ "/message": { 'ui:widget': 'textarea', 'ui:icon': 'message' }
133
+ };
134
+ ```
135
+
136
+ **Rule: When generating forms, analyze field names/types and select semantically appropriate icons from the available icon set.**
137
+
138
+ ### 6. Submit Handler Pattern - ALWAYS provide working async handler
139
+
140
+ **When generating a pds-form, ALWAYS include a complete, iteration-ready submit handler with:**
141
+ - `pw:submit` event (NOT native submit)
142
+ - `btn-working` class for loading state
143
+ - `PDS.toast()` for user feedback
144
+ - Error handling
145
+ - Realistic async simulation
146
+
147
+ ```javascript
148
+ // CORRECT: Complete submit handler pattern
149
+ form.addEventListener('pw:submit', async (e) => {
150
+ const submitBtn = form.querySelector('button[type="submit"]');
151
+ submitBtn?.classList.add('btn-working');
152
+
153
+ try {
154
+ // Simulate async operation (replace with real API call)
155
+ await new Promise(resolve => setTimeout(resolve, 2000));
156
+
157
+ // Log the data for debugging
158
+ console.log('Submitted data:', e.detail.json);
159
+
160
+ // Show success toast
161
+ await PDS.toast('Form submitted successfully!', { type: 'success' });
162
+
163
+ // Optionally reset form
164
+ form.reset();
165
+ } catch (error) {
166
+ // Show error toast
167
+ await PDS.toast('Submission failed: ' + error.message, { type: 'error' });
168
+ } finally {
169
+ // Always remove loading state
170
+ submitBtn?.classList.remove('btn-working');
171
+ }
172
+ });
173
+
174
+ // ❌ WRONG: Native submit event
175
+ form.addEventListener('submit', (e) => { /* Won't work */ });
176
+
177
+ // WRONG: No loading state
178
+ form.addEventListener('pw:submit', async (e) => {
179
+ await fetch('/api'); // No visual feedback!
180
+ });
181
+
182
+ // WRONG: Browser dialogs
183
+ form.addEventListener('pw:submit', async (e) => {
184
+ alert('Submitted!'); // Use PDS.toast() instead
185
+ });
186
+ ```
187
+
188
+ **PDS.toast() is available globally via window.PDS:**
189
+
190
+ ```javascript
191
+ // All toast types
192
+ await PDS.toast('Success message', { type: 'success' });
193
+ await PDS.toast('Error occurred', { type: 'error' });
194
+ await PDS.toast('Warning message', { type: 'warning' });
195
+ await PDS.toast('Info message', { type: 'information' });
196
+
197
+ // Custom duration (auto-calculated by default based on message length)
198
+ await PDS.toast('Quick message', { type: 'info', duration: 3000 });
199
+
200
+ // Persistent (requires manual close)
201
+ await PDS.toast('Important notice', { type: 'warning', persistent: true });
202
+ ```
203
+
204
+ ### 7. Conditional "Other" Fields - Auto-generate ui:visibleWhen
205
+
206
+ **When a schema has an "Other" enum option, ALWAYS auto-generate a conditional text field:**
207
+
208
+ ```javascript
209
+ const schema = {
210
+ type: "object",
211
+ properties: {
212
+ reason: {
213
+ type: "string",
214
+ title: "How did you hear about us?",
215
+ oneOf: [
216
+ { const: "search", title: "Search Engine" },
217
+ { const: "social", title: "Social Media" },
218
+ { const: "friend", title: "Friend Referral" },
219
+ { const: "other", title: "Other... (please specify)" }, // ← "Other" option
220
+ ],
221
+ },
222
+ otherReason: { // ← Conditional field for "Other"
223
+ type: "string",
224
+ title: "Please specify",
225
+ examples: ["Tell us more..."],
226
+ },
227
+ },
228
+ };
229
+
230
+ const uiSchema = {
231
+ // ALWAYS add these when "other" enum exists
232
+ "/otherReason": {
233
+ "ui:visibleWhen": { "/reason": "other" },
234
+ "ui:requiredWhen": { "/reason": "other" },
235
+ },
236
+ };
237
+ ```
238
+
239
+ ### 8. Complete Working Example
240
+
241
+ ```javascript
242
+ // Schema with examples for placeholders
243
+ const contactSchema = {
244
+ type: "object",
245
+ required: ["name", "email", "message"],
246
+ properties: {
247
+ name: {
248
+ type: "string",
249
+ title: "Name",
250
+ minLength: 2,
251
+ examples: ["John Doe"]
252
+ },
253
+ email: {
254
+ type: "string",
255
+ format: "email",
256
+ title: "Email",
257
+ examples: ["user@example.com"]
258
+ },
259
+ message: {
260
+ type: "string",
261
+ title: "Message",
262
+ minLength: 10,
263
+ examples: ["Your message here..."]
264
+ }
265
+ }
266
+ };
267
+
268
+ // UI schema with smart icons
269
+ const uiSchema = {
270
+ "/name": { 'ui:icon': 'user' },
271
+ "/email": { 'ui:icon': 'envelope' },
272
+ "/message": {
273
+ 'ui:widget': 'textarea',
274
+ 'ui:rows': 4,
275
+ 'ui:icon': 'message'
276
+ }
277
+ };
278
+
279
+ // Setup with pw:submit and btn-working
280
+ const form = document.getElementById('contactForm');
281
+ form.jsonSchema = contactSchema;
282
+ form.uiSchema = uiSchema;
283
+
284
+ form.addEventListener('pw:submit', async (e) => {
285
+ const submitBtn = form.querySelector('button[type="submit"]');
286
+ submitBtn?.classList.add('btn-working');
287
+
288
+ try {
289
+ // Simulate 2s async operation
290
+ await new Promise(resolve => setTimeout(resolve, 2000));
291
+ console.log('Submitted:', e.detail.json);
292
+ await PDS.toast('Message sent!', { type: 'success' });
293
+ form.reset();
294
+ } catch (error) {
295
+ await PDS.toast('Failed to send', { type: 'error' });
296
+ } finally {
297
+ submitBtn?.classList.remove('btn-working');
298
+ }
299
+ });
300
+ ```
301
+
302
+ ---
303
+
304
+ ## 🚫 Critical Anti-Patterns (NEVER DO THIS)
305
+
306
+ ```html
307
+ <!-- ❌ NEVER: Inline styles -->
308
+ <div style="display: flex; gap: 16px; padding: 20px;">
309
+
310
+ <!-- ❌ NEVER: Hardcoded colors -->
311
+ <button style="background: #007acc; color: white;">
312
+
313
+ <!-- ❌ NEVER: Non-semantic HTML -->
314
+ <div class="button" onclick="handleClick()">Click me</div>
315
+
316
+ <!-- ❌ NEVER: Custom CSS when primitives exist -->
317
+ <style>.my-card { border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }</style>
318
+ ```
319
+
320
+ ```javascript
321
+ // NEVER: Browser dialogs - Use PDS.ask() and PDS.toast()
322
+ alert("message"); // → await PDS.toast("message", { type: "info" })
323
+ confirm("sure?"); // → await PDS.ask("sure?", { type: "confirm" })
324
+ prompt("name?"); // await PDS.ask("name?", { type: "prompt" })
325
+
326
+ // ❌ NEVER: Manual dropdown/modal/tab implementations
327
+ // Use <nav data-dropdown>, PDS.ask(), <pds-tabstrip>
328
+
329
+ // NEVER: Access lazy-loaded component APIs before they're defined
330
+ const form = document.querySelector('pds-form');
331
+ form.getFormData(); // May fail - component not loaded yet
332
+
333
+ // ❌ NEVER: Use native 'submit' event with pds-form
334
+ form.addEventListener('submit', (e) => { }); // Use 'pw:submit'
335
+
336
+ // NEVER: Forget btn-working class for async operations
337
+ button.onclick = async () => {
338
+ await fetch('/api'); // No loading indicator!
339
+ };
340
+ // Add button.classList.add('btn-working') before, remove after
341
+
342
+ // ❌ NEVER: Hardcode placeholders instead of using schema examples
343
+ const schema = {
344
+ properties: {
345
+ email: { type: "string" } // Missing examples!
346
+ }
347
+ };
348
+ // → Add examples: ["user@example.com"]
349
+ ```
350
+
351
+ ---
352
+
353
+ ## ⚡ Lit Components & Import Maps
354
+
355
+ **Components that require Lit:** `pds-form`
356
+
357
+ This component uses `import { ... } from "#pds/lit"` and **requires** an import map:
358
+
359
+ ```html
360
+ <!-- REQUIRED in HTML <head> for Lit components -->
361
+ <script type="importmap">
362
+ {
363
+ "imports": {
364
+ "#pds/lit": "/assets/pds/external/lit.js"
365
+ }
366
+ }
367
+ </script>
368
+ ```
369
+
370
+ **Note:** `#pds/lit` is a convenience bundle that re-exports Lit and adds PDS helpers (`lazyProps`, `msg()`, `loadLocale()`). If a project prefers its own Lit bundle, it must provide equivalents and map `#pds/lit` accordingly.
371
+
372
+ **When generating code with lazy-loaded components, ALWAYS wait for definition:**
373
+
374
+ ```javascript
375
+ // CORRECT: Wait for component to load
376
+ await customElements.whenDefined('pds-form');
377
+ const form = document.querySelector('pds-form');
378
+ form.getFormData(); // Safe
379
+
380
+ // CORRECT: Alternative pattern
381
+ const FormClass = await customElements.get('pds-form');
382
+ if (FormClass) {
383
+ const form = document.createElement('pds-form');
384
+ // ...
385
+ }
386
+
387
+ // WRONG: Direct access without waiting
388
+ const form = document.querySelector('pds-form');
389
+ form.getFormData(); // May throw error
390
+ ```
391
+
392
+ ---
393
+
394
+ ## ✅ Quick Reference Patterns
395
+
396
+ ```html
397
+ <!-- Buttons: semantic HTML + PDS classes (see pds-ontology.js → primitives) -->
398
+ <button class="btn-primary">Save</button>
399
+ <button class="btn-secondary">Cancel</button>
400
+ <button class="btn-outline">Details</button>
401
+ <button class="btn-primary icon-only" aria-label="Settings">
402
+ <pds-icon icon="gear"></pds-icon>
403
+ </button>
404
+
405
+ <!-- Layout: utility classes (see pds-ontology.js → layoutPatterns, utilities) -->
406
+ <div class="flex gap-md items-center">
407
+ <div class="grid grid-cols-3 gap-lg">
408
+ <div class="stack-md">
409
+
410
+ <!-- Cards & Surfaces: primitives -->
411
+ <article class="card surface-elevated">
412
+ <header class="flex justify-between items-center">
413
+ <h3>Title</h3>
414
+ </header>
415
+ <p class="text-muted">Content</p>
416
+ </article>
417
+
418
+ <!-- Icons: web component (see custom-elements.json) -->
419
+ <pds-icon icon="heart" size="sm"></pds-icon>
420
+ <pds-icon icon="check" size="lg" color="var(--color-success-500)"></pds-icon>
421
+
422
+ <!-- Enhancements: data attributes (see pds-enhancers.js → defaultPDSEnhancerMetadata) -->
423
+ <nav data-dropdown>
424
+ <button>Menu</button>
425
+ <menu><li><a href="#">Item</a></li></menu>
426
+ </nav>
427
+
428
+ <label data-toggle>
429
+ <input type="checkbox">
430
+ <span data-label>Enable feature</span>
431
+ </label>
432
+
433
+ <form data-required>
434
+ <label><span>Email</span><input type="email" required></label>
435
+ </form>
436
+
437
+ <!-- Tabs: web component -->
438
+ <pds-tabstrip>
439
+ <pds-tabpanel label="Tab 1">
440
+ <p>Content for Tab 1</p>
441
+ </pds-tabpanel>
442
+ <pds-tabpanel label="Tab 2">
443
+ <p>Content for Tab 2</p>
444
+ </pds-tabpanel>
445
+ </pds-tabstrip>
446
+ ```
447
+
448
+ ### Empty State Pattern
449
+
450
+ - Use the `.empty-state` primitive for empty or onboarding states.
451
+ - Structure: heading + supporting text, an icon, then primary/secondary actions.
452
+ - Keep actions as buttons or links with PDS button classes, and include a meaningful icon when available.
453
+
454
+ ```javascript
455
+ // Dialogs & Toasts: PDS API
456
+ const confirmed = await PDS.ask("Delete this item?", {
457
+ type: "confirm",
458
+ buttons: { ok: { name: "Delete", variant: "danger" } }
459
+ });
460
+
461
+ await PDS.toast("Saved successfully!", { type: "success" });
462
+
463
+ // Theme management
464
+ PDS.theme = 'dark'; // 'light' | 'dark' | 'system'
465
+
466
+ // Query the design system
467
+ const results = await PDS.query("border gradient classes");
468
+ ```
469
+
470
+ ---
471
+
472
+ ## 📚 Additional Resources
473
+
474
+ **For comprehensive pds-form documentation:**
475
+ - Read [pds-form-docs.md](../pds-form-docs.md) for complete API reference
476
+ - See [packages/pds-storybook/stories/components/PdsForm.stories.js](../packages/pds-storybook/stories/components/PdsForm.stories.js) for real examples
477
+ - Check [custom-elements.json](../custom-elements.json) for component API details
478
+
479
+ **For toast notifications:**
480
+ - Use `PDS.toast()` method (see [src/js/common/toast.js](../src/js/common/toast.js) for implementation)
481
+ - Automatically ensures pds-toaster exists and is loaded before displaying
482
+ - See pds-toaster component API in [custom-elements.json](../custom-elements.json)
483
+
484
+ ---
485
+
486
+ ## How to Look Things Up
487
+
488
+ | Question | Action |
489
+ |----------|--------|
490
+ | "What CSS tokens exist?" | Read `pds.css-data.json` |
491
+ | "What components are available?" | Read `custom-elements.json` |
492
+ | "What utility classes exist?" | Read `pds-ontology.js` → `layoutPatterns`, `utilities` |
493
+ | "What primitives exist?" | Read `pds-ontology.js` → `primitives` |
494
+ | "How do I enhance HTML?" | Read `pds-enhancers.js` → `defaultPDSEnhancerMetadata` → `demoHtml` |
495
+ | "How are tokens named?" | Read `pds-generator.js` or `pds.css-data.json` |
496
+
497
+ ---
498
+
499
+ ## Summary Checklist
500
+
501
+ Before generating code:
502
+
503
+ 1. ✅ **Consult SSoT files** — Don't guess class names or token names
504
+ 2. ✅ **No inline styles** — Use CSS tokens via custom properties
505
+ 3. ✅ **No hardcoded values** — Colors, spacing, radii all have tokens
506
+ 4. ✅ **No alert/confirm/prompt** — Use `PDS.ask()` and `PDS.toast()`
507
+ 5. ✅ **Use semantic HTML** — `<button>`, `<nav>`, `<article>`, `<label>`, `<details>`
508
+ 6. ✅ **Apply enhancements via data-* attributes** — See `pds-enhancers.js` `defaultPDSEnhancerMetadata`
509
+ 7. ✅ **Components as last resort** — Only when native HTML can't achieve it
510
+ 8. ✅ **Prefer primitives** `.card`, `.badge`, `.callout` over custom components
511
+ 9. ✅ **Wait for lazy components** — Use `await customElements.whenDefined()` before accessing APIs
512
+ 10. ✅ **Include import map** — When using `pds-form` or `pds-drawer`, ensure `#pds/lit` is mapped
513
+
514
+ **For pds-form specifically:**
515
+
516
+ 11. ✅ **Use `pw:submit` event** — NOT native `submit` event
517
+ 12. ✅ **Add `btn-working` class** — For async submit operations, add during processing
518
+ 13. ✅ **Use `examples` in JSON schema** — First example becomes placeholder
519
+ 14. ✅ **Add smart icons** — Infer icons based on field names (email→envelope, phone→phone)
520
+ 15. ✅ **Wrap in `form[data-required]`** — For asterisk enhancement on required fields