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