@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,367 +0,0 @@
1
- # Layout Patterns
2
-
3
- Use this reference when building page-level layouts in any output mode.
4
-
5
- ## Contents
6
-
7
- - Dashboard Pattern (sidebar + main + optional panel) — Vue
8
- - Form Pattern (card with inputs and actions) — Vue
9
- - Settings Pattern (tabs + accordion) — Vue
10
- - Marketing Pattern (hero + features + CTA) — HTML
11
- - List-Detail Pattern (table + drawer) — Vue
12
-
13
- ## Dashboard Pattern
14
-
15
- Three-column layout with navigation sidebar, main content, and optional right panel.
16
-
17
- ### Vue Example
18
- ```vue
19
- <script setup>
20
- import { ref } from 'vue'
21
- import { PvButton, PvSearchInput, PvIcon } from '@turquoisehealth/pit-viper/components'
22
- import { PvDataTable } from '@turquoisehealth/pit-viper/components/visualizations'
23
- import PvSidePanel from '@turquoisehealth/pit-viper/components/layout/PvSidePanel/PvSidePanel.vue'
24
-
25
- const searchQuery = ref('')
26
- const activeNav = ref('dashboard')
27
-
28
- const navItems = [
29
- { id: 'dashboard', label: 'Dashboard', icon: 'home' },
30
- { id: 'users', label: 'Users', icon: 'user' },
31
- { id: 'settings', label: 'Settings', icon: 'gear' },
32
- ]
33
-
34
- const colDefs = [
35
- { field: 'name', headerName: 'Name', flex: 1 },
36
- { field: 'email', headerName: 'Email', flex: 1 },
37
- { field: 'role', headerName: 'Role', width: 120 },
38
- ]
39
-
40
- const rowData = [
41
- { name: 'Alice Johnson', email: 'alice@example.com', role: 'Admin' },
42
- { name: 'Bob Smith', email: 'bob@example.com', role: 'Editor' },
43
- ]
44
- </script>
45
-
46
- <template>
47
- <div style="height: 100vh;">
48
- <PvSidePanel :showLeftSidebar="true" :showRightSidebar="false">
49
- <template #left-sidebar>
50
- <nav class="pv-inset-square-16">
51
- <p class="pv-text-title-md pv-stack-24">My App</p>
52
- <ul style="list-style: none; padding: 0; margin: 0;" class="pv-flow-4">
53
- <li
54
- v-for="item in navItems"
55
- :key="item.id"
56
- class="pv-flex pv-inset-squish-8 pv-radius"
57
- :class="{ 'pv-surface-highlight': activeNav === item.id }"
58
- style="cursor: pointer; --flex-gap: 0.75rem;"
59
- @click="activeNav = item.id"
60
- >
61
- <PvIcon :name="item.icon" :size="16" />
62
- <span class="pv-text-body-md">{{ item.label }}</span>
63
- </li>
64
- </ul>
65
- </nav>
66
- </template>
67
-
68
- <template #main>
69
- <div class="pv-inset-square-24">
70
- <div class="pv-flex pv-space-between pv-stack-24">
71
- <h1 class="pv-heading-1">Users</h1>
72
- <PvButton leftIcon="plus">Add User</PvButton>
73
- </div>
74
-
75
- <div class="pv-stack-16">
76
- <PvSearchInput v-model="searchQuery" placeholder="Search users..." style="max-width: 300px;" />
77
- </div>
78
-
79
- <PvDataTable :rowData="rowData" :colDefs="colDefs" style="height: 400px;" />
80
- </div>
81
- </template>
82
- </PvSidePanel>
83
- </div>
84
- </template>
85
- ```
86
-
87
- ## Form Pattern
88
-
89
- Card-based form with header, body (inputs), and footer (actions).
90
-
91
- ### Vue Example
92
- ```vue
93
- <script setup>
94
- import { ref } from 'vue'
95
- import { PvButton, PvInput, PvSelectButton, PvCard } from '@turquoisehealth/pit-viper/components'
96
-
97
- const formData = ref({
98
- name: '',
99
- email: '',
100
- role: null,
101
- })
102
-
103
- const roleOptions = [
104
- { label: 'Admin', value: 'admin' },
105
- { label: 'Editor', value: 'editor' },
106
- { label: 'Viewer', value: 'viewer' },
107
- ]
108
-
109
- const handleSubmit = () => {
110
- console.log('Submitting:', formData.value)
111
- }
112
- </script>
113
-
114
- <template>
115
- <PvCard style="max-width: 500px;">
116
- <template #header>
117
- <h2 class="pv-heading-2">Create User</h2>
118
- </template>
119
-
120
- <form @submit.prevent="handleSubmit" class="pv-flow-16">
121
- <div>
122
- <label class="pv-text-title-sm pv-stack-4">Name</label>
123
- <PvInput v-model="formData.name" placeholder="Full name" />
124
- </div>
125
-
126
- <div>
127
- <label class="pv-text-title-sm pv-stack-4">Email</label>
128
- <PvInput v-model="formData.email" type="email" placeholder="email@example.com" />
129
- </div>
130
-
131
- <div>
132
- <label class="pv-text-title-sm pv-stack-4">Role</label>
133
- <PvSelectButton v-model="formData.role" :options="roleOptions" placeholder="Select role" />
134
- </div>
135
- </form>
136
-
137
- <template #footer>
138
- <div class="pv-flex" style="--flex-justify: flex-end; --flex-gap: 0.5rem;">
139
- <PvButton variant="ghost">Cancel</PvButton>
140
- <PvButton @click="handleSubmit">Create User</PvButton>
141
- </div>
142
- </template>
143
- </PvCard>
144
- </template>
145
- ```
146
-
147
- ## Settings Pattern
148
-
149
- Tabbed interface with accordion sections for complex settings.
150
-
151
- ### Vue Example
152
- ```vue
153
- <script setup>
154
- import { ref } from 'vue'
155
- import { PvTabs, PvAccordion, PvInput, PvSwitch, PvButton } from '@turquoisehealth/pit-viper/components'
156
-
157
- const activeTab = ref('profile')
158
- const profileExpanded = ref(true)
159
- const notificationsExpanded = ref(false)
160
-
161
- const settings = ref({
162
- displayName: 'Alice Johnson',
163
- email: 'alice@example.com',
164
- emailNotifications: true,
165
- slackNotifications: false,
166
- })
167
- </script>
168
-
169
- <template>
170
- <div class="pv-inset-square-24">
171
- <h1 class="pv-heading-1 pv-stack-24">Settings</h1>
172
-
173
- <PvTabs
174
- v-model="activeTab"
175
- :tabs="[
176
- { label: 'Profile', value: 'profile' },
177
- { label: 'Notifications', value: 'notifications' },
178
- { label: 'Security', value: 'security' },
179
- ]"
180
- class="pv-stack-24"
181
- />
182
-
183
- <div v-if="activeTab === 'profile'" class="pv-flow-16">
184
- <PvAccordion v-model="profileExpanded" header="Personal Information">
185
- <div class="pv-flow-16 pv-inset-square-16">
186
- <div>
187
- <label class="pv-text-title-sm pv-stack-4">Display Name</label>
188
- <PvInput v-model="settings.displayName" />
189
- </div>
190
- <div>
191
- <label class="pv-text-title-sm pv-stack-4">Email</label>
192
- <PvInput v-model="settings.email" type="email" />
193
- </div>
194
- </div>
195
- </PvAccordion>
196
- </div>
197
-
198
- <div v-if="activeTab === 'notifications'" class="pv-flow-16">
199
- <PvAccordion v-model="notificationsExpanded" header="Notification Preferences">
200
- <div class="pv-flow-16 pv-inset-square-16">
201
- <div class="pv-flex pv-space-between">
202
- <span class="pv-text-body-md">Email notifications</span>
203
- <PvSwitch v-model="settings.emailNotifications" />
204
- </div>
205
- <div class="pv-flex pv-space-between">
206
- <span class="pv-text-body-md">Slack notifications</span>
207
- <PvSwitch v-model="settings.slackNotifications" />
208
- </div>
209
- </div>
210
- </PvAccordion>
211
- </div>
212
-
213
- <div class="pv-stack-24">
214
- <PvButton>Save Changes</PvButton>
215
- </div>
216
- </div>
217
- </template>
218
- ```
219
-
220
- ## Marketing / Landing Pattern
221
-
222
- Consumer theme with large typography, full-width sections, and prominent CTAs.
223
-
224
- ### HTML Example (Consumer Theme)
225
- ```html
226
- <!DOCTYPE html>
227
- <html lang="en">
228
- <head>
229
- <meta charset="UTF-8">
230
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
231
- <title>Product Landing</title>
232
- <link rel="stylesheet" href="https://pitviper.turquoise.health/assets/css/pit-viper-consumer.css">
233
- <script src="https://unpkg.com/@turquoisehealth/pit-viper/pv-components/dist/web/pv-components.iife.js"></script>
234
- </head>
235
- <body>
236
- <!-- Hero Section -->
237
- <section class="pv-surface-brand-accent pv-inset-square-64">
238
- <div class="pv-container-md" style="margin: 0 auto; text-align: center;">
239
- <h1 class="pv-heading-1 pv-stack-24">Transform Your Healthcare Data</h1>
240
- <p class="pv-text-body-xl pv-text-subdued pv-stack-32" style="max-width: 600px; margin: 0 auto;">
241
- Unlock insights from your healthcare data with our powerful analytics platform.
242
- </p>
243
- <pv-button size="lg">Get Started</pv-button>
244
- </div>
245
- </section>
246
-
247
- <!-- Features Section -->
248
- <section class="pv-inset-square-64">
249
- <div class="pv-container-lg" style="margin: 0 auto;">
250
- <h2 class="pv-heading-2 pv-stack-32" style="text-align: center;">Features</h2>
251
- <div class="pv-flex pv-flex-responsive" style="--flex-gap: 2rem;">
252
- <pv-card style="flex: 1;">
253
- <div slot="default" class="pv-inset-square-24">
254
- <h3 class="pv-heading-3 pv-stack-8">Real-time Analytics</h3>
255
- <p class="pv-text-body-md pv-text-subdued">
256
- Monitor your data in real-time with customizable dashboards.
257
- </p>
258
- </div>
259
- </pv-card>
260
- <pv-card style="flex: 1;">
261
- <div slot="default" class="pv-inset-square-24">
262
- <h3 class="pv-heading-3 pv-stack-8">Secure & Compliant</h3>
263
- <p class="pv-text-body-md pv-text-subdued">
264
- HIPAA-compliant infrastructure with enterprise-grade security.
265
- </p>
266
- </div>
267
- </pv-card>
268
- <pv-card style="flex: 1;">
269
- <div slot="default" class="pv-inset-square-24">
270
- <h3 class="pv-heading-3 pv-stack-8">Easy Integration</h3>
271
- <p class="pv-text-body-md pv-text-subdued">
272
- Connect to your existing systems with our REST API.
273
- </p>
274
- </div>
275
- </pv-card>
276
- </div>
277
- </div>
278
- </section>
279
-
280
- <!-- CTA Section -->
281
- <section class="pv-surface-brand pv-inset-square-64">
282
- <div class="pv-container-md" style="margin: 0 auto; text-align: center;">
283
- <h2 class="pv-heading-2 pv-text-inverse pv-stack-16">Ready to get started?</h2>
284
- <p class="pv-text-body-lg pv-text-inverse pv-stack-24" style="opacity: 0.8;">
285
- Join thousands of healthcare organizations using our platform.
286
- </p>
287
- <pv-button variant="inverse" size="lg">Contact Sales</pv-button>
288
- </div>
289
- </section>
290
- </body>
291
- </html>
292
- ```
293
-
294
- ## List-Detail Pattern
295
-
296
- Table with a drawer or modal for viewing/editing details.
297
-
298
- ### Vue Example
299
- ```vue
300
- <script setup>
301
- import { ref } from 'vue'
302
- import { PvButton, PvDrawer, PvInput } from '@turquoisehealth/pit-viper/components'
303
- import { PvDataTable } from '@turquoisehealth/pit-viper/components/visualizations'
304
-
305
- const selectedUser = ref(null)
306
- const drawerOpen = ref(false)
307
-
308
- const users = ref([
309
- { id: 1, name: 'Alice Johnson', email: 'alice@example.com', role: 'Admin' },
310
- { id: 2, name: 'Bob Smith', email: 'bob@example.com', role: 'Editor' },
311
- ])
312
-
313
- const colDefs = [
314
- { field: 'name', headerName: 'Name', flex: 1 },
315
- { field: 'email', headerName: 'Email', flex: 1 },
316
- { field: 'role', headerName: 'Role', width: 120 },
317
- {
318
- headerName: 'Actions',
319
- width: 100,
320
- cellRenderer: 'actionCell',
321
- },
322
- ]
323
-
324
- const openDetail = (user) => {
325
- selectedUser.value = { ...user }
326
- drawerOpen.value = true
327
- }
328
- </script>
329
-
330
- <template>
331
- <div class="pv-inset-square-24">
332
- <div class="pv-flex pv-space-between pv-stack-24">
333
- <h1 class="pv-heading-1">Users</h1>
334
- <PvButton leftIcon="plus">Add User</PvButton>
335
- </div>
336
-
337
- <PvDataTable
338
- :rowData="users"
339
- :colDefs="colDefs"
340
- @row-clicked="openDetail($event.data)"
341
- style="height: 400px;"
342
- />
343
-
344
- <PvDrawer v-model="drawerOpen" header="Edit User">
345
- <template v-if="selectedUser">
346
- <div class="pv-flow-16">
347
- <div>
348
- <label class="pv-text-title-sm pv-stack-4">Name</label>
349
- <PvInput v-model="selectedUser.name" />
350
- </div>
351
- <div>
352
- <label class="pv-text-title-sm pv-stack-4">Email</label>
353
- <PvInput v-model="selectedUser.email" type="email" />
354
- </div>
355
- </div>
356
- </template>
357
-
358
- <template #footer>
359
- <div class="pv-flex" style="--flex-justify: flex-end; --flex-gap: 0.5rem;">
360
- <PvButton variant="ghost" @click="drawerOpen = false">Cancel</PvButton>
361
- <PvButton>Save</PvButton>
362
- </div>
363
- </template>
364
- </PvDrawer>
365
- </div>
366
- </template>
367
- ```
@@ -1,97 +0,0 @@
1
- # Core Patterns
2
-
3
- Universal patterns for Pit Viper utility classes. These apply to both Vue and HTML output.
4
-
5
- ## Anti-Pattern: Tailwind Classes
6
-
7
- Pit Viper has its own utility system. Never use Tailwind.
8
-
9
- ```html
10
- <!-- BAD: Tailwind -->
11
- <div class="flex gap-4 p-6 bg-gray-100 rounded-lg">
12
- <h1 class="text-xl font-semibold mb-4">Title</h1>
13
- <p class="text-sm text-gray-600">Description</p>
14
- </div>
15
-
16
- <!-- GOOD: Pit Viper -->
17
- <div class="pv-flex pv-inset-square-24 pv-surface-accent pv-radius">
18
- <h1 class="pv-heading-2 pv-stack-16">Title</h1>
19
- <p class="pv-text-body-sm pv-text-subdued">Description</p>
20
- </div>
21
- ```
22
-
23
- | Tailwind | Pit Viper |
24
- |----------|-----------|
25
- | `flex` | `pv-flex` |
26
- | `gap-4` | `--flex-gap: 1rem` or spacing class |
27
- | `p-6` | `pv-inset-square-24` |
28
- | `bg-gray-100` | `pv-surface-accent` |
29
- | `rounded-lg` | `pv-radius` |
30
- | `text-xl font-semibold` | `pv-heading-2` |
31
- | `text-sm text-gray-600` | `pv-text-body-sm pv-text-subdued` |
32
- | `mb-4` | `pv-stack-16` |
33
-
34
- ## Anti-Pattern: Components Without Spacing
35
-
36
- Elements jammed together without breathing room look broken.
37
-
38
- ```html
39
- <!-- BAD: No spacing -->
40
- <div>
41
- <h1>Settings</h1>
42
- <p>Configure your preferences below.</p>
43
- <pv-input placeholder="Name"></pv-input>
44
- <pv-input placeholder="Email"></pv-input>
45
- <pv-button label="Save"></pv-button>
46
- </div>
47
-
48
- <!-- GOOD: Proper spacing hierarchy -->
49
- <div class="pv-inset-square-24">
50
- <h1 class="pv-heading-1 pv-stack-8">Settings</h1>
51
- <p class="pv-text-body-md pv-text-subdued pv-stack-24">Configure your preferences below.</p>
52
- <div class="pv-flex-vertical pv-stack-24" style="--flex-gap: 1rem;">
53
- <pv-input placeholder="Name"></pv-input>
54
- <pv-input placeholder="Email"></pv-input>
55
- </div>
56
- <pv-button label="Save"></pv-button>
57
- </div>
58
- ```
59
-
60
- ## Anti-Pattern: Hardcoded Colors
61
-
62
- Never use hex values. Use semantic color classes.
63
-
64
- ```html
65
- <!-- BAD: Hardcoded colors -->
66
- <span style="color: #0E8019;">Success!</span>
67
- <span style="color: #DA1E28;">Error!</span>
68
- <div style="background: #F7F8F8;">Content</div>
69
-
70
- <!-- GOOD: Semantic classes -->
71
- <span class="pv-text-success">Success!</span>
72
- <span class="pv-text-critical">Error!</span>
73
- <div class="pv-surface-accent">Content</div>
74
- ```
75
-
76
- ## Allowed Inline Styles
77
-
78
- These inline styles are acceptable when no pv-* class exists:
79
-
80
- | Style | Use Case |
81
- |-------|----------|
82
- | `style="max-width: 500px;"` | Constrain element width |
83
- | `style="width: 298px;"` | Specific width for modals |
84
- | `style="height: 400px;"` | Table/chart height |
85
- | `style="--flex-gap: 1rem;"` | Flex gap override |
86
- | `style="--flex-justify: flex-end;"` | Flex alignment |
87
- | `style="--flow-size: 16px;"` | Flow spacing override |
88
- | `style="text-decoration: none;"` | Link reset |
89
- | `style="cursor: pointer;"` | Clickable non-button elements |
90
- | `style="padding: 0px;"` | Reset default padding |
91
-
92
- **Not allowed:**
93
- - `style="color: #xxx;"` — use `pv-text-*` classes
94
- - `style="background: #xxx;"` — use `pv-surface-*` classes
95
- - `style="font-size: Xpx;"` — use `pv-heading-*` or `pv-text-*` classes
96
- - `style="margin-bottom: Xpx;"` — use `pv-stack-*` classes
97
- - `style="display: flex;"` — use `pv-flex` class
@@ -1,160 +0,0 @@
1
- # Theme Selection Guide
2
-
3
- Use this reference when choosing between Platform and Consumer themes or applying design tokens.
4
-
5
- ## Contents
6
-
7
- - Theme Comparison (Platform vs Consumer)
8
- - Theme URLs (CDN hrefs)
9
- - Auto-Detection Keywords
10
- - Typography Scale
11
- - Spacing System (4px grid)
12
- - Color Palette
13
- - Border Radius
14
- - Layout Classes
15
-
16
- ## Theme Comparison
17
-
18
- | Aspect | Platform | Consumer |
19
- |--------|----------|----------|
20
- | **Use case** | Internal tools, dashboards, admin panels | Marketing sites, public pages, consumer apps |
21
- | **CSS file** | `pit-viper-v2.css` | `pit-viper-consumer.css` |
22
- | **Font** | Inter | GT Standard |
23
- | **Body text** | 12px (compact) | 16px (spacious) |
24
- | **Heading 1** | 20px | 62px |
25
- | **Overall density** | Compact, data-dense | Spacious, content-focused |
26
- | **Color warmth** | Cool blue-gray | Warmer teal |
27
-
28
- ## Theme URLs
29
-
30
- ```
31
- Platform: https://pitviper.turquoise.health/assets/css/pit-viper-v2.css
32
- Consumer: https://pitviper.turquoise.health/assets/css/pit-viper-consumer.css
33
- ```
34
-
35
- Do NOT use the `-scoped` variants.
36
-
37
- ## Auto-Detection Keywords
38
-
39
- ### Platform Theme (Default)
40
- - "dashboard"
41
- - "admin"
42
- - "internal"
43
- - "tool"
44
- - "portal"
45
- - "app"
46
- - "settings"
47
- - "management"
48
-
49
- ### Consumer Theme
50
- - "marketing"
51
- - "landing"
52
- - "public"
53
- - "consumer"
54
- - "website"
55
- - "customer-facing"
56
- - "external"
57
- - "hero"
58
-
59
- If none of these keywords appear, default to **Platform**.
60
-
61
- ## Typography Scale
62
-
63
- ### Platform Theme
64
- | Class | Size | Weight | Line Height |
65
- |-------|------|--------|-------------|
66
- | `.pv-heading-1` | 1.25rem (20px) | 600 | 1.2 |
67
- | `.pv-heading-2` | 1rem (16px) | 600 | 1.5 |
68
- | `.pv-heading-3` | 0.875rem (14px) | 600 | 1.43 |
69
- | `.pv-heading-4` | 0.75rem (12px) | 600 | 1.33 |
70
- | `.pv-text-body-xl` | 0.875rem (14px) | 400 | 1.14 |
71
- | `.pv-text-body-lg` | 0.875rem (14px) | 400 | 1.14 |
72
- | `.pv-text-body-md` | 0.75rem (12px) | 400 | 1.33 |
73
- | `.pv-text-body-sm` | 0.6875rem (11px) | 400 | 1.45 |
74
-
75
- ### Consumer Theme
76
- Heading 1 scales up dramatically (62px). Body text is larger (16px base). Use for marketing pages where readability at a glance matters more than information density.
77
-
78
- ## Spacing System
79
-
80
- 4px base grid. Valid values:
81
-
82
- | Class | Value | Pixels |
83
- |-------|-------|--------|
84
- | `.pv-stack-0` | 0 | 0 |
85
- | `.pv-stack-4` | 0.25rem | 4px |
86
- | `.pv-stack-8` | 0.5rem | 8px |
87
- | `.pv-stack-12` | 0.75rem | 12px |
88
- | `.pv-stack-16` | 1rem | 16px |
89
- | `.pv-stack-20` | 1.25rem | 20px |
90
- | `.pv-stack-24` | 1.5rem | 24px |
91
- | `.pv-stack-32` | 2rem | 32px |
92
- | `.pv-stack-64` | 4rem | 64px |
93
- | `.pv-stack-128` | 8rem | 128px |
94
-
95
- Same values exist for `.pv-inset-square-*` (padding) and `.pv-flow-*` (gap).
96
-
97
- ## Color Palette
98
-
99
- ### Brand Colors
100
- | Token | Hex | Usage |
101
- |-------|-----|-------|
102
- | Primary Brand | `#176F6F` | Interactive elements, links, primary buttons |
103
- | Secondary Brand | `#02363D` | Body text, secondary buttons, dark surfaces |
104
- | Brand Accent Light | `#A8E6E1` | Inverse buttons, AI button, tertiary |
105
- | Brand Accent Bg | `#F3FCFB` | Light teal backgrounds |
106
-
107
- ### Text Colors
108
- | Class | Usage |
109
- |-------|-------|
110
- | `.pv-text-default` | Primary body text |
111
- | `.pv-text-subdued` | Secondary/helper text |
112
- | `.pv-text-brand` | Links, brand-colored text |
113
- | `.pv-text-inverse` | Text on dark surfaces |
114
- | `.pv-text-critical` | Error messages |
115
- | `.pv-text-warning` | Warning text |
116
- | `.pv-text-success` | Success text |
117
-
118
- ### Surface Colors
119
- | Class | Usage |
120
- |-------|-------|
121
- | `.pv-surface` | Default white background |
122
- | `.pv-surface-accent` | Subtle alternate backgrounds |
123
- | `.pv-surface-brand` | Dark brand surfaces |
124
- | `.pv-surface-highlight` | Selection/highlight backgrounds |
125
- | `.pv-surface-brand-accent` | Light teal backgrounds |
126
- | `.pv-surface-critical` | Error backgrounds |
127
- | `.pv-surface-warning` | Warning backgrounds |
128
- | `.pv-surface-success` | Success backgrounds |
129
-
130
- ## Border Radius
131
-
132
- | Class | Value | Use Case |
133
- |-------|-------|----------|
134
- | `.pv-radius-sm` | 2px | Tight, subtle rounding |
135
- | `.pv-radius` | 5px | Default, most components |
136
- | `.pv-radius-lg` | 12px | Cards, modals, prominent elements |
137
-
138
- ## Layout Classes
139
-
140
- | Class | Description |
141
- |-------|-------------|
142
- | `.pv-flex` | `display: flex; gap: 0.5rem; align-items: center` |
143
- | `.pv-flex-vertical` | Column direction |
144
- | `.pv-flex-responsive` | Column on mobile, row at 768px+ |
145
- | `.pv-space-between` | `justify-content: space-between` |
146
- | `.pv-container-sm` | Max-width 768px |
147
- | `.pv-container-md` | Max-width 972px |
148
- | `.pv-container-lg` | Max-width 1448px |
149
-
150
- Use CSS custom property `--flex-gap` to override gap.
151
-
152
- ## Responsive Breakpoint
153
-
154
- Primary breakpoint: **768px**
155
-
156
- - Below 768px: Mobile layout (sidebar collapses, flex stacks)
157
- - 768px and above: Desktop layout
158
-
159
- Use `.pv-flex-responsive` for automatic column-to-row behavior.
160
- Use `.pv-stack-*-responsive` for half-size spacing on mobile.