@turquoisehealth/pit-viper 2.179.1-dev.0 → 2.181.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/claude-plugin/.claude-plugin/plugin.json +8 -2
  2. package/claude-plugin/.mcp.json +8 -0
  3. package/claude-plugin/CLAUDE.md +71 -250
  4. package/claude-plugin/README.md +123 -69
  5. package/claude-plugin/skills/pit-viper/SKILL.md +180 -0
  6. package/claude-plugin/skills/pit-viper/references/design-language.md +80 -0
  7. package/claude-plugin/skills/pit-viper/references/design-rules.md +254 -0
  8. package/claude-plugin/skills/pit-viper/references/html-patterns.md +451 -0
  9. package/claude-plugin/skills/pit-viper/references/layout-patterns.md +359 -0
  10. package/claude-plugin/skills/pit-viper/references/patterns-core.md +97 -0
  11. package/claude-plugin/skills/pit-viper/references/theme-guide.md +149 -0
  12. package/claude-plugin/skills/pit-viper/references/vue-guidelines.md +513 -0
  13. package/package.json +3 -3
  14. package/pv-components/dist/stats/vue/base/stats.html +1 -1
  15. package/pv-components/dist/stats/vue/visualizations/stats.html +1 -1
  16. package/pv-components/dist/stats/web/pv-menu-stats.html +1 -1
  17. package/pv-components/dist/stats/web/pv-multi-select-button-stats.html +1 -1
  18. package/pv-components/dist/stats/web/pv-query-builder-input-stats.html +1 -1
  19. package/pv-components/dist/stats/web/pv-select-button-stats.html +1 -1
  20. package/pv-components/dist/vue/base/pv-components-base.mjs +2 -2
  21. package/pv-components/dist/vue/base/pv-components-base.mjs.map +1 -1
  22. package/pv-components/dist/vue/visualizations/components/tables/PvDataTable/filters/filterHelpers.d.ts +1 -1
  23. package/pv-components/dist/vue/visualizations/pv-components-visualizations.mjs +5 -5
  24. package/pv-components/dist/vue/visualizations/pv-components-visualizations.mjs.map +1 -1
  25. package/pv-components/dist/web/components/pv-menu/pv-menu.js +2 -2
  26. package/pv-components/dist/web/components/pv-multi-select-button/pv-multi-select-button.js +2 -2
  27. package/pv-components/dist/web/components/pv-query-builder-input/pv-query-builder-input.js +2 -2
  28. package/pv-components/dist/web/components/pv-select-button/pv-select-button.js +2 -2
  29. package/pv-components/dist/web/pv-components.iife.js +1 -1
  30. package/pv-components/dist/web/pv-components.iife.js.map +1 -1
  31. package/claude-plugin/skills/audit/SKILL.md +0 -125
  32. package/claude-plugin/skills/create/SKILL.md +0 -389
  33. package/claude-plugin/skills/resolve/SKILL.md +0 -142
@@ -0,0 +1,451 @@
1
+ # HTML / Web Component Patterns
2
+
3
+ Use this reference when generating HTML with Pit Viper CSS classes and web components (Prototype Mode).
4
+
5
+ ## Adding Pit Viper to Your Project
6
+
7
+ ### Step 1: Include CSS
8
+
9
+ Choose one CSS theme based on your use case:
10
+
11
+ | Theme | File | Use Case |
12
+ |-------|------|----------|
13
+ | Platform | `pit-viper-v2.css` | Internal tools, dashboards |
14
+ | Consumer | `pit-viper-consumer.css` | Marketing, public-facing sites |
15
+
16
+ ### Step 2: Include Web Components
17
+
18
+ **Option A: Combined bundle (recommended for 2+ components)**
19
+ ```html
20
+ <script src="https://unpkg.com/@turquoisehealth/pit-viper/pv-components/dist/web/pv-components.iife.js"></script>
21
+ ```
22
+ Includes all base components. The Vue runtime (~16kb) is bundled automatically.
23
+
24
+ **Option B: Standalone components (for minimal usage)**
25
+ ```html
26
+ <script src="https://unpkg.com/@turquoisehealth/pit-viper/pv-components/dist/web/pv-button/pv-button.js"></script>
27
+ ```
28
+ Import only specific components. Note: each standalone component bundles the Vue runtime, so use the combined bundle if using more than a few components.
29
+
30
+ ### Complete Setup Example
31
+
32
+ ```html
33
+ <!DOCTYPE html>
34
+ <html lang="en">
35
+ <head>
36
+ <meta charset="UTF-8">
37
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
38
+ <title>Page Title</title>
39
+
40
+ <!-- Pit Viper CSS (choose one) -->
41
+ <link rel="stylesheet" href="https://pitviper.turquoise.health/assets/css/pit-viper-v2.css">
42
+ <!-- OR for consumer/marketing sites: -->
43
+ <!-- <link rel="stylesheet" href="https://pitviper.turquoise.health/assets/css/pit-viper-consumer.css"> -->
44
+
45
+ <!-- Pit Viper Web Components (combined bundle) -->
46
+ <script src="https://unpkg.com/@turquoisehealth/pit-viper/pv-components/dist/web/pv-components.iife.js"></script>
47
+ </head>
48
+ <body>
49
+ <!-- Your content here -->
50
+ </body>
51
+ </html>
52
+ ```
53
+
54
+ ## Naming Conventions
55
+
56
+ Web components use kebab-case (unlike Vue which uses PascalCase):
57
+
58
+ | Vue Component | Web Component |
59
+ |---------------|---------------|
60
+ | `<PvButton>` | `<pv-button>` |
61
+ | `<PvInput>` | `<pv-input>` |
62
+ | `<PvSelectButton>` | `<pv-select-button>` |
63
+ | `<PvCard>` | `<pv-card>` |
64
+
65
+ Properties are also kebab-case:
66
+
67
+ | Vue Prop | HTML Attribute |
68
+ |----------|----------------|
69
+ | `leftIcon` | `left-icon` |
70
+ | `isLoading` | `is-loading` |
71
+ | `optionsVariant` | `options-variant` |
72
+
73
+ ## Web Component Best Practices
74
+
75
+ These rules are critical for web components to work correctly:
76
+
77
+ ### 1. Always Use Complete End Tags
78
+
79
+ Web components **require** full closing tags. Self-closing tags will break slots.
80
+
81
+ ```html
82
+ <!-- CORRECT -->
83
+ <pv-button>Click me</pv-button>
84
+ <pv-card></pv-card>
85
+
86
+ <!-- WRONG - slots will not work -->
87
+ <pv-button />
88
+ <pv-card />
89
+ ```
90
+
91
+ ### 2. Slots Must Use Div Wrappers
92
+
93
+ Unlike Vue's `<template #slot-name>`, web component slots need a wrapper element with the `slot` attribute:
94
+
95
+ ```html
96
+ <!-- CORRECT for web components -->
97
+ <pv-card>
98
+ <div slot="header">Header content</div>
99
+ <div slot="default">Body content</div>
100
+ <div slot="footer">Footer content</div>
101
+ </pv-card>
102
+
103
+ <!-- Vue shorthand (does NOT work in web components) -->
104
+ <PvCard>
105
+ <template #header>Header content</template>
106
+ </PvCard>
107
+ ```
108
+
109
+ ### 3. No Scoped or Conditional Slots
110
+
111
+ Web components do not support:
112
+ - Scoped slots (slots that pass data back to the parent)
113
+ - Conditional slots (`v-if="$slots.someSlot"` patterns)
114
+
115
+ Always render slot content statically.
116
+
117
+ ### 4. Complex Props Require JavaScript
118
+
119
+ Arrays, objects, and functions cannot be passed as HTML attributes:
120
+
121
+ ```html
122
+ <!-- Primitive props work as attributes -->
123
+ <pv-button variant="secondary" disabled>OK</pv-button>
124
+
125
+ <!-- Complex props must be set via JavaScript -->
126
+ <pv-select-button id="my-select"></pv-select-button>
127
+ <script>
128
+ document.getElementById('my-select').options = [
129
+ { label: 'Option 1', value: '1' },
130
+ { label: 'Option 2', value: '2' },
131
+ ];
132
+ </script>
133
+ ```
134
+
135
+ ### 5. Base Components Only
136
+
137
+ Web component support is limited to **base components** (buttons, inputs, cards, etc.).
138
+
139
+ **Not available as web components:**
140
+ - `PvDataTable` (use Vue or CSS tables)
141
+ - `PvChart` (requires Vue)
142
+ - Other visualization components
143
+
144
+ For more details, see the [Vue Web Components documentation](https://vuejs.org/guide/extras/web-components).
145
+
146
+ ## Component Examples
147
+
148
+ ### Button
149
+
150
+ The pv-button web component uses the `label` attribute for button text (not slot content).
151
+
152
+ ```html
153
+ <pv-button label="Click me"></pv-button>
154
+ <pv-button label="Secondary" variant="secondary"></pv-button>
155
+ <pv-button label="Add Item" variant="ghost" left-icon="plus"></pv-button>
156
+ <pv-button label="Saving..." loading></pv-button>
157
+ ```
158
+
159
+ ### Input
160
+ ```html
161
+ <pv-input placeholder="Enter text..." id="my-input"></pv-input>
162
+
163
+ <script>
164
+ const input = document.getElementById('my-input');
165
+ input.addEventListener('input', (e) => {
166
+ console.log('Value:', e.target.value);
167
+ });
168
+ </script>
169
+ ```
170
+
171
+ ### Card
172
+ ```html
173
+ <pv-card>
174
+ <div slot="header">
175
+ <h2 class="pv-heading-2">Card Title</h2>
176
+ </div>
177
+ <div slot="default">
178
+ <p class="pv-text-body-md">Card content goes here.</p>
179
+ </div>
180
+ <div slot="footer">
181
+ <pv-button>Action</pv-button>
182
+ </div>
183
+ </pv-card>
184
+ ```
185
+
186
+ ## Event Handling
187
+
188
+ Web components emit custom events. Use `addEventListener`:
189
+
190
+ ```html
191
+ <pv-button id="my-button">Click me</pv-button>
192
+
193
+ <script>
194
+ document.getElementById('my-button').addEventListener('click', () => {
195
+ console.log('Button clicked');
196
+ });
197
+ </script>
198
+ ```
199
+
200
+ For components with custom events:
201
+
202
+ ```html
203
+ <pv-select-button id="my-select"></pv-select-button>
204
+
205
+ <script>
206
+ const select = document.getElementById('my-select');
207
+
208
+ // Set options via JavaScript (complex props can't be set as attributes)
209
+ select.options = [
210
+ { label: 'Option 1', value: '1' },
211
+ { label: 'Option 2', value: '2' },
212
+ ];
213
+
214
+ // Listen for value changes
215
+ select.addEventListener('update:modelValue', (e) => {
216
+ console.log('Selected:', e.detail);
217
+ });
218
+ </script>
219
+ ```
220
+
221
+ ## Complex Props
222
+
223
+ Non-primitive props (arrays, objects) must be set via JavaScript, not HTML attributes:
224
+
225
+ ```html
226
+ <pv-data-table id="my-table"></pv-data-table>
227
+
228
+ <script>
229
+ const table = document.getElementById('my-table');
230
+
231
+ table.colDefs = [
232
+ { field: 'name', headerName: 'Name' },
233
+ { field: 'email', headerName: 'Email' },
234
+ ];
235
+
236
+ table.rowData = [
237
+ { name: 'Alice', email: 'alice@example.com' },
238
+ { name: 'Bob', email: 'bob@example.com' },
239
+ ];
240
+ </script>
241
+ ```
242
+
243
+ ## CSS Classes
244
+
245
+ Use pv-* utility classes for layout and styling:
246
+
247
+ ```html
248
+ <div class="pv-flex pv-flex-vertical pv-stack-16">
249
+ <h1 class="pv-heading-1">Page Title</h1>
250
+ <p class="pv-text-body-md pv-text-subdued">Description text</p>
251
+ </div>
252
+
253
+ <div class="pv-card pv-inset-square-24 pv-radius">
254
+ <p class="pv-text-body-md">Card content</p>
255
+ </div>
256
+ ```
257
+
258
+ ## Tables
259
+
260
+ Use pv-table CSS classes for static tables. For interactive tables with sorting/filtering, use Vue with PvDataTable instead.
261
+
262
+ ### Basic Table
263
+
264
+ ```html
265
+ <table class="pv-table">
266
+ <thead>
267
+ <tr>
268
+ <th>Name</th>
269
+ <th>Email</th>
270
+ <th>Role</th>
271
+ </tr>
272
+ </thead>
273
+ <tbody>
274
+ <tr>
275
+ <td>Alice Johnson</td>
276
+ <td>alice@example.com</td>
277
+ <td>Admin</td>
278
+ </tr>
279
+ <tr>
280
+ <td>Bob Smith</td>
281
+ <td>bob@example.com</td>
282
+ <td>Editor</td>
283
+ </tr>
284
+ </tbody>
285
+ </table>
286
+ ```
287
+
288
+ ### Table Variants
289
+
290
+ | Class | Use Case |
291
+ |-------|----------|
292
+ | `.pv-table` | Standard table with header styling |
293
+ | `.pv-table-compressed` | Compact rows for dense data |
294
+ | `.pv-table-bordered` | Full borders on all cells |
295
+ | `.pv-table-matrix` | Matrix/grid layout with sticky headers |
296
+
297
+ ### Sortable Headers
298
+
299
+ Use `data-sort` attribute for sort indicators:
300
+
301
+ ```html
302
+ <th data-sort>Name</th>
303
+ <th data-sort="ascending">Email</th>
304
+ <th data-sort="descending">Date</th>
305
+ ```
306
+
307
+ ## Charts
308
+
309
+ **Charts are NOT available in HTML/Prototype mode.** Charting requires Vue components (PvChart, PvDataTableWithChart).
310
+
311
+ If the user requests a chart in HTML mode:
312
+ 1. Explain that charts require Vue components
313
+ 2. Offer to switch to Vue (Engineer Mode) if appropriate
314
+ 3. Or use a placeholder with a note: "Chart visualization requires Vue implementation"
315
+
316
+ ## Limitations
317
+
318
+ Web components have some differences from Vue components:
319
+
320
+ | Limitation | Workaround |
321
+ |------------|------------|
322
+ | No v-model | Use events + property assignment |
323
+ | No scoped slots | Use named slots with `slot="name"` |
324
+ | No conditional slots | Always render slot content |
325
+ | Complex props as attributes | Set via JavaScript |
326
+ | No Vue reactivity | Manually update properties |
327
+ | No charts | Charts require Vue — use PvChart |
328
+
329
+ ## Complete Example
330
+
331
+ ```html
332
+ <!DOCTYPE html>
333
+ <html lang="en">
334
+ <head>
335
+ <meta charset="UTF-8">
336
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
337
+ <title>Login</title>
338
+ <link rel="stylesheet" href="https://pitviper.turquoise.health/assets/css/pit-viper-v2.css">
339
+ <script src="https://unpkg.com/@turquoisehealth/pit-viper/pv-components/dist/web/pv-components.iife.js"></script>
340
+ </head>
341
+ <body class="pv-surface-accent">
342
+ <div class="pv-flex" style="min-height: 100vh; align-items: center; justify-content: center;">
343
+ <pv-card style="width: 400px;">
344
+ <div slot="header">
345
+ <h1 class="pv-heading-2">Sign In</h1>
346
+ </div>
347
+ <div slot="default" class="pv-flow-16">
348
+ <div>
349
+ <label class="pv-text-title-sm pv-stack-4">Email</label>
350
+ <pv-input id="email" type="email" placeholder="you@example.com"></pv-input>
351
+ </div>
352
+ <div>
353
+ <label class="pv-text-title-sm pv-stack-4">Password</label>
354
+ <pv-input id="password" type="password" placeholder="Enter password"></pv-input>
355
+ </div>
356
+ </div>
357
+ <div slot="footer">
358
+ <pv-button id="submit" label="Sign In" style="width: 100%;"></pv-button>
359
+ </div>
360
+ </pv-card>
361
+ </div>
362
+
363
+ <script>
364
+ document.getElementById('submit').addEventListener('click', () => {
365
+ const email = document.getElementById('email').value;
366
+ const password = document.getElementById('password').value;
367
+ console.log('Login attempt:', { email, password });
368
+ });
369
+ </script>
370
+ </body>
371
+ </html>
372
+ ```
373
+
374
+ ---
375
+
376
+ ## Anti-Patterns
377
+
378
+ ### Button Slot Content Instead of Label
379
+
380
+ Web components require the `label` attribute. Slot content won't render.
381
+
382
+ ```html
383
+ <!-- BAD: Slot content -->
384
+ <pv-button>Click me</pv-button>
385
+ <pv-button variant="secondary">Cancel</pv-button>
386
+
387
+ <!-- GOOD: Label attribute -->
388
+ <pv-button label="Click me"></pv-button>
389
+ <pv-button variant="secondary" label="Cancel"></pv-button>
390
+ ```
391
+
392
+ ---
393
+
394
+ ## Good Patterns
395
+
396
+ ### Sidebar Navigation
397
+
398
+ ```html
399
+ <div class="pv-card pv-inset-square-8 pv-flex pv-stack-24 pv-full-width">
400
+ <div class="pv-flex">
401
+ <img src="logo.png" alt="Logo" class="pv-icon-24" />
402
+ <span class="pv-text-title-lg pv-text-default">Organization Name</span>
403
+ </div>
404
+ </div>
405
+
406
+ <ul role="list" class="pv-popover-list" style="padding: 0px; --flow-size: 2px;">
407
+ <li data-active>
408
+ <a href="/overview" style="text-decoration: none; cursor: pointer;">
409
+ <svg aria-hidden="true" class="pv-icon pv-text-subdued">
410
+ <use xlink:href="#file"></use>
411
+ </svg>
412
+ <span class="pv-text-body-md">Overview</span>
413
+ </a>
414
+ </li>
415
+ <li>
416
+ <a href="/settings" style="text-decoration: none; cursor: pointer;">
417
+ <svg aria-hidden="true" class="pv-icon pv-text-subdued">
418
+ <use xlink:href="#sliders-horizontal"></use>
419
+ </svg>
420
+ <span class="pv-text-body-md">Settings</span>
421
+ </a>
422
+ </li>
423
+ </ul>
424
+ ```
425
+
426
+ **What makes this good:**
427
+ - Uses `pv-card` with `pv-inset-square-8` for consistent padding
428
+ - Uses `pv-popover-list` for nav styling (reused for sidebar nav — same visual pattern)
429
+ - Uses `data-active` attribute for active state
430
+ - Uses `pv-icon` and `pv-text-subdued` for icon styling
431
+ - Minimal inline styles for link resets only
432
+
433
+ ### Form Fields with Labels
434
+
435
+ ```html
436
+ <div class="pv-stack-24">
437
+ <div>
438
+ <label class="pv-text-title-sm pv-stack-4">Group Name</label>
439
+ <pv-input placeholder="Enter group name"></pv-input>
440
+ </div>
441
+ <div>
442
+ <label class="pv-text-title-sm pv-stack-4">Description</label>
443
+ <pv-input placeholder="Optional description"></pv-input>
444
+ </div>
445
+ </div>
446
+ ```
447
+
448
+ **What makes this good:**
449
+ - Uses `pv-stack-4` between label and input (tight coupling)
450
+ - Uses `pv-stack-24` between field groups (section spacing)
451
+ - Uses `pv-text-title-sm` for labels (consistent typography)