@pure-ds/core 0.7.56 → 0.7.58
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 +63 -589
- package/.github/copilot-instructions.md +63 -598
- package/custom-elements.json +110 -5
- package/dist/types/pds.d.ts +65 -0
- package/dist/types/src/js/common/common.d.ts +7 -0
- package/dist/types/src/js/common/common.d.ts.map +1 -1
- package/dist/types/src/js/pds-autocomplete.d.ts +74 -1
- package/dist/types/src/js/pds-autocomplete.d.ts.map +1 -1
- package/dist/types/src/js/pds-core/pds-start-helpers.d.ts.map +1 -1
- package/dist/types/src/js/pds-reactive.d.ts +101 -0
- package/dist/types/src/js/pds-reactive.d.ts.map +1 -0
- package/dist/types/src/js/pds.d.ts +4 -1
- package/dist/types/src/js/pds.d.ts.map +1 -1
- package/package.json +1 -2
- package/packages/pds-cli/lib/pds-mcp-core.js +436 -1
- package/public/assets/js/app.js +7 -6
- package/public/assets/js/pds-ask.js +4 -4
- package/public/assets/js/pds-autocomplete.js +7 -7
- package/public/assets/js/pds-manager.js +144 -143
- package/public/assets/js/pds.js +3 -2
- package/public/assets/pds/components/pds-calendar.js +103 -7
- package/public/assets/pds/components/pds-fab.js +2 -2
- package/public/assets/pds/core/pds-ask.js +4 -4
- package/public/assets/pds/core/pds-autocomplete.js +7 -7
- package/public/assets/pds/core/pds-manager.js +144 -143
- package/public/assets/pds/core.js +3 -2
- package/public/assets/pds/custom-elements.json +382 -28
- package/public/assets/pds/pds-css-complete.json +1 -1
- package/public/assets/pds/vscode-custom-data.json +29 -1
- package/src/js/common/common.js +74 -0
- package/src/js/pds-core/pds-start-helpers.js +17 -2
- package/src/js/pds.d.ts +65 -0
- package/src/js/pds.js +13 -0
|
@@ -1,636 +1,101 @@
|
|
|
1
|
-
# PDS (Pure Design System)
|
|
1
|
+
# PDS (Pure Design System) — AI Instructions
|
|
2
2
|
|
|
3
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
4
|
|
|
5
|
-
> **CRITICAL — Unless
|
|
5
|
+
> **CRITICAL — Unless in the pure-ds origin project, `/pds/` PATHS ARE READ-ONLY**: Files under `public/pds/**`, `public/assets/pds/**`, and `node_modules/@pure-ds/core/public/**` are immutable package output. To change behavior, edit source (`src/js/pds-core/**`, `pds.config.js`) then rebuild.
|
|
6
6
|
|
|
7
|
-
> **Note:
|
|
8
|
-
|
|
9
|
-
## Philosophy
|
|
10
|
-
|
|
11
|
-
PDS follows the [Pure Web Manifesto](https://pureweb.dev/manifesto): "The browser is the framework."
|
|
12
|
-
|
|
13
|
-
1. **Standards-first**: Web Platform APIs only (no framework dependencies)
|
|
14
|
-
2. **Configuration-driven**: `pds.config.js` generates everything
|
|
15
|
-
3. **Progressive Enhancement**: Semantic HTML first, enhance where needed
|
|
16
|
-
4. **Components as Last Resort**: Web Components only when native HTML cannot achieve it
|
|
17
|
-
|
|
18
|
-
> Note: all (lazy loaded) PDS web components are rolled out to consuming projects in their [public root]/assets/pds/components/ folder, and are never provided as source that needs to be bundled. The web component scripts are copied there at npm install time (postinstall), and are ready to be used because the PDS AutoDefiner will define them when added to the DOM.
|
|
19
|
-
|
|
20
|
-
### The Four Layers
|
|
21
|
-
|
|
22
|
-
**Layer 1 — Styles**: From minimal config, PDS generates complete CSS: tokens, scales, semantics, surfaces, states. Zero specificity via `:where()`.
|
|
23
|
-
|
|
24
|
-
**Layer 2 — Enhancements**: Behavior added to semantic HTML via selector-based upgrades (`data-dropdown`, `data-toggle`, etc.).
|
|
25
|
-
|
|
26
|
-
**Layer 3 — Web Components**: `<pds-tabstrip>`, `<pds-drawer>`, etc. only when native HTML has no equivalent.
|
|
27
|
-
|
|
28
|
-
**Layer 4: LLM Support**: Install `@pure-ds/core` and get instant PDS AI Coding Instrucions at your fingertips (GitHub Copilot & Cursor support built in)
|
|
7
|
+
> **Note: In the pure-ds (@pure-ds/core origin) project itself**, `pds-*` web components in `pds/components/` ARE their own source — not compiled from elsewhere.
|
|
29
8
|
|
|
30
9
|
---
|
|
31
10
|
|
|
32
|
-
##
|
|
33
|
-
|
|
34
|
-
**Before generating code, read the relevant SSoT file to get accurate class names, tokens, and APIs.**
|
|
35
|
-
|
|
36
|
-
| Need | SSoT File | What It Contains |
|
|
37
|
-
|------|-----------|------------------|
|
|
38
|
-
| **CSS Tokens** | `public/assets/pds/pds.css-data.json` | All `--color-*`, `--spacing-*`, `--radius-*`, `--shadow-*`, `--font-*` |
|
|
39
|
-
| **Web Components** | `custom-elements.json` | Complete component APIs, attributes, methods, events, slots |
|
|
40
|
-
| **HTML Tags** | `public/assets/pds/vscode-custom-data.json` | Component HTML structure, attribute values |
|
|
41
|
-
| **Primitives & Utilities** | `src/js/pds-core/pds-ontology.js` | `.card`, `.badge`, `.btn-*`, `.flex`, `.gap-*`, `.surface-*` |
|
|
42
|
-
| **Enhancements** | `src/js/pds-core/pds-enhancers.js` | Enhancement metadata (`defaultPDSEnhancerMetadata`) + runtime (`defaultPDSEnhancers`) |
|
|
43
|
-
| **Generator Logic** | `src/js/pds-core/pds-generator.js` | How CSS is generated, token naming conventions |
|
|
44
|
-
| **Config (Runtime)** | `pds.config.js` | What's enabled in this workspace |
|
|
45
|
-
| **Config (SSoT)** | `src/js/pds-core/pds-config.js` | JSDoc types + validation spec + `PDS_CONFIG_RELATIONS` for deterministic token mapping |
|
|
46
|
-
|
|
47
|
-
**For consuming projects** using `@pure-ds/core`, files are in `node_modules/@pure-ds/core/`:
|
|
48
|
-
- `custom-elements.json`
|
|
49
|
-
- `public/assets/pds/pds.css-data.json`
|
|
50
|
-
- `public/assets/pds/vscode-custom-data.json`
|
|
51
|
-
- `src/js/pds-core/pds-ontology.js`
|
|
52
|
-
|
|
53
|
-
**Path resolution helper:** When looking up SSoT files:
|
|
54
|
-
1. First check if `node_modules/@pure-ds/core/` exists (consuming project)
|
|
55
|
-
2. Otherwise use workspace root paths (pure-ds development)
|
|
56
|
-
3. Prefer reading actual files over guessing - the data is authoritative
|
|
57
|
-
|
|
58
|
-
## 🔌 MCP Lookup Protocol (Optional)
|
|
59
|
-
|
|
60
|
-
Use MCP as an optimization, not a prerequisite. For fast, simple lookups, read local SSoT files directly first.
|
|
61
|
-
|
|
62
|
-
### When MCP is already connected
|
|
63
|
-
|
|
64
|
-
1. **Tokens** → call `get_tokens`
|
|
65
|
-
2. **Primitives / utilities / selectors** → call `find_utility_class`
|
|
66
|
-
3. **Natural-language DS search** → call `query_design_system`
|
|
67
|
-
4. **Web component API** → call `get_component_api`
|
|
68
|
-
5. **Enhancer metadata + demoHtml** → call `get_enhancer_metadata`
|
|
69
|
-
6. **Design config deterministic mapping** → call `get_config_relations`
|
|
70
|
-
7. **Final snippet sanity check** → call `validate_pds_snippet`
|
|
71
|
-
|
|
72
|
-
### Non-negotiable rules
|
|
73
|
-
|
|
74
|
-
- Do not invent class names, tokens, attributes, events, or selectors.
|
|
75
|
-
- For form generation, `ui:icon` / icon-input rendering is allowed only on regular text-like inputs (`text`, `email`, `url`, `tel`, `search`, `password`). Do **not** apply icon-input rendering to date/date-range, range, select, checkbox/radio groups, textarea, or omnibox widgets.
|
|
76
|
-
- If a value is not found in MCP results, state it is unavailable and suggest nearest matches.
|
|
77
|
-
- Prefer MCP results over memory when MCP is already available.
|
|
78
|
-
- If MCP is unavailable, slow to start, or errors, continue immediately with direct SSoT file reads using the paths above.
|
|
79
|
-
- Never block or fail an answer solely because MCP is unavailable.
|
|
80
|
-
- If neither MCP nor file reads are available, provide only conservative guidance and clearly mark uncertainty.
|
|
11
|
+
## Philosophy
|
|
81
12
|
|
|
82
|
-
|
|
13
|
+
PDS follows the [Pure Web Manifesto](https://pureweb.dev/manifesto): "The browser is the framework."
|
|
83
14
|
|
|
84
|
-
|
|
15
|
+
- **Standards-first**: Web Platform APIs only (no framework dependencies)
|
|
16
|
+
- **Configuration-driven**: `pds.config.js` generates all CSS (tokens, scales, semantics, surfaces, states). Zero specificity via `:where()`.
|
|
17
|
+
- **Progressive Enhancement**: Semantic HTML first, enhance where needed
|
|
18
|
+
- **Layers**: 1 — Styles/tokens → 2 — Enhancements (`data-*`) → 3 — Web Components (`pds-*`) → 4 — LLM Support
|
|
85
19
|
|
|
86
|
-
|
|
87
|
-
- Do **not** inspect or modify specialized components (e.g., `pds-form`) unless the user explicitly asks for that component or the failing code is clearly inside that component.
|
|
88
|
-
- Start from the smallest relevant layer: **Layer 1 (styles/utilities)** → **Layer 2 (enhancers)** → **Layer 3 (web components)** only if needed.
|
|
89
|
-
- For consuming projects, prefer usage-level fixes in app markup/classes before proposing framework/core changes.
|
|
20
|
+
> PDS web components are lazy-loaded from `[public-root]/assets/pds/components/` — never bundled by the consuming project. The PDS AutoDefiner registers them when added to the DOM.
|
|
90
21
|
|
|
91
22
|
---
|
|
92
23
|
|
|
93
|
-
##
|
|
24
|
+
## Single Sources of Truth
|
|
94
25
|
|
|
95
|
-
|
|
26
|
+
> In consuming projects, prefix paths with `node_modules/@pure-ds/core/`.
|
|
96
27
|
|
|
97
|
-
|
|
28
|
+
| Need | SSoT File |
|
|
29
|
+
|------|-----------|
|
|
30
|
+
| CSS Tokens | `public/assets/pds/pds.css-data.json` |
|
|
31
|
+
| Web Component APIs | `custom-elements.json` |
|
|
32
|
+
| HTML tags & attributes | `public/assets/pds/vscode-custom-data.json` |
|
|
33
|
+
| Primitives & Utilities | `src/js/pds-core/pds-ontology.js` |
|
|
34
|
+
| Enhancement metadata | `src/js/pds-core/pds-enhancers.js` |
|
|
35
|
+
| Config types & token relations | `src/js/pds-core/pds-config.js` |
|
|
98
36
|
|
|
99
|
-
|
|
100
|
-
// ✅ CORRECT: Listen to pw:submit custom event
|
|
101
|
-
form.addEventListener('pw:submit', async (e) => {
|
|
102
|
-
const { json, formData, valid, issues } = e.detail;
|
|
103
|
-
if (valid) {
|
|
104
|
-
// Handle submission with json or formData
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// ❌ WRONG: Native submit event
|
|
109
|
-
form.addEventListener('submit', (e) => { /* Won't work */ });
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### 2. Submit Button Progress - Add `btn-working` automatically
|
|
113
|
-
|
|
114
|
-
**When user requests a form with async submission, ALWAYS:**
|
|
115
|
-
- Add `btn-working` class to submit button during processing
|
|
116
|
-
- Remove it when done (PDS automatically shows spinner icon)
|
|
117
|
-
- Use a realistic 2-3 second delay for demos
|
|
118
|
-
|
|
119
|
-
```javascript
|
|
120
|
-
// ✅ CORRECT: Auto-add progress state
|
|
121
|
-
form.addEventListener('pw:submit', async (e) => {
|
|
122
|
-
const submitBtn = form.querySelector('button[type="submit"]');
|
|
123
|
-
submitBtn?.classList.add('btn-working');
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
await simulateSubmit(e.detail.json); // 2-3 second promise
|
|
127
|
-
await PDS.toast('Submitted successfully!', { type: 'success' });
|
|
128
|
-
} finally {
|
|
129
|
-
submitBtn?.classList.remove('btn-working');
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
async function simulateSubmit(data) {
|
|
134
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
135
|
-
console.log('Submitted:', data);
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### 3. Adding `data-required` to pds-form generated form: simply add the attribute to the pds-form tag
|
|
140
|
-
|
|
141
|
-
```html
|
|
142
|
-
<pds-form data-required id="myForm" hide-actions></pds-form>
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### 4. Placeholders - ALWAYS include examples
|
|
146
|
-
|
|
147
|
-
**Placeholders improve UX significantly. Try to add 'examples' array to schema properties:**
|
|
148
|
-
|
|
149
|
-
**Rule: When generating a form, infer appropriate placeholders based on field name/type if not specified.**
|
|
150
|
-
|
|
151
|
-
### 5. Smart Icons - Infer from field semantics
|
|
152
|
-
|
|
153
|
-
**When generating forms, automatically add appropriate icons based on field names and semantics for regular text-like inputs only.**
|
|
154
|
-
|
|
155
|
-
**Scope rule (critical):** `ui:icon` / icon-input rendering is valid only for text-like inputs (`text`, `email`, `url`, `tel`, `search`, `password`).
|
|
156
|
-
|
|
157
|
-
**Never apply `ui:icon` to:** date/date-range, range sliders, textarea, select, checkbox/radio groups, tag pickers, or omnibox widgets.
|
|
158
|
-
|
|
159
|
-
**Sources of truth for available icons:**
|
|
160
|
-
- Check `pds.config.js` for project-specific icon configuration
|
|
161
|
-
- Consult icon sprite at `public/assets/img/icons/pds-icons.svg` for available icons
|
|
162
|
-
- See `public/assets/pds/vscode-custom-data.json` for icon attribute values
|
|
163
|
-
|
|
164
|
-
**Use semantic reasoning to match field names to appropriate icons:**
|
|
165
|
-
|
|
166
|
-
```javascript
|
|
167
|
-
// ✅ CORRECT: Infer icons based on field semantics
|
|
168
|
-
const uiSchema = {
|
|
169
|
-
"/email": { 'ui:icon': 'envelope', 'ui:autocomplete': 'email' },
|
|
170
|
-
"/phone": { 'ui:icon': 'phone', 'ui:autocomplete': 'tel' },
|
|
171
|
-
"/name": { 'ui:icon': 'user', 'ui:autocomplete': 'name' },
|
|
172
|
-
"/password": { 'ui:icon': 'lock', 'ui:widget': 'password' },
|
|
173
|
-
"/website": { 'ui:icon': 'link' },
|
|
174
|
-
"/address": { 'ui:icon': 'map-pin' }
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
// ❌ WRONG: Non-text widgets should not use ui:icon/icon-input rendering
|
|
178
|
-
const invalidUiSchema = {
|
|
179
|
-
"/travelDates": { 'ui:widget': 'date-range', 'ui:icon': 'calendar' },
|
|
180
|
-
"/budget": { 'ui:widget': 'input-range', 'ui:icon': 'coins' },
|
|
181
|
-
"/notes": { 'ui:widget': 'textarea', 'ui:icon': 'message' }
|
|
182
|
-
};
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
**Rule: When generating forms, analyze field names/types and select semantically appropriate icons from the available icon set, but only for text-like inputs.**
|
|
186
|
-
|
|
187
|
-
### 6. Submit Handler Pattern - ALWAYS provide working async handler
|
|
188
|
-
|
|
189
|
-
**When generating a pds-form, ALWAYS include a complete, iteration-ready submit handler with:**
|
|
190
|
-
- `pw:submit` event (NOT native submit)
|
|
191
|
-
- `btn-working` class for loading state
|
|
192
|
-
- `PDS.toast()` for user feedback
|
|
193
|
-
- Error handling
|
|
194
|
-
- Realistic async simulation
|
|
195
|
-
|
|
196
|
-
```javascript
|
|
197
|
-
// ✅ CORRECT: Complete submit handler pattern
|
|
198
|
-
form.addEventListener('pw:submit', async (e) => {
|
|
199
|
-
const submitBtn = form.querySelector('button[type="submit"]');
|
|
200
|
-
submitBtn?.classList.add('btn-working');
|
|
201
|
-
|
|
202
|
-
try {
|
|
203
|
-
// Simulate async operation (replace with real API call)
|
|
204
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
205
|
-
|
|
206
|
-
// Log the data for debugging
|
|
207
|
-
console.log('Submitted data:', e.detail.json);
|
|
208
|
-
|
|
209
|
-
// Show success toast
|
|
210
|
-
await PDS.toast('Form submitted successfully!', { type: 'success' });
|
|
211
|
-
|
|
212
|
-
// Optionally reset form
|
|
213
|
-
form.reset();
|
|
214
|
-
} catch (error) {
|
|
215
|
-
// Show error toast
|
|
216
|
-
await PDS.toast('Submission failed: ' + error.message, { type: 'error' });
|
|
217
|
-
} finally {
|
|
218
|
-
// Always remove loading state
|
|
219
|
-
submitBtn?.classList.remove('btn-working');
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
// ❌ WRONG: Native submit event
|
|
224
|
-
form.addEventListener('submit', (e) => { /* Won't work */ });
|
|
225
|
-
|
|
226
|
-
// ❌ WRONG: No loading state
|
|
227
|
-
form.addEventListener('pw:submit', async (e) => {
|
|
228
|
-
await fetch('/api'); // No visual feedback!
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
// ❌ WRONG: Browser dialogs
|
|
232
|
-
form.addEventListener('pw:submit', async (e) => {
|
|
233
|
-
alert('Submitted!'); // Use PDS.toast() instead
|
|
234
|
-
});
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
**PDS.toast() is available from the imported `PDS` runtime:**
|
|
238
|
-
|
|
239
|
-
```javascript
|
|
240
|
-
import { PDS } from '#pds';
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
```javascript
|
|
244
|
-
// All toast types
|
|
245
|
-
await PDS.toast('Success message', { type: 'success' });
|
|
246
|
-
await PDS.toast('Error occurred', { type: 'error' });
|
|
247
|
-
await PDS.toast('Warning message', { type: 'warning' });
|
|
248
|
-
await PDS.toast('Info message', { type: 'information' });
|
|
249
|
-
|
|
250
|
-
// Custom duration (auto-calculated by default based on message length)
|
|
251
|
-
await PDS.toast('Quick message', { type: 'info', duration: 3000 });
|
|
252
|
-
|
|
253
|
-
// Persistent (requires manual close)
|
|
254
|
-
await PDS.toast('Important notice', { type: 'warning', persistent: true });
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
### 7. Conditional "Other" Fields - Auto-generate ui:visibleWhen
|
|
258
|
-
|
|
259
|
-
**When a schema has an "Other" enum option, ALWAYS auto-generate a conditional text field:**
|
|
260
|
-
|
|
261
|
-
```javascript
|
|
262
|
-
const schema = {
|
|
263
|
-
type: "object",
|
|
264
|
-
properties: {
|
|
265
|
-
reason: {
|
|
266
|
-
type: "string",
|
|
267
|
-
title: "How did you hear about us?",
|
|
268
|
-
oneOf: [
|
|
269
|
-
{ const: "search", title: "Search Engine" },
|
|
270
|
-
{ const: "social", title: "Social Media" },
|
|
271
|
-
{ const: "friend", title: "Friend Referral" },
|
|
272
|
-
{ const: "other", title: "Other... (please specify)" }, // ← "Other" option
|
|
273
|
-
],
|
|
274
|
-
},
|
|
275
|
-
otherReason: { // ← Conditional field for "Other"
|
|
276
|
-
type: "string",
|
|
277
|
-
title: "Please specify",
|
|
278
|
-
examples: ["Tell us more..."],
|
|
279
|
-
},
|
|
280
|
-
},
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
const uiSchema = {
|
|
284
|
-
// ✅ ALWAYS add these when "other" enum exists
|
|
285
|
-
"/otherReason": {
|
|
286
|
-
"ui:visibleWhen": { "/reason": "other" },
|
|
287
|
-
"ui:requiredWhen": { "/reason": "other" },
|
|
288
|
-
},
|
|
289
|
-
};
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
### 8. Complete Working Example
|
|
293
|
-
|
|
294
|
-
```javascript
|
|
295
|
-
// Schema with examples for placeholders
|
|
296
|
-
const contactSchema = {
|
|
297
|
-
type: "object",
|
|
298
|
-
required: ["name", "email", "message"],
|
|
299
|
-
properties: {
|
|
300
|
-
name: {
|
|
301
|
-
type: "string",
|
|
302
|
-
title: "Name",
|
|
303
|
-
minLength: 2,
|
|
304
|
-
examples: ["John Doe"]
|
|
305
|
-
},
|
|
306
|
-
email: {
|
|
307
|
-
type: "string",
|
|
308
|
-
format: "email",
|
|
309
|
-
title: "Email",
|
|
310
|
-
examples: ["user@example.com"]
|
|
311
|
-
},
|
|
312
|
-
message: {
|
|
313
|
-
type: "string",
|
|
314
|
-
title: "Message",
|
|
315
|
-
minLength: 10,
|
|
316
|
-
examples: ["Your message here..."]
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
// UI schema with smart icons
|
|
322
|
-
const uiSchema = {
|
|
323
|
-
"/name": { 'ui:icon': 'user' },
|
|
324
|
-
"/email": { 'ui:icon': 'envelope', 'ui:autocomplete': 'email' },
|
|
325
|
-
"/message": {
|
|
326
|
-
'ui:widget': 'textarea',
|
|
327
|
-
'ui:rows': 4
|
|
328
|
-
}
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
// Setup with pw:submit and btn-working
|
|
332
|
-
const form = document.getElementById('contactForm');
|
|
333
|
-
form.jsonSchema = contactSchema;
|
|
334
|
-
form.uiSchema = uiSchema;
|
|
335
|
-
|
|
336
|
-
form.addEventListener('pw:submit', async (e) => {
|
|
337
|
-
const submitBtn = form.querySelector('button[type="submit"]');
|
|
338
|
-
submitBtn?.classList.add('btn-working');
|
|
339
|
-
|
|
340
|
-
try {
|
|
341
|
-
// Simulate 2s async operation
|
|
342
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
343
|
-
console.log('Submitted:', e.detail.json);
|
|
344
|
-
await PDS.toast('Message sent!', { type: 'success' });
|
|
345
|
-
form.reset();
|
|
346
|
-
} catch (error) {
|
|
347
|
-
await PDS.toast('Failed to send', { type: 'error' });
|
|
348
|
-
} finally {
|
|
349
|
-
submitBtn?.classList.remove('btn-working');
|
|
350
|
-
}
|
|
351
|
-
});
|
|
352
|
-
```
|
|
37
|
+
**Prefer reading these files over guessing class names or token names.**
|
|
353
38
|
|
|
354
39
|
---
|
|
355
40
|
|
|
356
|
-
##
|
|
357
|
-
|
|
358
|
-
```html
|
|
359
|
-
<!-- ❌ NEVER: Inline styles -->
|
|
360
|
-
<div style="display: flex; gap: 16px; padding: 20px;">
|
|
361
|
-
|
|
362
|
-
<!-- ❌ NEVER: Hardcoded colors -->
|
|
363
|
-
<button style="background: #007acc; color: white;">
|
|
364
|
-
|
|
365
|
-
<!-- ❌ NEVER: Non-semantic HTML -->
|
|
366
|
-
<div class="button" onclick="handleClick()">Click me</div>
|
|
367
|
-
|
|
368
|
-
<!-- ❌ NEVER: Custom CSS when primitives exist -->
|
|
369
|
-
<style>.my-card { border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }</style>
|
|
370
|
-
```
|
|
41
|
+
## 🔌 MCP Lookup Protocol — MANDATORY
|
|
371
42
|
|
|
372
|
-
|
|
373
|
-
// ❌ NEVER: Browser dialogs - Use PDS.ask() and PDS.toast()
|
|
374
|
-
alert("message"); // → await PDS.toast("message", { type: "info" })
|
|
375
|
-
confirm("sure?"); // → await PDS.ask("sure?", { type: "confirm" })
|
|
376
|
-
prompt("name?"); // → await PDS.ask("name?", { type: "prompt" })
|
|
43
|
+
**Before generating any PDS code, call the relevant MCP tool(s) first. Never invent class names, tokens, attributes, or component APIs.**
|
|
377
44
|
|
|
378
|
-
|
|
379
|
-
|
|
45
|
+
| Topic | MCP Tool |
|
|
46
|
+
|-------|----------|
|
|
47
|
+
| CSS tokens (`--color-*`, `--spacing-*`, etc.) | `get_tokens` |
|
|
48
|
+
| Buttons, cards, layout, surfaces, utilities | `find_utility_class` |
|
|
49
|
+
| Any `pds-*` component | `get_component_api` |
|
|
50
|
+
| Any `data-*` enhancement | `get_enhancer_metadata` |
|
|
51
|
+
| Forms, localization, DOM building, toasts, treeview | `query_design_system("your question")` |
|
|
52
|
+
| Config / token naming rules | `get_config_relations` |
|
|
53
|
+
| After generating HTML | `validate_pds_snippet` |
|
|
380
54
|
|
|
381
|
-
|
|
382
|
-
const form = document.querySelector('pds-form');
|
|
383
|
-
form.getFormData(); // May fail - component not loaded yet
|
|
55
|
+
**`query_design_system` auto-injects best-practice guidance with code examples** based on your question. Use it for: pds-form patterns, submit handlers, localization, `parse`/`html` DOM building, `PDS.toast`/`PDS.ask`, treeview lazy loading, Lit import maps, empty states, and more.
|
|
384
56
|
|
|
385
|
-
|
|
386
|
-
form.addEventListener('submit', (e) => { }); // → Use 'pw:submit'
|
|
387
|
-
|
|
388
|
-
// ❌ NEVER: Forget btn-working class for async operations
|
|
389
|
-
button.onclick = async () => {
|
|
390
|
-
await fetch('/api'); // No loading indicator!
|
|
391
|
-
};
|
|
392
|
-
// → Add button.classList.add('btn-working') before, remove after
|
|
393
|
-
|
|
394
|
-
// ❌ NEVER: Hardcode placeholders instead of using schema examples
|
|
395
|
-
const schema = {
|
|
396
|
-
properties: {
|
|
397
|
-
email: { type: "string" } // Missing examples!
|
|
398
|
-
}
|
|
399
|
-
};
|
|
400
|
-
// → Add examples: ["user@example.com"]
|
|
401
|
-
```
|
|
57
|
+
**If MCP is unavailable**: fall back to reading SSoT files directly. Never block on MCP.
|
|
402
58
|
|
|
403
59
|
---
|
|
404
60
|
|
|
405
|
-
##
|
|
406
|
-
|
|
407
|
-
**Components that require Lit:** `pds-form`
|
|
408
|
-
|
|
409
|
-
This component uses `import { ... } from "#pds/lit"` and **requires** an import map:
|
|
410
|
-
|
|
411
|
-
```html
|
|
412
|
-
<!-- REQUIRED in HTML <head> for Lit components -->
|
|
413
|
-
<script type="importmap">
|
|
414
|
-
{
|
|
415
|
-
"imports": {
|
|
416
|
-
"#pds": "/assets/pds/core.js",
|
|
417
|
-
"#pds/lit": "/assets/pds/external/lit.js"
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
</script>
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
**Note:** `#pds/lit` is a convenience bundle that re-exports Lit and adds `lazyProps`. Localization helpers (`msg()`, `str`, `setLocale()`, `loadLocale()`) come from `#pds`, not `#pds/lit`.
|
|
424
|
-
|
|
425
|
-
**When generating code with lazy-loaded components, ALWAYS wait for definition:**
|
|
426
|
-
|
|
427
|
-
```javascript
|
|
428
|
-
// ✅ CORRECT: Wait for component to load
|
|
429
|
-
await customElements.whenDefined('pds-form');
|
|
430
|
-
const form = document.querySelector('pds-form');
|
|
431
|
-
form.getFormData(); // Safe
|
|
432
|
-
|
|
433
|
-
// In a Lit context, use the lazyProps directive (included in #pds/lit bundle)
|
|
434
|
-
<pds-fab
|
|
435
|
-
id="fab-three"
|
|
436
|
-
${lazyProps({ satellites })}
|
|
437
|
-
>
|
|
438
|
-
<pds-icon icon="plus" size="lg" data-original-icon="plus"></pds-icon>
|
|
439
|
-
</pds-fab>
|
|
440
|
-
|
|
441
|
-
// ✅ CORRECT: Alternative pattern
|
|
442
|
-
const FormClass = await customElements.get('pds-form');
|
|
443
|
-
if (FormClass) {
|
|
444
|
-
const form = document.createElement('pds-form');
|
|
445
|
-
// ...
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// ❌ WRONG: Direct access without waiting
|
|
449
|
-
const form = document.querySelector('pds-form');
|
|
450
|
-
form.getFormData(); // May throw error
|
|
451
|
-
```
|
|
61
|
+
## Intent Scoping
|
|
452
62
|
|
|
453
|
-
|
|
63
|
+
Match implementation to the smallest relevant layer first:
|
|
454
64
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
<!-- Buttons: semantic HTML + PDS classes (see pds-ontology.js → primitives) -->
|
|
459
|
-
<button class="btn-primary">Save</button>
|
|
460
|
-
<button class="btn-secondary">Cancel</button>
|
|
461
|
-
<button class="btn-outline">Details</button>
|
|
462
|
-
<button class="btn-primary icon-only" aria-label="Settings">
|
|
463
|
-
<pds-icon icon="gear"></pds-icon>
|
|
464
|
-
</button>
|
|
465
|
-
|
|
466
|
-
<!-- Layout: utility classes (see pds-ontology.js → layoutPatterns, utilities) -->
|
|
467
|
-
<div class="flex gap-md items-center">
|
|
468
|
-
<div class="grid grid-cols-3 gap-lg">
|
|
469
|
-
<div class="stack-md">
|
|
470
|
-
|
|
471
|
-
<!-- Cards & Surfaces: primitives -->
|
|
472
|
-
<article class="card surface-elevated">
|
|
473
|
-
<header class="flex justify-between items-center">
|
|
474
|
-
<h3>Title</h3>
|
|
475
|
-
</header>
|
|
476
|
-
<p class="text-muted">Content</p>
|
|
477
|
-
</article>
|
|
478
|
-
|
|
479
|
-
<!-- Icons: web component (see custom-elements.json) -->
|
|
480
|
-
<pds-icon icon="heart" size="sm"></pds-icon>
|
|
481
|
-
<pds-icon icon="check" size="lg" color="var(--color-success-500)"></pds-icon>
|
|
482
|
-
|
|
483
|
-
<!-- Enhancements: data attributes (see pds-enhancers.js → defaultPDSEnhancerMetadata) -->
|
|
484
|
-
<nav data-dropdown>
|
|
485
|
-
<button>Menu</button>
|
|
486
|
-
<menu><li><a href="#">Item</a></li></menu>
|
|
487
|
-
</nav>
|
|
488
|
-
|
|
489
|
-
<label data-toggle>
|
|
490
|
-
<input type="checkbox">
|
|
491
|
-
<span data-label>Enable feature</span>
|
|
492
|
-
</label>
|
|
493
|
-
|
|
494
|
-
<!-- Treeview: lazy node loading -->
|
|
495
|
-
<pds-treeview id="docsTree"></pds-treeview>
|
|
496
|
-
|
|
497
|
-
<script type="module">
|
|
498
|
-
const tree = document.getElementById('docsTree');
|
|
499
|
-
tree.options = {
|
|
500
|
-
// Initial payload can be shallow (e.g., 2 levels)
|
|
501
|
-
source: [
|
|
502
|
-
{
|
|
503
|
-
id: 'docs',
|
|
504
|
-
text: 'Docs',
|
|
505
|
-
hasChildren: true,
|
|
506
|
-
children: [
|
|
507
|
-
{ id: 'guides', text: 'Guides', hasChildren: true },
|
|
508
|
-
{ id: 'components', text: 'Components', hasChildren: true }
|
|
509
|
-
]
|
|
510
|
-
}
|
|
511
|
-
],
|
|
512
|
-
// Fetch children only when a node is expanded
|
|
513
|
-
getChildren: async ({ nodeId }) => {
|
|
514
|
-
const res = await fetch(`/api/tree?parent=${encodeURIComponent(nodeId)}`);
|
|
515
|
-
return res.ok ? res.json() : [];
|
|
516
|
-
}
|
|
517
|
-
};
|
|
518
|
-
</script>
|
|
519
|
-
|
|
520
|
-
**Treeview lazy-loading rules:**
|
|
521
|
-
- Mark expandable nodes with `hasChildren: true` when children are not yet included in the initial `source`.
|
|
522
|
-
- Provide `options.getChildren({ node, nodeId, host, options, settings })` for node-level fetch on first expand.
|
|
523
|
-
- Prefer shallow initial payloads (root + immediate children), then fetch deeper levels as users expand.
|
|
524
|
-
- Use `node-load` and `node-load-error` events for telemetry, loading UX, and retries.
|
|
525
|
-
|
|
526
|
-
<form data-required>
|
|
527
|
-
<label><span>Email</span><input type="email" required></label>
|
|
528
|
-
</form>
|
|
529
|
-
|
|
530
|
-
<!-- Tabs: web component -->
|
|
531
|
-
<pds-tabstrip>
|
|
532
|
-
<pds-tabpanel label="Tab 1">
|
|
533
|
-
<p>Content for Tab 1</p>
|
|
534
|
-
</pds-tabpanel>
|
|
535
|
-
<pds-tabpanel label="Tab 2">
|
|
536
|
-
<p>Content for Tab 2</p>
|
|
537
|
-
</pds-tabpanel>
|
|
538
|
-
</pds-tabstrip>
|
|
539
|
-
|
|
540
|
-
<!-- Details: wrap post-summary content in a padded primitive -->
|
|
541
|
-
<details>
|
|
542
|
-
<summary>Section title</summary>
|
|
543
|
-
<div class="card">
|
|
544
|
-
<p>Use a padded container after summary to avoid edge-to-edge content.</p>
|
|
545
|
-
</div>
|
|
546
|
-
</details>
|
|
547
|
-
```
|
|
548
|
-
|
|
549
|
-
### Empty State Pattern
|
|
550
|
-
|
|
551
|
-
- Use the `.empty-state` primitive for empty or onboarding states.
|
|
552
|
-
- Structure: heading + supporting text, an icon, then primary/secondary actions.
|
|
553
|
-
- Keep actions as buttons or links with PDS button classes, and include a meaningful icon when available.
|
|
554
|
-
|
|
555
|
-
```javascript
|
|
556
|
-
// Dialogs & Toasts: PDS API
|
|
557
|
-
const confirmed = await PDS.ask("Delete this item?", {
|
|
558
|
-
type: "confirm",
|
|
559
|
-
buttons: { ok: { name: "Delete", variant: "danger" } }
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
const result = await PDS.ask("Publish this change?", {
|
|
563
|
-
title: "Final approval",
|
|
564
|
-
buttons: {
|
|
565
|
-
ok: { name: "Publish", primary: true },
|
|
566
|
-
cancel: { name: "Cancel", cancel: true }
|
|
567
|
-
},
|
|
568
|
-
beforeClose: async ({ actionKind }) => {
|
|
569
|
-
if (actionKind !== "ok") return true;
|
|
570
|
-
const response = await fetch("/api/publish/can-close", { method: "POST" });
|
|
571
|
-
if (!response.ok) return { allow: false };
|
|
572
|
-
const payload = await response.json();
|
|
573
|
-
return { allow: payload?.ok === true };
|
|
574
|
-
}
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
await PDS.toast("Saved successfully!", { type: "success" });
|
|
578
|
-
|
|
579
|
-
// Theme management
|
|
580
|
-
PDS.theme = 'dark'; // 'light' | 'dark' | 'system'
|
|
581
|
-
|
|
582
|
-
// Query the design system via MCP tool: query_design_system
|
|
583
|
-
```
|
|
584
|
-
|
|
585
|
-
---
|
|
65
|
+
1. **Layer 1**: CSS tokens + utility classes + primitive classes (`.card`, `.btn-*`, `.flex`, `.grid-*`)
|
|
66
|
+
2. **Layer 2**: `data-*` enhancements (`data-dropdown`, `data-toggle`, `data-required`)
|
|
67
|
+
3. **Layer 3**: Web Components — only when native HTML has no equivalent
|
|
586
68
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
**For comprehensive pds-form documentation:**
|
|
590
|
-
- Read [pds-form-docs.md](https://github.com/Pure-Web-Foundation/pure-ds/blob/main/pds-form-docs.md) for complete API reference
|
|
591
|
-
- See [packages/pds-storybook/stories/components/PdsForm.stories.js](https://github.com/Pure-Web-Foundation/pure-ds/blob/main/packages/pds-storybook/stories/components/PdsForm.stories.js) for real examples
|
|
592
|
-
- Check [custom-elements.json](https://github.com/Pure-Web-Foundation/pure-ds/blob/main/custom-elements.json) for component API details
|
|
593
|
-
|
|
594
|
-
**For toast notifications:**
|
|
595
|
-
- Use `PDS.toast()` method (see [src/js/common/toast.js](https://github.com/Pure-Web-Foundation/pure-ds/blob/main/src/js/common/toast.js) for implementation)
|
|
596
|
-
- Automatically ensures pds-toaster exists and is loaded before displaying
|
|
597
|
-
- See pds-toaster component API in [custom-elements.json](https://github.com/Pure-Web-Foundation/pure-ds/blob/main/custom-elements.json)
|
|
69
|
+
Do not inspect or modify `pds-form` unless the request explicitly involves that component.
|
|
598
70
|
|
|
599
71
|
---
|
|
600
72
|
|
|
601
|
-
##
|
|
73
|
+
## Critical Anti-Patterns
|
|
602
74
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
75
|
+
- ❌ `style="..."` → use `var(--token-name)` CSS custom properties
|
|
76
|
+
- ❌ Hardcoded colors / spacing → tokens: `--color-*`, `--spacing-*`, `--radius-*`
|
|
77
|
+
- ❌ `alert()` / `confirm()` / `prompt()` → `PDS.ask()` / `PDS.toast()`
|
|
78
|
+
- ❌ Manual dropdown / modal / tab → `<nav data-dropdown>`, `PDS.ask()`, `<pds-tabstrip>`
|
|
79
|
+
- ❌ Native `submit` event on pds-form → listen to `pw:submit`
|
|
80
|
+
- ❌ Accessing lazy component before definition → `await customElements.whenDefined('tag-name')`
|
|
81
|
+
- ❌ Missing `btn-working` class during async button operations
|
|
82
|
+
- ❌ `<div class="button">` or other non-semantic HTML → `<button class="btn-primary">`
|
|
83
|
+
- ❌ `ui:icon` on non-text-like inputs (date-range, textarea, select, omnibox)
|
|
84
|
+
- ❌ Importing localization helpers from `#pds/lit` → import from `#pds`
|
|
611
85
|
|
|
612
86
|
---
|
|
613
87
|
|
|
614
88
|
## Summary Checklist
|
|
615
89
|
|
|
616
|
-
Before generating code:
|
|
617
|
-
|
|
618
|
-
1. ✅ **
|
|
619
|
-
2. ✅ **No inline styles** —
|
|
620
|
-
3. ✅ **No
|
|
621
|
-
4. ✅ **
|
|
622
|
-
5. ✅ **
|
|
623
|
-
6. ✅ **
|
|
624
|
-
7. ✅ **
|
|
625
|
-
8. ✅ **
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
**For pds-form specifically:**
|
|
630
|
-
|
|
631
|
-
11. ✅ **Use `pw:submit` event** — NOT native `submit` event
|
|
632
|
-
12. ✅ **Add `btn-working` class** — For async submit operations, add during processing
|
|
633
|
-
13. ✅ **Use `examples` in JSON schema** — First example becomes placeholder
|
|
634
|
-
14. ✅ **Add smart icons only to text-like inputs** — Use `ui:icon` only for text/email/url/tel/search/password fields
|
|
635
|
-
15. ✅ **Wrap in `form[data-required]`** — For asterisk enhancement on required fields
|
|
636
|
-
16. ✅ **Pad details content** — After `<summary>`, wrap content in a padded container (usually `.card`)
|
|
90
|
+
Before generating any code:
|
|
91
|
+
|
|
92
|
+
1. ✅ **Call MCP first** — `query_design_system`, `get_component_api`, `find_utility_class` as needed
|
|
93
|
+
2. ✅ **No inline styles** — CSS custom property tokens only
|
|
94
|
+
3. ✅ **No browser dialogs** — `PDS.ask()` and `PDS.toast()`
|
|
95
|
+
4. ✅ **Semantic HTML** — `<button>`, `<nav>`, `<article>`, `<label>`, `<details>`
|
|
96
|
+
5. ✅ **Enhancements via `data-*`** — not manual JavaScript
|
|
97
|
+
6. ✅ **Components as last resort** — prefer primitives and enhancements
|
|
98
|
+
7. ✅ **Await lazy-loaded components** — `await customElements.whenDefined()`
|
|
99
|
+
8. ✅ **Validate generated HTML** — `validate_pds_snippet`
|
|
100
|
+
|
|
101
|
+
> **For all topic-specific patterns** (pds-form, localization, DOM building, toasts, treeview, Lit import maps), call `query_design_system("your question")` — it returns authoritative guidance with live code examples straight from source.
|