@nuxt-customer-portal/saas-configuration 0.3.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 (39) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/LICENSE +21 -0
  3. package/README.md +13 -0
  4. package/app/assets/css/main.css +439 -0
  5. package/app/assets/css/themes/brutal.css +453 -0
  6. package/app/assets/css/tokens.css +136 -0
  7. package/app/components/PortalSettingsEditor.vue +452 -0
  8. package/app/components/PublicPortalLogo.vue +26 -0
  9. package/app/components/portal-settings/BrandingStep.vue +198 -0
  10. package/app/components/portal-settings/HomeStep.vue +116 -0
  11. package/app/components/portal-settings/LegalStep.vue +44 -0
  12. package/app/components/portal-settings/ModulesStep.vue +88 -0
  13. package/app/components/portal-settings/ReviewStep.vue +54 -0
  14. package/app/composables/usePortalSettings.ts +25 -0
  15. package/app/middleware/00-portal-onboarding.global.ts +52 -0
  16. package/app/pages/admin/portal-settings.vue +6 -0
  17. package/app/pages/index.vue +170 -0
  18. package/app/pages/onboarding.vue +8 -0
  19. package/app/pages/privacy.vue +46 -0
  20. package/app/pages/terms.vue +46 -0
  21. package/app/plugins/00-portal-settings.ts +26 -0
  22. package/app/plugins/saas-configuration-feature.ts +3 -0
  23. package/i18n/locales/en.json +101 -0
  24. package/i18n/locales/nl.json +105 -0
  25. package/migrations/0000_portal_settings.sql +9 -0
  26. package/nuxt.config.ts +15 -0
  27. package/package.json +50 -0
  28. package/portal.manifest.mjs +7 -0
  29. package/server/api/admin/portal-settings/complete.post.ts +5 -0
  30. package/server/api/admin/portal-settings/index.get.ts +4 -0
  31. package/server/api/admin/portal-settings/index.put.ts +5 -0
  32. package/server/api/portal/bootstrap.get.ts +1 -0
  33. package/server/api/portal/public.get.ts +10 -0
  34. package/server/middleware/01-portal-bootstrap-gate.ts +50 -0
  35. package/server/utils/portal-settings.ts +138 -0
  36. package/shared/feature.ts +30 -0
  37. package/shared/primary-contrast.ts +12 -0
  38. package/shared/settings.ts +146 -0
  39. package/shared/theme.ts +134 -0
@@ -0,0 +1,453 @@
1
+ /* Hallmark · genre: editorial · macrostructure: existing application structures · theme: Brutal
2
+ * states: default · hover · focus · active · disabled · loading · error · success
3
+ * contrast: pass (40–41) · nav: N7 · footer: Ft2 · slop: pass (42–45)
4
+ * honest: pass (46) · chrome: pass (47) · tokens: pass (48) · icons: pass (30)
5
+ * responsive: pass (49) · mobile: pass (34, 49, 50–57)
6
+ */
7
+
8
+ html[data-portal-theme='brutal'] {
9
+ --ui-primary: var(--color-accent);
10
+ --ui-secondary: var(--color-secondary-600);
11
+ --ui-success: var(--color-success-600);
12
+ --ui-warning: var(--color-warning-600);
13
+ --ui-error: var(--color-error-600);
14
+ --ui-info: var(--color-info-600);
15
+
16
+ --ui-text-dimmed: var(--color-muted);
17
+ --ui-text-muted: var(--color-muted);
18
+ --ui-text-toned: var(--color-ink-2);
19
+ --ui-text: var(--color-ink);
20
+ --ui-text-highlighted: var(--color-ink);
21
+ --ui-text-inverted: var(--color-accent-ink);
22
+
23
+ --ui-bg: var(--color-paper);
24
+ --ui-bg-muted: var(--color-paper-2);
25
+ --ui-bg-elevated: var(--color-paper-2);
26
+ --ui-bg-accented: var(--color-paper-3);
27
+ --ui-bg-inverted: var(--color-ink);
28
+
29
+ --ui-border: var(--color-rule-muted);
30
+ --ui-border-muted: var(--color-rule-muted);
31
+ --ui-border-accented: var(--color-rule);
32
+ --ui-border-inverted: var(--color-ink);
33
+
34
+ --ui-radius: var(--radius-flat);
35
+ --ui-header-height: 5rem;
36
+ --ui-shadow-sm: none;
37
+ --ui-shadow: none;
38
+ --ui-shadow-md: var(--shadow-hard-small);
39
+ --ui-shadow-lg: var(--shadow-hard);
40
+ --ui-shadow-xl: var(--shadow-hard);
41
+
42
+ --radius-xs: var(--radius-flat);
43
+ --radius-sm: var(--radius-flat);
44
+ --radius-md: var(--radius-flat);
45
+ --radius-lg: var(--radius-raised);
46
+ --radius-xl: var(--radius-raised);
47
+ --radius-2xl: var(--radius-raised);
48
+ --radius-3xl: var(--radius-raised);
49
+ --shadow-sm: none;
50
+ --shadow-md: var(--shadow-hard-small);
51
+ --shadow-lg: var(--shadow-hard);
52
+ --shadow-xl: var(--shadow-hard);
53
+
54
+ color: var(--color-ink);
55
+ font-family: var(--font-body);
56
+ font-variant-numeric: tabular-nums;
57
+ }
58
+
59
+ html.dark[data-portal-theme='brutal'] {
60
+ --ui-primary: var(--color-accent);
61
+ --ui-secondary: var(--color-secondary-400);
62
+ --ui-success: var(--color-success-400);
63
+ --ui-warning: var(--color-warning-400);
64
+ --ui-error: var(--color-error-400);
65
+ --ui-info: var(--color-info-400);
66
+ }
67
+
68
+ html[data-portal-theme='brutal'] body {
69
+ overflow-x: clip;
70
+ background: var(--color-paper);
71
+ background-image: none;
72
+ color: var(--color-ink);
73
+ }
74
+
75
+ html[data-portal-theme='brutal'] :where(h1, h2, h3, .portal-wordmark) {
76
+ min-width: 0;
77
+ overflow-wrap: anywhere;
78
+ color: var(--color-ink);
79
+ font-family: var(--font-display);
80
+ font-style: normal;
81
+ font-weight: 800;
82
+ letter-spacing: -0.025em;
83
+ }
84
+
85
+ html[data-portal-theme='brutal'] :where(button, a, [role='button'], [role='tab']) {
86
+ text-decoration-thickness: var(--rule-heavy);
87
+ text-underline-offset: 0.2em;
88
+ }
89
+
90
+ html[data-portal-theme='brutal'] :where(button, a, input, textarea, select, [tabindex]):focus-visible {
91
+ outline: 3px solid var(--color-focus);
92
+ outline-offset: 2px;
93
+ transition: none;
94
+ }
95
+
96
+ html[data-portal-theme='brutal'] :where([class*='shadow']) {
97
+ box-shadow: none;
98
+ }
99
+
100
+ html[data-portal-theme='brutal'] :where([class*='card'], [data-variant='subtle'], [data-variant='outline']) {
101
+ border-color: var(--color-rule);
102
+ border-radius: var(--radius-flat);
103
+ box-shadow: none;
104
+ transition:
105
+ border-color var(--dur-short) var(--ease-out),
106
+ transform var(--dur-micro) var(--ease-out);
107
+ }
108
+
109
+ html[data-portal-theme='brutal'] :where([class*='card']):hover {
110
+ border-color: var(--color-accent);
111
+ box-shadow: none;
112
+ }
113
+
114
+ html[data-portal-theme='brutal'] :where([class*='bg-gray-50'], [class*='bg-slate-50'], [class*='dark:bg-gray-900']) {
115
+ background-color: var(--color-paper-2) !important;
116
+ }
117
+
118
+ html[data-portal-theme='brutal'] :where([class*='border-gray-200'], [class*='border-slate-200']) {
119
+ border-color: var(--color-rule-muted) !important;
120
+ }
121
+
122
+ html[data-portal-theme='brutal']
123
+ :where([class*='text-gray-600'], [class*='text-slate-600'], [class*='dark:text-gray-400']) {
124
+ color: var(--color-muted) !important;
125
+ }
126
+
127
+ html[data-portal-theme='brutal']
128
+ :where([class*='text-gray-900'], [class*='text-slate-900'], [class*='dark:text-white']) {
129
+ color: var(--color-ink) !important;
130
+ }
131
+
132
+ html[data-portal-theme='brutal'] :where(button.bg-primary, .bg-blue-600, .bg-primary-600) {
133
+ box-shadow: var(--shadow-hard-small);
134
+ transition:
135
+ background-color var(--dur-short) var(--ease-out),
136
+ color var(--dur-short) var(--ease-out),
137
+ transform var(--dur-micro) var(--ease-out);
138
+ }
139
+
140
+ @media (hover: hover) and (pointer: fine) {
141
+ html[data-portal-theme='brutal'] :where(button.bg-primary, .bg-blue-600, .bg-primary-600):hover {
142
+ box-shadow: var(--shadow-hard-small);
143
+ transform: translate(-1px, -1px);
144
+ }
145
+ }
146
+
147
+ html[data-portal-theme='brutal'] :where(button.bg-primary, .bg-blue-600, .bg-primary-600):active {
148
+ box-shadow: none;
149
+ transform: translate(1px, 1px);
150
+ }
151
+
152
+ html[data-portal-theme='brutal'] .brutal-button {
153
+ min-height: 2.75rem;
154
+ border: var(--rule-hairline) solid currentColor;
155
+ border-radius: var(--radius-flat);
156
+ box-shadow: var(--shadow-hard-small);
157
+ font-weight: 700;
158
+ letter-spacing: 0.035em;
159
+ text-transform: uppercase;
160
+ white-space: nowrap;
161
+ transition:
162
+ background-color var(--dur-short) var(--ease-out),
163
+ color var(--dur-short) var(--ease-out),
164
+ transform var(--dur-micro) var(--ease-out);
165
+ }
166
+
167
+ html[data-portal-theme='brutal'] .brutal-button:active {
168
+ box-shadow: none;
169
+ transform: translate(1px, 1px);
170
+ }
171
+
172
+ html[data-portal-theme='brutal'] .brutal-button:disabled,
173
+ html[data-portal-theme='brutal'] .brutal-button[aria-disabled='true'] {
174
+ box-shadow: none;
175
+ cursor: not-allowed;
176
+ opacity: 0.55;
177
+ transform: none;
178
+ }
179
+
180
+ html[data-portal-theme='brutal'] .brutal-input {
181
+ min-height: 2.75rem;
182
+ border: var(--rule-hairline) solid var(--color-rule);
183
+ border-radius: var(--radius-flat);
184
+ background: var(--color-paper);
185
+ outline: 2px solid transparent;
186
+ outline-offset: 1px;
187
+ transition:
188
+ background-color var(--dur-short) var(--ease-out),
189
+ border-color var(--dur-short) var(--ease-out);
190
+ }
191
+
192
+ @media (hover: hover) and (pointer: fine) {
193
+ html[data-portal-theme='brutal'] .brutal-input:hover {
194
+ background: var(--color-paper-2);
195
+ }
196
+ }
197
+
198
+ html[data-portal-theme='brutal'] .brutal-input:focus-visible,
199
+ html[data-portal-theme='brutal'] .brutal-input[data-state='open'] {
200
+ border-color: var(--color-rule);
201
+ outline-color: var(--color-focus);
202
+ }
203
+
204
+ html[data-portal-theme='brutal'] .brutal-input:disabled,
205
+ html[data-portal-theme='brutal'] .brutal-input[aria-disabled='true'] {
206
+ cursor: not-allowed;
207
+ opacity: 0.55;
208
+ }
209
+
210
+ html[data-portal-theme='brutal'] .brutal-input[aria-invalid='true'] {
211
+ border-color: var(--ui-error);
212
+ }
213
+
214
+ html[data-portal-theme='brutal'] .brutal-card,
215
+ html[data-portal-theme='brutal'] .brutal-auth-form,
216
+ html[data-portal-theme='brutal'] .brutal-dashboard-panel {
217
+ border: var(--rule-heavy) solid var(--color-rule);
218
+ border-radius: var(--radius-flat);
219
+ background: var(--color-paper);
220
+ box-shadow: none;
221
+ }
222
+
223
+ html[data-portal-theme='brutal'] :where(.brutal-card-header, .brutal-card-footer, .brutal-dashboard-navbar) {
224
+ border-color: var(--color-rule);
225
+ border-width: var(--rule-hairline);
226
+ }
227
+
228
+ html[data-portal-theme='brutal'] .brutal-badge {
229
+ border: var(--rule-hairline) solid currentColor;
230
+ border-radius: var(--radius-flat);
231
+ font-weight: 700;
232
+ letter-spacing: 0.035em;
233
+ text-transform: uppercase;
234
+ }
235
+
236
+ html[data-portal-theme='brutal'] .brutal-skeleton {
237
+ border: var(--rule-hairline) solid var(--color-rule-muted);
238
+ border-radius: var(--radius-flat);
239
+ background: var(--color-paper-3);
240
+ }
241
+
242
+ html[data-portal-theme='brutal'] .brutal-alert {
243
+ border: var(--rule-heavy) solid currentColor;
244
+ border-radius: var(--radius-flat);
245
+ }
246
+
247
+ html[data-portal-theme='brutal'] .brutal-overlay {
248
+ background: var(--color-overlay) !important;
249
+ backdrop-filter: none !important;
250
+ }
251
+
252
+ html[data-portal-theme='brutal'] .brutal-overlay-surface,
253
+ html[data-portal-theme='brutal'] .brutal-toast,
254
+ html[data-portal-theme='brutal'] .brutal-tooltip {
255
+ border: var(--rule-heavy) solid var(--color-rule);
256
+ border-radius: var(--radius-flat);
257
+ background: var(--color-paper-2);
258
+ color: var(--color-ink);
259
+ box-shadow: var(--shadow-hard);
260
+ }
261
+
262
+ html[data-portal-theme='brutal'] .brutal-menu-item {
263
+ min-height: 2.75rem;
264
+ border-radius: 0;
265
+ font-weight: 700;
266
+ }
267
+
268
+ html[data-portal-theme='brutal'] .brutal-menu-item[data-highlighted]:not([data-disabled]) {
269
+ background: var(--color-ink);
270
+ color: var(--color-paper);
271
+ }
272
+
273
+ html[data-portal-theme='brutal'] .brutal-navigation-link {
274
+ border-radius: 0;
275
+ font-weight: 700;
276
+ }
277
+
278
+ html[data-portal-theme='brutal'] .brutal-dashboard-sidebar {
279
+ border-inline-end: var(--rule-heavy) solid var(--color-rule);
280
+ border-radius: 0;
281
+ background: var(--color-paper-2);
282
+ }
283
+
284
+ html[data-portal-theme='brutal'] :where(.brutal-table, .brutal-table-head, .brutal-table-row, .brutal-table-heading) {
285
+ border-color: var(--color-rule);
286
+ }
287
+
288
+ html[data-portal-theme='brutal'] .brutal-table-head {
289
+ background: var(--color-ink);
290
+ color: var(--color-paper);
291
+ }
292
+
293
+ html[data-portal-theme='brutal'] .brutal-table-heading {
294
+ color: inherit;
295
+ font-family: var(--font-display);
296
+ font-weight: 800;
297
+ letter-spacing: 0.035em;
298
+ text-transform: uppercase;
299
+ }
300
+
301
+ html[data-portal-theme='brutal'] .brutal-table-row {
302
+ border-block-end: var(--rule-hairline) solid var(--color-rule-muted);
303
+ }
304
+
305
+ html[data-portal-theme='brutal'] .brutal-tabs-list {
306
+ border-block-end: var(--rule-heavy) solid var(--color-rule);
307
+ border-radius: 0;
308
+ background: transparent;
309
+ }
310
+
311
+ html[data-portal-theme='brutal'] .brutal-tabs-trigger {
312
+ min-height: 2.75rem;
313
+ border-radius: 0;
314
+ font-weight: 700;
315
+ white-space: nowrap;
316
+ }
317
+
318
+ html[data-portal-theme='brutal'] .brutal-tabs-indicator {
319
+ height: var(--rule-heavy);
320
+ border-radius: 0;
321
+ background: var(--color-accent);
322
+ box-shadow: none;
323
+ }
324
+
325
+ html[data-portal-theme='brutal'] .portal-header {
326
+ border-block-end: var(--rule-heavy) solid var(--color-rule);
327
+ background: var(--color-paper);
328
+ backdrop-filter: none;
329
+ box-shadow: none;
330
+ }
331
+
332
+ html[data-portal-theme='brutal'] .portal-header nav a {
333
+ border-radius: 0;
334
+ font-family: var(--font-display);
335
+ font-weight: 800;
336
+ letter-spacing: 0.065em;
337
+ text-transform: uppercase;
338
+ white-space: nowrap;
339
+ }
340
+
341
+ html[data-portal-theme='brutal'] .portal-header nav a[aria-current='page'],
342
+ html[data-portal-theme='brutal'] .portal-header nav a.router-link-active {
343
+ text-decoration: underline;
344
+ text-decoration-color: var(--color-accent);
345
+ }
346
+
347
+ html[data-portal-theme='brutal'] .portal-header .-me-1\.5 {
348
+ margin-inline-end: 0;
349
+ }
350
+
351
+ html[data-portal-theme='brutal'] .portal-wordmark {
352
+ font-size: 1.5rem;
353
+ line-height: 1.25;
354
+ letter-spacing: -0.035em;
355
+ text-transform: uppercase;
356
+ }
357
+
358
+ html[data-portal-theme='brutal'] .portal-footer {
359
+ border-block-start: var(--rule-heavy) solid var(--color-rule);
360
+ background: var(--color-paper);
361
+ font-size: var(--text-sm);
362
+ font-weight: 700;
363
+ letter-spacing: 0.04em;
364
+ text-transform: uppercase;
365
+ }
366
+
367
+ html[data-portal-theme='brutal'] :where(.portal-layout, .dashboard-layout, .auth-shell) {
368
+ background: var(--color-paper);
369
+ color: var(--color-ink);
370
+ }
371
+
372
+ html[data-portal-theme='brutal'] .auth-shell [class*='rounded-full'] {
373
+ border-radius: var(--radius-flat);
374
+ }
375
+
376
+ html[data-portal-theme='brutal'] .public-stars {
377
+ display: none;
378
+ }
379
+
380
+ html[data-portal-theme='brutal'] .public-hero-background {
381
+ top: 0;
382
+ height: 0.75rem;
383
+ border-block: var(--rule-heavy) solid var(--color-rule);
384
+ color: var(--color-accent);
385
+ opacity: 1;
386
+ }
387
+
388
+ html[data-portal-theme='brutal'] .public-hero-background svg {
389
+ display: none;
390
+ }
391
+
392
+ html[data-portal-theme='brutal'] .public-hero-background::after {
393
+ display: block;
394
+ width: min(12rem, 35vw);
395
+ height: 100%;
396
+ background: var(--color-accent);
397
+ content: '';
398
+ }
399
+
400
+ html[data-portal-theme='brutal'] .public-home-hero-image {
401
+ border: var(--rule-heavy) solid var(--color-rule);
402
+ border-radius: var(--radius-flat);
403
+ box-shadow: var(--shadow-hard);
404
+ }
405
+
406
+ html[data-portal-theme='brutal'] .main-fade-enter-active::before {
407
+ background: none;
408
+ }
409
+
410
+ html[data-portal-theme='brutal']:not(.dark)
411
+ :where(
412
+ .fixed.inset-0.z-50:not([role='dialog']),
413
+ [data-headlessui-state].fixed.inset-0:not([role='dialog']),
414
+ [role='dialog'] ~ .fixed.inset-0
415
+ ) {
416
+ background: var(--color-overlay) !important;
417
+ backdrop-filter: none !important;
418
+ }
419
+
420
+ @media (pointer: coarse) {
421
+ html[data-portal-theme='brutal'] :where(button, a, input, select, textarea, [role='button'], [role='tab']) {
422
+ min-height: 2.75rem;
423
+ }
424
+
425
+ html[data-portal-theme='brutal'] .portal-header a {
426
+ display: inline-flex;
427
+ align-items: center;
428
+ }
429
+ }
430
+
431
+ @media (max-width: 48rem) {
432
+ html[data-portal-theme='brutal'] .portal-header :where(a, button) {
433
+ min-width: 2.75rem;
434
+ min-height: 2.75rem;
435
+ }
436
+
437
+ html[data-portal-theme='brutal'] .portal-header a {
438
+ display: inline-flex;
439
+ align-items: center;
440
+ }
441
+ }
442
+
443
+ @media (prefers-reduced-motion: reduce) {
444
+ html[data-portal-theme='brutal'] *,
445
+ html[data-portal-theme='brutal'] *::before,
446
+ html[data-portal-theme='brutal'] *::after {
447
+ animation-duration: 150ms !important;
448
+ animation-iteration-count: 1 !important;
449
+ scroll-behavior: auto !important;
450
+ transition-duration: 150ms !important;
451
+ transition-property: opacity, background-color, color, border-color !important;
452
+ }
453
+ }
@@ -0,0 +1,136 @@
1
+ /* Hallmark · genre: editorial · macrostructure: existing application structures · theme: Brutal
2
+ * tone: austere industrial · anchor hue: signal red · enrichment: none · nav: N7-derived · footer: Ft2
3
+ * pre-emit critique: P5 H5 E5 S5 R5 V3 · contrast: pass (40–41) · nav: N7 · footer: Ft2
4
+ * slop: pass (42–45) · honest: pass (46) · chrome: pass (47) · tokens: pass (48) · responsive: pass (49)
5
+ * icons: pass (30) · mobile: pass (34, 49, 50–57)
6
+ */
7
+
8
+ html[data-portal-theme='brutal'] {
9
+ --font-display: 'Bricolage Grotesque', ui-sans-serif, sans-serif;
10
+ --font-body: 'Geist', ui-sans-serif, sans-serif;
11
+ --font-sans: var(--font-body);
12
+
13
+ --color-paper: oklch(96.5% 0.015 85);
14
+ --color-paper-2: oklch(92.5% 0.018 85);
15
+ --color-paper-3: oklch(87% 0.018 80);
16
+ --color-ink: oklch(18% 0.015 25);
17
+ --color-ink-2: oklch(27% 0.015 28);
18
+ --color-muted: oklch(45% 0.018 50);
19
+ --color-rule: oklch(22% 0.015 25);
20
+ --color-rule-muted: oklch(62% 0.015 50);
21
+ --color-accent: oklch(48% 0.22 28);
22
+ --color-accent-ink: var(--color-paper);
23
+ --color-focus: oklch(48% 0.22 28);
24
+ --color-overlay: oklch(18% 0.015 25 / 0.72);
25
+
26
+ --color-primary-50: oklch(97% 0.02 28);
27
+ --color-primary-100: oklch(92% 0.04 28);
28
+ --color-primary-200: oklch(84% 0.08 28);
29
+ --color-primary-300: oklch(75% 0.13 28);
30
+ --color-primary-400: oklch(65% 0.18 28);
31
+ --color-primary-500: oklch(56% 0.22 28);
32
+ --color-primary-600: var(--color-accent);
33
+ --color-primary-700: oklch(41% 0.19 28);
34
+ --color-primary-800: oklch(34% 0.15 28);
35
+ --color-primary-900: oklch(28% 0.11 28);
36
+ --color-primary-950: oklch(20% 0.07 28);
37
+
38
+ --color-secondary-50: oklch(97% 0.018 88);
39
+ --color-secondary-100: oklch(93% 0.035 88);
40
+ --color-secondary-200: oklch(87% 0.07 88);
41
+ --color-secondary-300: oklch(81% 0.11 88);
42
+ --color-secondary-400: oklch(74% 0.14 88);
43
+ --color-secondary-500: oklch(67% 0.14 88);
44
+ --color-secondary-600: oklch(58% 0.12 88);
45
+ --color-secondary-700: oklch(49% 0.1 88);
46
+ --color-secondary-800: oklch(40% 0.08 88);
47
+ --color-secondary-900: oklch(31% 0.06 88);
48
+ --color-secondary-950: oklch(22% 0.04 88);
49
+
50
+ --color-neutral-50: var(--color-paper);
51
+ --color-neutral-100: var(--color-paper-2);
52
+ --color-neutral-200: oklch(84% 0.017 75);
53
+ --color-neutral-300: oklch(72% 0.016 65);
54
+ --color-neutral-400: var(--color-rule-muted);
55
+ --color-neutral-500: oklch(52% 0.014 45);
56
+ --color-neutral-600: oklch(42% 0.014 35);
57
+ --color-neutral-700: oklch(33% 0.014 30);
58
+ --color-neutral-800: var(--color-ink-2);
59
+ --color-neutral-900: var(--color-ink);
60
+ --color-neutral-950: oklch(14.5% 0.014 25);
61
+
62
+ --color-success-400: oklch(70% 0.15 145);
63
+ --color-success-600: oklch(48% 0.14 145);
64
+ --color-warning-400: oklch(78% 0.16 78);
65
+ --color-warning-600: oklch(55% 0.15 62);
66
+ --color-error-400: oklch(67% 0.19 20);
67
+ --color-error-600: oklch(44% 0.19 20);
68
+ --color-info-400: oklch(72% 0.13 235);
69
+ --color-info-600: oklch(47% 0.13 235);
70
+
71
+ --space-3xs: 0.125rem;
72
+ --space-2xs: 0.25rem;
73
+ --space-xs: 0.5rem;
74
+ --space-sm: 0.75rem;
75
+ --space-md: 1rem;
76
+ --space-lg: 1.5rem;
77
+ --space-xl: 2.5rem;
78
+ --space-2xl: 4rem;
79
+ --space-3xl: 6rem;
80
+ --space-4xl: 9rem;
81
+
82
+ --text-xs: 0.64rem;
83
+ --text-sm: 0.8rem;
84
+ --text-base: 1rem;
85
+ --text-md: 1.25rem;
86
+ --text-lg: 1.5625rem;
87
+ --text-xl: 1.9531rem;
88
+ --text-2xl: 2.4414rem;
89
+ --text-3xl: 3.0518rem;
90
+ --text-4xl: 3.8147rem;
91
+ --text-display: clamp(2.75rem, 5vw + 1rem, 5.25rem);
92
+
93
+ --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
94
+ --ease-in: cubic-bezier(0.7, 0, 0.84, 0);
95
+ --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
96
+ --dur-micro: 90ms;
97
+ --dur-short: 165ms;
98
+ --dur-long: 315ms;
99
+
100
+ --rule-hairline: 1px;
101
+ --rule-heavy: 2px;
102
+ --radius-flat: 0.125rem;
103
+ --radius-raised: 0.25rem;
104
+ --shadow-hard: 0.25rem 0.25rem 0 var(--color-rule);
105
+ --shadow-hard-small: 0.125rem 0.125rem 0 var(--color-rule);
106
+
107
+ --z-base: 1;
108
+ --z-raised: 10;
109
+ --z-dropdown: 100;
110
+ --z-sticky: 200;
111
+ --z-modal: 400;
112
+ --z-toast: 500;
113
+ --z-tooltip: 600;
114
+
115
+ --portal-logo-surface: var(--color-ink);
116
+ --portal-logo-ink: var(--color-paper);
117
+ }
118
+
119
+ html.dark[data-portal-theme='brutal'] {
120
+ --color-paper: oklch(14.5% 0.014 25);
121
+ --color-paper-2: oklch(19.5% 0.016 25);
122
+ --color-paper-3: oklch(24% 0.016 28);
123
+ --color-ink: oklch(94% 0.016 85);
124
+ --color-ink-2: oklch(86% 0.016 80);
125
+ --color-muted: oklch(72% 0.014 70);
126
+ --color-rule: oklch(78% 0.015 80);
127
+ --color-rule-muted: oklch(55% 0.015 70);
128
+ --color-accent: oklch(72% 0.16 28);
129
+ --color-accent-ink: var(--color-paper);
130
+ --color-focus: oklch(72% 0.16 28);
131
+ --color-overlay: oklch(8% 0.012 25 / 0.78);
132
+ --shadow-hard: 0.25rem 0.25rem 0 var(--color-rule);
133
+ --shadow-hard-small: 0.125rem 0.125rem 0 var(--color-rule);
134
+ --portal-logo-surface: var(--color-ink);
135
+ --portal-logo-ink: var(--color-paper);
136
+ }