@almanaclabs/blaze-ui 0.4.0 → 0.5.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 (122) hide show
  1. package/dist/components/accordion/accordion.js +2 -2
  2. package/dist/components/alert/alert.d.ts +1 -1
  3. package/dist/components/alert/alert.js +4 -4
  4. package/dist/components/alert-dialog/alert-dialog.d.ts.map +1 -1
  5. package/dist/components/alert-dialog/alert-dialog.js +3 -3
  6. package/dist/components/attachment/attachment.d.ts +2 -2
  7. package/dist/components/attachment/attachment.js +3 -3
  8. package/dist/components/avatar/avatar.d.ts +6 -4
  9. package/dist/components/avatar/avatar.d.ts.map +1 -1
  10. package/dist/components/avatar/avatar.js +10 -9
  11. package/dist/components/badge/badge.d.ts +3 -2
  12. package/dist/components/badge/badge.d.ts.map +1 -1
  13. package/dist/components/badge/badge.js +30 -14
  14. package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -1
  15. package/dist/components/breadcrumb/breadcrumb.js +2 -2
  16. package/dist/components/bubble/bubble.d.ts +1 -1
  17. package/dist/components/bubble/bubble.js +1 -1
  18. package/dist/components/button/button.d.ts +5 -3
  19. package/dist/components/button/button.d.ts.map +1 -1
  20. package/dist/components/button/button.js +46 -27
  21. package/dist/components/button/index.d.ts +1 -1
  22. package/dist/components/button/index.d.ts.map +1 -1
  23. package/dist/components/button-group/button-group.js +4 -4
  24. package/dist/components/calendar/calendar.d.ts.map +1 -1
  25. package/dist/components/calendar/calendar.js +18 -18
  26. package/dist/components/card/card.js +4 -4
  27. package/dist/components/carousel/carousel.d.ts.map +1 -1
  28. package/dist/components/carousel/carousel.js +31 -31
  29. package/dist/components/chart/chart.js +34 -34
  30. package/dist/components/checkbox/checkbox.js +1 -1
  31. package/dist/components/combobox/combobox.d.ts.map +1 -1
  32. package/dist/components/combobox/combobox.js +44 -44
  33. package/dist/components/data-table/data-table.js +1 -1
  34. package/dist/components/date-picker/date-picker.js +10 -10
  35. package/dist/components/dialog/dialog.js +3 -3
  36. package/dist/components/drawer/drawer.js +37 -37
  37. package/dist/components/dropdown-menu/dropdown-menu.js +7 -7
  38. package/dist/components/empty/empty.d.ts.map +1 -1
  39. package/dist/components/empty/empty.js +4 -4
  40. package/dist/components/field/field.d.ts.map +1 -1
  41. package/dist/components/field/field.js +28 -28
  42. package/dist/components/identity-mark/identity-mark.d.ts +5 -0
  43. package/dist/components/identity-mark/identity-mark.d.ts.map +1 -0
  44. package/dist/components/identity-mark/identity-mark.js +20 -0
  45. package/dist/components/identity-mark/index.d.ts +2 -0
  46. package/dist/components/identity-mark/index.d.ts.map +1 -0
  47. package/dist/components/input/input.d.ts +9 -2
  48. package/dist/components/input/input.d.ts.map +1 -1
  49. package/dist/components/input/input.js +23 -10
  50. package/dist/components/input-group/input-group.d.ts +11 -3
  51. package/dist/components/input-group/input-group.d.ts.map +1 -1
  52. package/dist/components/input-group/input-group.js +56 -38
  53. package/dist/components/item/item.d.ts +2 -2
  54. package/dist/components/item/item.js +2 -2
  55. package/dist/components/label/label.js +2 -2
  56. package/dist/components/marker/marker.d.ts +1 -1
  57. package/dist/components/marker/marker.js +3 -3
  58. package/dist/components/message/message.js +1 -1
  59. package/dist/components/metric-overview/index.d.ts +2 -0
  60. package/dist/components/metric-overview/index.d.ts.map +1 -0
  61. package/dist/components/metric-overview/metric-overview.d.ts +12 -0
  62. package/dist/components/metric-overview/metric-overview.d.ts.map +1 -0
  63. package/dist/components/metric-overview/metric-overview.js +42 -0
  64. package/dist/components/native-select/native-select.d.ts +2 -1
  65. package/dist/components/native-select/native-select.d.ts.map +1 -1
  66. package/dist/components/native-select/native-select.js +22 -21
  67. package/dist/components/navigation-action/index.d.ts +2 -0
  68. package/dist/components/navigation-action/index.d.ts.map +1 -0
  69. package/dist/components/navigation-action/navigation-action.d.ts +6 -0
  70. package/dist/components/navigation-action/navigation-action.d.ts.map +1 -0
  71. package/dist/components/navigation-action/navigation-action.js +18 -0
  72. package/dist/components/pagination/pagination.d.ts.map +1 -1
  73. package/dist/components/pagination/pagination.js +6 -6
  74. package/dist/components/popover/popover.js +1 -1
  75. package/dist/components/progress/progress.js +2 -2
  76. package/dist/components/questionnaire/questionnaire-primitive.js +71 -71
  77. package/dist/components/questionnaire/questionnaire.d.ts.map +1 -1
  78. package/dist/components/questionnaire/questionnaire.js +7 -7
  79. package/dist/components/scroll-area/scroll-area.js +1 -1
  80. package/dist/components/select/select.d.ts +2 -1
  81. package/dist/components/select/select.d.ts.map +1 -1
  82. package/dist/components/select/select.js +50 -49
  83. package/dist/components/sheet/sheet.js +2 -2
  84. package/dist/components/sidebar/sidebar.d.ts +13 -2
  85. package/dist/components/sidebar/sidebar.d.ts.map +1 -1
  86. package/dist/components/sidebar/sidebar.js +152 -91
  87. package/dist/components/spinner/spinner.js +1 -1
  88. package/dist/components/table/table.d.ts +4 -1
  89. package/dist/components/table/table.d.ts.map +1 -1
  90. package/dist/components/table/table.js +11 -9
  91. package/dist/components/tabs/tabs.d.ts +1 -1
  92. package/dist/components/tabs/tabs.js +2 -2
  93. package/dist/components/textarea/textarea.js +2 -2
  94. package/dist/components/toast/toast.d.ts.map +1 -1
  95. package/dist/components/toast/toast.js +6 -6
  96. package/dist/components/toggle/toggle.d.ts +1 -1
  97. package/dist/components/toggle/toggle.js +1 -1
  98. package/dist/components/toggle-group/toggle-group.js +10 -10
  99. package/dist/components/tooltip/tooltip.js +1 -1
  100. package/dist/index.d.ts +4 -1
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +58 -55
  103. package/dist/lib/control-size.d.ts +9 -0
  104. package/dist/lib/control-size.d.ts.map +1 -0
  105. package/dist/lib/control-size.js +10 -0
  106. package/dist/recipes/recipe-runtime.d.ts +12 -0
  107. package/dist/recipes/recipe-runtime.d.ts.map +1 -1
  108. package/package.json +1 -1
  109. package/recipes/README.md +2 -0
  110. package/recipes/guided-workflow/README.md +51 -0
  111. package/recipes/guided-workflow/guided-workflow.tsx +495 -0
  112. package/recipes/manifest.json +8 -1
  113. package/recipes/workspace-shell/README.md +79 -25
  114. package/recipes/workspace-shell/assets/google-search-ads.svg +6 -0
  115. package/recipes/workspace-shell/assets/home.svg +5 -0
  116. package/recipes/workspace-shell/assets/radiant-health.png +0 -0
  117. package/recipes/workspace-shell/workspace-shell.css +55 -0
  118. package/recipes/workspace-shell/workspace-shell.tsx +470 -220
  119. package/src/styles/fonts/Sohne-Book.woff2 +0 -0
  120. package/src/styles/fonts/Sohne-Medium.woff2 +0 -0
  121. package/src/styles/fonts/Sohne-Semi.woff2 +0 -0
  122. package/src/styles/globals.css +233 -54
@@ -0,0 +1,495 @@
1
+ import type { ReactNode } from 'react'
2
+ import {
3
+ IconAlertCircle,
4
+ IconCheck,
5
+ IconChevronRight,
6
+ } from '@tabler/icons-react'
7
+
8
+ import {
9
+ Badge,
10
+ Button,
11
+ NavigationAction,
12
+ Progress,
13
+ cn,
14
+ } from '@almanaclabs/blaze-ui'
15
+
16
+ export type GuidedWorkflowStep = {
17
+ key: string
18
+ label: string
19
+ description?: string
20
+ }
21
+
22
+ export type GuidedWorkflowItemStatus = 'complete' | 'attention' | 'pending'
23
+
24
+ export type GuidedWorkflowItem = {
25
+ key: string
26
+ label: string
27
+ description?: string
28
+ status?: GuidedWorkflowItemStatus
29
+ statusLabel?: string
30
+ }
31
+
32
+ export type GuidedWorkflowVariant = 'default' | 'compact'
33
+
34
+ export type GuidedWorkflowRecipeProps = {
35
+ title: string
36
+ steps: readonly GuidedWorkflowStep[]
37
+ activeStepKey: string
38
+ railLabel: string
39
+ items: readonly GuidedWorkflowItem[]
40
+ activeItemKey: string
41
+ onItemSelect: (key: string) => void
42
+ children: ReactNode
43
+ backLabel?: string
44
+ continueLabel?: string
45
+ continueDisabled?: boolean
46
+ footerStatus?: ReactNode
47
+ secondaryActions?: ReactNode
48
+ onBack?: () => void
49
+ onContinue: () => void
50
+ variant?: GuidedWorkflowVariant
51
+ className?: string
52
+ }
53
+
54
+ export type GuidedWorkflowPageHeaderProps = {
55
+ title: string
56
+ steps: readonly GuidedWorkflowStep[]
57
+ activeStepKey: string
58
+ variant?: GuidedWorkflowVariant
59
+ className?: string
60
+ }
61
+
62
+ export type GuidedWorkflowBodyProps = {
63
+ railLabel: string
64
+ items: readonly GuidedWorkflowItem[]
65
+ activeItemKey: string
66
+ onItemSelect: (key: string) => void
67
+ children: ReactNode
68
+ contentLabel?: string
69
+ variant?: GuidedWorkflowVariant
70
+ className?: string
71
+ }
72
+
73
+ export type GuidedWorkflowFooterProps = {
74
+ backLabel?: string
75
+ continueLabel?: string
76
+ continueDisabled?: boolean
77
+ footerStatus?: ReactNode
78
+ secondaryActions?: ReactNode
79
+ onBack?: () => void
80
+ onContinue: () => void
81
+ variant?: GuidedWorkflowVariant
82
+ className?: string
83
+ }
84
+
85
+ export type GuidedWorkflowSectionProps = {
86
+ title: ReactNode
87
+ icon?: ReactNode
88
+ description?: ReactNode
89
+ action?: ReactNode
90
+ children: ReactNode
91
+ className?: string
92
+ markerClassName?: string
93
+ }
94
+
95
+ const statusConfiguration = {
96
+ attention: {
97
+ fallbackLabel: 'Needs attention',
98
+ icon: IconAlertCircle,
99
+ variant: 'destructive',
100
+ },
101
+ complete: {
102
+ fallbackLabel: 'Complete',
103
+ icon: IconCheck,
104
+ variant: 'success',
105
+ },
106
+ pending: {
107
+ fallbackLabel: 'Pending',
108
+ icon: null,
109
+ variant: 'secondary',
110
+ },
111
+ } as const
112
+
113
+ function WorkflowItemStatus({ item }: { item: GuidedWorkflowItem }) {
114
+ if (!item.status) return null
115
+
116
+ const configuration = statusConfiguration[item.status]
117
+ const Icon = configuration.icon
118
+
119
+ return (
120
+ <Badge variant={configuration.variant}>
121
+ {Icon ? <Icon data-icon="inline-start" aria-hidden="true" /> : null}
122
+ {item.statusLabel ?? configuration.fallbackLabel}
123
+ </Badge>
124
+ )
125
+ }
126
+
127
+ export function GuidedWorkflowSections({
128
+ className,
129
+ ...props
130
+ }: React.ComponentProps<'div'>) {
131
+ return <div className={cn('grid gap-8', className)} {...props} />
132
+ }
133
+
134
+ export function GuidedWorkflowSection({
135
+ title,
136
+ icon,
137
+ description,
138
+ action,
139
+ children,
140
+ className,
141
+ markerClassName,
142
+ }: GuidedWorkflowSectionProps) {
143
+ return (
144
+ <section
145
+ className={cn(
146
+ 'grid gap-5 border-b border-border pb-8 last:border-b-0 last:pb-0',
147
+ className,
148
+ )}
149
+ >
150
+ <header className="flex min-w-0 items-start justify-between gap-4">
151
+ <div className="flex min-w-0 items-center gap-2.5">
152
+ {icon ? (
153
+ <span
154
+ className={cn(
155
+ 'grid size-8 shrink-0 place-items-center rounded-full text-primary-foreground [--icon-stroke-width:var(--icon-stroke-16)] [&>svg:not([class*=size-])]:size-4',
156
+ markerClassName,
157
+ )}
158
+ aria-hidden="true"
159
+ >
160
+ {icon}
161
+ </span>
162
+ ) : null}
163
+ <div className="min-w-0">
164
+ <h2 className="type-section-title font-normal text-foreground">
165
+ {title}
166
+ </h2>
167
+ {description ? (
168
+ <p className="type-body mt-1 text-muted-foreground">
169
+ {description}
170
+ </p>
171
+ ) : null}
172
+ </div>
173
+ </div>
174
+ {action}
175
+ </header>
176
+ <div className="min-w-0">{children}</div>
177
+ </section>
178
+ )
179
+ }
180
+
181
+ function getActiveStepIndex(
182
+ steps: readonly GuidedWorkflowStep[],
183
+ activeStepKey: string,
184
+ ) {
185
+ return Math.max(
186
+ 0,
187
+ steps.findIndex((step) => step.key === activeStepKey),
188
+ )
189
+ }
190
+
191
+ export function GuidedWorkflowPageHeader({
192
+ title,
193
+ steps,
194
+ activeStepKey,
195
+ variant = 'default',
196
+ className,
197
+ }: GuidedWorkflowPageHeaderProps) {
198
+ const activeStepIndex = getActiveStepIndex(steps, activeStepKey)
199
+ const activeStep = steps[activeStepIndex]
200
+ const progressValue =
201
+ steps.length === 0 ? 0 : ((activeStepIndex + 1) / steps.length) * 100
202
+
203
+ return (
204
+ <header
205
+ className={cn(
206
+ 'border-b bg-background px-4 md:px-6',
207
+ variant === 'compact' ? 'flex min-h-[76px] items-center py-3' : 'py-4',
208
+ className,
209
+ )}
210
+ >
211
+ <div className="flex w-full flex-col gap-4 xl:flex-row xl:items-center xl:justify-between">
212
+ <div className="min-w-0">
213
+ {variant === 'default' ? (
214
+ <p className="type-meta text-muted-foreground uppercase">
215
+ Step {steps.length === 0 ? 0 : activeStepIndex + 1} of{' '}
216
+ {steps.length}
217
+ </p>
218
+ ) : null}
219
+ <h1
220
+ className={cn(
221
+ 'type-page-title truncate font-normal',
222
+ variant === 'default' && 'mt-1',
223
+ )}
224
+ >
225
+ {title}
226
+ </h1>
227
+ {variant === 'default' && activeStep?.description ? (
228
+ <p className="mt-1 text-sm text-muted-foreground">
229
+ {activeStep.description}
230
+ </p>
231
+ ) : null}
232
+ </div>
233
+
234
+ <nav aria-label="Workflow progress" className="min-w-0 xl:w-3/5">
235
+ <ol className="type-meta flex min-w-max items-center gap-2 overflow-x-auto pb-1 sm:min-w-0 sm:justify-end">
236
+ {steps.map((step, index) => {
237
+ const complete = index < activeStepIndex
238
+ const active = index === activeStepIndex
239
+
240
+ return (
241
+ <li
242
+ key={step.key}
243
+ aria-current={active ? 'step' : undefined}
244
+ className={cn(
245
+ 'flex items-center gap-2 text-muted-foreground',
246
+ active && 'font-medium text-foreground',
247
+ )}
248
+ >
249
+ <span
250
+ className={cn(
251
+ 'grid shrink-0 place-items-center border bg-muted',
252
+ variant === 'compact'
253
+ ? 'size-4 rounded-[4px] text-[0.625rem]'
254
+ : 'size-5 rounded-md text-[0.65rem]',
255
+ active && 'border-foreground/40 bg-background',
256
+ complete &&
257
+ variant === 'default' &&
258
+ 'border-primary bg-primary text-primary-foreground',
259
+ )}
260
+ aria-hidden="true"
261
+ >
262
+ {complete && variant === 'default' ? (
263
+ <IconCheck className="size-3" />
264
+ ) : (
265
+ index + 1
266
+ )}
267
+ </span>
268
+ <span>{step.label}</span>
269
+ {index < steps.length - 1 ? (
270
+ <IconChevronRight
271
+ className="size-3.5 text-border"
272
+ aria-hidden="true"
273
+ />
274
+ ) : null}
275
+ </li>
276
+ )
277
+ })}
278
+ </ol>
279
+ {variant === 'default' ? (
280
+ <Progress
281
+ value={progressValue}
282
+ aria-label={`${title} progress`}
283
+ className="mt-2 gap-0"
284
+ />
285
+ ) : null}
286
+ </nav>
287
+ </div>
288
+ </header>
289
+ )
290
+ }
291
+
292
+ export function GuidedWorkflowBody({
293
+ railLabel,
294
+ items,
295
+ activeItemKey,
296
+ onItemSelect,
297
+ children,
298
+ contentLabel,
299
+ variant = 'default',
300
+ className,
301
+ }: GuidedWorkflowBodyProps) {
302
+ return (
303
+ <div
304
+ className={cn(
305
+ 'grid min-h-0 grid-rows-[auto_minmax(0,1fr)]',
306
+ variant === 'compact'
307
+ ? 'md:grid-cols-[21.25rem_minmax(0,1fr)] md:grid-rows-1'
308
+ : 'lg:grid-cols-[20rem_minmax(0,1fr)] lg:grid-rows-1',
309
+ className,
310
+ )}
311
+ >
312
+ <aside
313
+ className={cn(
314
+ 'min-w-0 border-b',
315
+ variant === 'compact'
316
+ ? 'bg-background md:border-r md:border-b-0'
317
+ : 'bg-surface-subtle lg:border-r lg:border-b-0',
318
+ )}
319
+ >
320
+ <nav
321
+ aria-label={railLabel}
322
+ className={cn(
323
+ 'flex gap-2 overflow-x-auto p-3',
324
+ variant === 'compact'
325
+ ? 'md:grid md:max-h-full md:overflow-y-auto md:p-6'
326
+ : 'lg:grid lg:max-h-full lg:overflow-y-auto lg:p-5',
327
+ )}
328
+ >
329
+ <p
330
+ className={cn(
331
+ 'type-label hidden px-2 font-medium text-foreground',
332
+ variant === 'compact' ? 'md:block' : 'lg:block',
333
+ )}
334
+ >
335
+ {railLabel}
336
+ </p>
337
+ {items.map((item) => {
338
+ const selected = item.key === activeItemKey
339
+
340
+ return (
341
+ <NavigationAction
342
+ key={item.key}
343
+ type="button"
344
+ aria-current={selected ? 'page' : undefined}
345
+ onClick={() => onItemSelect(item.key)}
346
+ className={cn(
347
+ 'h-auto min-w-64 items-start justify-start whitespace-normal border border-transparent px-3 py-3 text-left',
348
+ variant === 'compact'
349
+ ? 'md:min-w-0 md:w-full'
350
+ : 'lg:min-w-0 lg:w-full',
351
+ variant === 'compact' && 'rounded-sm md:min-h-19 md:px-5',
352
+ )}
353
+ >
354
+ <span className="grid min-w-0 flex-1 gap-1">
355
+ <span className="type-content truncate font-normal">
356
+ {item.label}
357
+ </span>
358
+ {item.description ? (
359
+ <span className="type-body truncate font-normal text-muted-foreground">
360
+ {item.description}
361
+ </span>
362
+ ) : null}
363
+ <WorkflowItemStatus item={item} />
364
+ </span>
365
+ </NavigationAction>
366
+ )
367
+ })}
368
+ </nav>
369
+ </aside>
370
+
371
+ <section
372
+ aria-label={contentLabel ?? railLabel}
373
+ className={cn(
374
+ 'min-w-0 overflow-y-auto',
375
+ variant === 'compact' ? 'bg-background' : 'bg-surface-subtle',
376
+ )}
377
+ >
378
+ <div
379
+ className={cn(
380
+ 'mx-auto w-full p-4 md:p-8',
381
+ variant === 'compact' ? 'max-w-none md:p-6' : 'max-w-5xl lg:p-10',
382
+ )}
383
+ >
384
+ {children}
385
+ </div>
386
+ </section>
387
+ </div>
388
+ )
389
+ }
390
+
391
+ export function GuidedWorkflowFooter({
392
+ backLabel = 'Back',
393
+ continueLabel = 'Continue',
394
+ continueDisabled = false,
395
+ footerStatus,
396
+ secondaryActions,
397
+ onBack,
398
+ onContinue,
399
+ variant = 'default',
400
+ className,
401
+ }: GuidedWorkflowFooterProps) {
402
+ return (
403
+ <footer
404
+ className={cn(
405
+ 'flex flex-wrap items-center justify-between gap-3 border-t bg-background px-4 py-3 md:px-6',
406
+ variant === 'compact' ? 'min-h-[88px]' : 'min-h-16',
407
+ className,
408
+ )}
409
+ >
410
+ <div className="flex min-w-0 items-center gap-3">
411
+ {onBack ? (
412
+ <Button type="button" variant="secondary" size="md" onClick={onBack}>
413
+ {backLabel}
414
+ </Button>
415
+ ) : null}
416
+ {footerStatus ? (
417
+ <div className="truncate text-sm text-muted-foreground">
418
+ {footerStatus}
419
+ </div>
420
+ ) : null}
421
+ </div>
422
+ <div className="flex items-center gap-2">
423
+ {secondaryActions}
424
+ <Button
425
+ type="button"
426
+ size="md"
427
+ disabled={continueDisabled}
428
+ onClick={onContinue}
429
+ >
430
+ {continueLabel}
431
+ <IconChevronRight data-icon="inline-end" aria-hidden="true" />
432
+ </Button>
433
+ </div>
434
+ </footer>
435
+ )
436
+ }
437
+
438
+ export function GuidedWorkflowRecipe({
439
+ title,
440
+ steps,
441
+ activeStepKey,
442
+ railLabel,
443
+ items,
444
+ activeItemKey,
445
+ onItemSelect,
446
+ children,
447
+ backLabel = 'Back',
448
+ continueLabel = 'Continue',
449
+ continueDisabled = false,
450
+ footerStatus,
451
+ secondaryActions,
452
+ onBack,
453
+ onContinue,
454
+ variant = 'default',
455
+ className,
456
+ }: GuidedWorkflowRecipeProps) {
457
+ const activeStepIndex = getActiveStepIndex(steps, activeStepKey)
458
+ const activeStep = steps[activeStepIndex]
459
+
460
+ return (
461
+ <section
462
+ className={cn(
463
+ 'grid h-dvh min-h-[720px] grid-rows-[auto_minmax(0,1fr)_auto] overflow-hidden bg-background text-foreground',
464
+ className,
465
+ )}
466
+ >
467
+ <GuidedWorkflowPageHeader
468
+ title={title}
469
+ steps={steps}
470
+ activeStepKey={activeStepKey}
471
+ variant={variant}
472
+ />
473
+ <GuidedWorkflowBody
474
+ railLabel={railLabel}
475
+ items={items}
476
+ activeItemKey={activeItemKey}
477
+ onItemSelect={onItemSelect}
478
+ contentLabel={activeStep?.label ?? title}
479
+ variant={variant}
480
+ >
481
+ {children}
482
+ </GuidedWorkflowBody>
483
+ <GuidedWorkflowFooter
484
+ backLabel={backLabel}
485
+ continueLabel={continueLabel}
486
+ continueDisabled={continueDisabled}
487
+ footerStatus={footerStatus}
488
+ secondaryActions={secondaryActions}
489
+ onBack={onBack}
490
+ onContinue={onContinue}
491
+ variant={variant}
492
+ />
493
+ </section>
494
+ )
495
+ }
@@ -3,10 +3,17 @@
3
3
  "recipes": [
4
4
  {
5
5
  "name": "workspace-shell",
6
- "description": "Authenticated Blaze workspace chrome with access-mode theming, navigation, and an empty page-content slot.",
6
+ "description": "Authenticated Blaze workspace chrome with shared standard and focused modes, replaceable identity, page chrome, and pinned actions.",
7
7
  "dependencies": ["@almanaclabs/blaze-ui", "@tabler/icons-react"],
8
8
  "entry": "workspace-shell/workspace-shell.tsx",
9
9
  "instructions": "workspace-shell/README.md"
10
+ },
11
+ {
12
+ "name": "guided-workflow",
13
+ "description": "Multi-step creation and review flow with ordered progress, contextual work-item navigation, a focused canvas, and persistent actions.",
14
+ "dependencies": ["@almanaclabs/blaze-ui", "@tabler/icons-react"],
15
+ "entry": "guided-workflow/guided-workflow.tsx",
16
+ "instructions": "guided-workflow/README.md"
10
17
  }
11
18
  ]
12
19
  }
@@ -1,41 +1,95 @@
1
1
  # Workspace shell recipe
2
2
 
3
- Use this recipe for authenticated pages that sit inside a selected customer
4
- workspace. It establishes the persistent application chrome; the page owns only the
5
- content rendered inside the shell.
3
+ Use this recipe for authenticated Blaze pages inside a selected customer
4
+ workspace. It establishes one persistent application frame; the page supplies its
5
+ own navigation, workflow header, canvas, and actions through slots.
6
+
7
+ ## Modes
8
+
9
+ - `standard`: dashboard, settings, reporting, and channel-management pages. The
10
+ content area owns its scrolling.
11
+ - `focused`: creation and review flows. The content slot owns its internal rail and
12
+ canvas scrolling, while the optional shell footer stays pinned.
13
+
14
+ Both modes preserve the same account sidebar, outer workspace gutter, rounded
15
+ workspace frame, and 56px channel header. A focused workflow is content inside the
16
+ workspace shell, not a second application shell.
6
17
 
7
18
  ## Preserve
8
19
 
9
- - One `SidebarProvider` wrapping the sidebar and inset.
10
- - Primary and settings navigation landmarks with an active item marked by
20
+ - One `SidebarProvider` wrapping the 220px sidebar and workspace inset.
21
+ - A 10px desktop gutter around the bordered, rounded workspace frame.
22
+ - A 76px workspace-identity header aligning the identity to the channel title,
23
+ followed by the same 8px section inset in every shell mode.
24
+ - `Manage` and `Channels` navigation groups with the active destination marked by
11
25
  `aria-current="page"`.
12
- - A single page heading in the sticky header.
13
- - Optional page-level navigation centered in the header on desktop and available in
14
- the normal document order.
15
- - The sidebar's icon-collapse and mobile-sheet behavior.
16
- - Semantic Blaze UI colors and spacing.
17
- - Access-mode theming: AM mode always uses the light sidebar; client mode always uses
18
- the dark sidebar.
26
+ - 12px medium section labels and 14px regular destination text using the 90%
27
+ foreground in default, hover, and selected states.
28
+ - 15px rail icons using the same 90% foreground as their destination labels in
29
+ default, hover, and selected states.
30
+ - The bespoke Home glyph from the approved workspace-shell Figma frame, inlined
31
+ with `currentColor` and the system 7/6px rendered stroke at 15px; do not
32
+ substitute the Tabler Home icon.
33
+ - Shared `NavigationAction` / `navigationActionVariants` styling for rail
34
+ destinations and standard shell tabs: transparent default, muted hover, and
35
+ 8% selected surface.
36
+ - Workspace and account menus composed as specialized tertiary `Button` triggers,
37
+ rather than one-off native-button states. Their surfaces share the navigation
38
+ row's 10px inset and pad from the identity icon so hover does not begin at the
39
+ glyph edge; both retain the default 32px control height for balanced vertical
40
+ space around the identity mark.
41
+ - Rail collapse, expansion, and settings actions use tertiary `icon-sm` buttons
42
+ without overriding their system size or hit area.
43
+ - The expanded rail uses `SidebarRail` resize mode with a 220px default, 200–320px
44
+ caps, a transparent edge at rest, a centered 2px divider on hover/focus/drag,
45
+ column-resize cursor, pointer dragging, and Arrow/Home/End keyboard control.
46
+ Width and caps remain configurable.
47
+ - A 55px collapsed rail that preserves row and group geometry, hides visible
48
+ labels without removing their layout slots, stacks Settings above the profile,
49
+ and moves the expand control into the channel header. Collapse and expansion use
50
+ a 150ms Material standard curve with staged label fades for a settled endpoint.
51
+ Those pattern-specific rules live in `workspace-shell.css`, not the global
52
+ Blaze UI stylesheet.
53
+ - An 8px first-section inset that combines with the identity header's bottom
54
+ padding to optically match the 24px separation between navigation groups; shell
55
+ mode must not alter left-rail geometry.
56
+ - A single channel header with replaceable channel identity and action slots.
57
+ - The shell's responsive mobile sheet and desktop icon-collapse behavior.
58
+ - An optional page-header slot above the content and an optional 88px pinned action
59
+ footer below it.
60
+ - Söhne typography, optical icon sizing, semantic colors, and shared Blaze UI
61
+ controls.
19
62
 
20
63
  ## Adapt
21
64
 
22
- - Replace the fictional workspace and user with serialized application data.
23
- - Declare `@tabler/icons-react` as an application dependency because the copied
24
- recipe imports its navigation icons directly.
65
+ - Replace the reference workspace, channel, and user identity through
66
+ `workspaceMark`, `channelMark`, and `userAvatar`.
67
+ - Keep fixed brand marks resolution-independent where a vector source exists. When
68
+ a workspace avatar is raster-only, retain the original high-density source and
69
+ size it explicitly in the slot rather than generating a CSS-sized derivative.
70
+ - Keep workspace and channel marks in a 24px identity container with 20px centered
71
+ artwork by composing `IdentityMark` and `IdentityMarkImage`. Compose account
72
+ avatars with `Avatar size="xs" shape="rounded"` for the 20px box and 6px corner
73
+ radius. Account labels use the same 14px regular weight as navigation
74
+ destinations.
25
75
  - Replace `href` values through the application's routing layer. In Inertia
26
76
  applications, render `Link` through the Blaze UI component's `render` prop rather
27
77
  than adding a parallel client-side router.
28
- - Derive the active item and access mode from server-authorized page data.
29
- - Fill the empty content area with the prototype's page-specific design. The shell
30
- recipe deliberately provides no example page content.
78
+ - Pass application-specific navigation groups and derive the active key from the
79
+ current route.
80
+ - Put standard tabs and page titles in `pageHeader`.
81
+ - Put guided-workflow progress in `pageHeader`, its rail and canvas in `children`,
82
+ and its actions in `footer` with `mode="focused"`.
83
+ - Keep routing, authorization, persistence, and workflow state in the application.
31
84
 
32
85
  ## Avoid
33
86
 
34
- - Importing this recipe as a runtime package component.
35
- - Moving routing, authorization, access switching, or data fetching into the recipe.
36
- - Rebuilding the sidebar, header, or responsive behavior with arbitrary CSS.
37
- - Using production customer names, emails, logos, or metrics in Storybook.
87
+ - Importing this recipe as a runtime package component; copy and adapt its source.
88
+ - Creating separate outer shells for dashboard and creation-flow routes.
89
+ - Placing page titles, tabs, or domain state inside the persistent sidebar.
90
+ - Hiding workflow actions in the scrolling canvas when they must remain available.
91
+ - Using production customer data in Storybook.
38
92
 
39
- Start from `workspace-shell.tsx`. Use the hosted Storybook
40
- `Recipes/Workspace shell` entry to review expanded, collapsed, AM-mode, client-mode,
41
- and mobile states.
93
+ Start from `workspace-shell.tsx` and its colocated `workspace-shell.css`. Review
94
+ `Recipes/Workspace shell/Standard` and `Recipes/Workspace shell/Focused Campaign`
95
+ in Storybook before adapting them.
@@ -0,0 +1,6 @@
1
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M16 8.18185C16 7.61458 15.9481 7.06913 15.8516 6.54549H8.16326V9.64003H12.5566C12.3673 10.64 11.7922 11.4873 10.9276 12.0546V14.0618H13.5659C15.1095 12.6691 16 10.6182 16 8.18185Z" fill="#4285F4"/>
3
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M8.16327 16C10.3674 16 12.2152 15.2837 13.5659 14.0618L10.9276 12.0546C10.1967 12.5346 9.26161 12.8182 8.16327 12.8182C6.03711 12.8182 4.23748 11.4109 3.59555 9.51998H0.868279V11.5927C2.21151 14.2072 4.97218 16 8.16327 16Z" fill="#34A853"/>
4
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M3.59555 9.51998C3.43229 9.03998 3.33952 8.5273 3.33952 8.00003C3.33952 7.47276 3.43228 6.96003 3.59555 6.48003V4.40731H0.868275C0.315399 5.48731 0 6.70912 0 8.00003C0 9.29094 0.315403 10.5127 0.868279 11.5927L3.59555 9.51998Z" fill="#FBBC05"/>
5
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M8.16327 3.18181C9.36179 3.18181 10.4379 3.58545 11.2839 4.37818L13.6252 2.08363C12.2115 0.792726 10.3636 0 8.16327 0C4.97218 0 2.2115 1.79277 0.868275 4.40731L3.59555 6.48003C4.23748 4.58913 6.03711 3.18181 8.16327 3.18181Z" fill="#EA4335"/>
6
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g id="icon">
3
+ <path id="Icon" d="M4.6875 10.6641H10.3125M7.06536 2.00698L2.19036 5.30377C1.99251 5.43757 1.875 5.65416 1.875 5.88503V12.0551C1.875 12.646 2.37868 13.125 3 13.125H12C12.6213 13.125 13.125 12.646 13.125 12.0551V5.88503C13.125 5.65416 13.0075 5.43757 12.8096 5.30377L7.93464 2.00698C7.67442 1.83101 7.32558 1.83101 7.06536 2.00698Z" stroke="black" stroke-opacity="0.9" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </g>
5
+ </svg>