astryx-dracula 0.5.0 → 0.6.2

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 (129) hide show
  1. package/BRAND.md +3 -3
  2. package/README.md +7 -4
  3. package/USAGE.md +49 -49
  4. package/astryx-dracula.d.ts +2 -2
  5. package/astryx-dracula.js +25 -9
  6. package/astryx-dracula.variants.d.ts +2 -2
  7. package/astryx.integration.mjs +1 -1
  8. package/icons.mjs +21 -17
  9. package/icons.tsx +69 -62
  10. package/package.json +125 -120
  11. package/shared/auth-chrome-config.ts +6 -6
  12. package/shared/auth-chrome.tsx +143 -144
  13. package/shared/auth-copy.ts +14 -14
  14. package/shared/chaptered-doc-config.ts +48 -51
  15. package/shared/chaptered-doc.tsx +267 -289
  16. package/shared/chart-hues.ts +25 -10
  17. package/shared/chart-labels.tsx +31 -30
  18. package/shared/chart-legend.tsx +55 -56
  19. package/shared/chart-panel-style.ts +3 -3
  20. package/shared/data-bar.tsx +97 -102
  21. package/shared/format-money.ts +1 -1
  22. package/shared/gallery-image.ts +6 -6
  23. package/shared/login-demo.ts +1 -1
  24. package/shared/metric-card.tsx +101 -105
  25. package/shared/metric-delta.tsx +28 -27
  26. package/shared/order-desk.tsx +205 -216
  27. package/shared/revenue-chart.tsx +145 -165
  28. package/shared/scene-castle.tsx +206 -291
  29. package/shared/scene-frame.tsx +33 -32
  30. package/shared/scene-hues.ts +17 -17
  31. package/shared/scene-tile.tsx +88 -92
  32. package/shared/settings-data.ts +119 -125
  33. package/shared/settings-rows.tsx +122 -159
  34. package/shared/sparkline.tsx +75 -88
  35. package/shared/sso-icons.tsx +10 -22
  36. package/shared/stars.tsx +16 -25
  37. package/templates/ai-chat-landing.template.mjs +5 -4
  38. package/templates/ai-chat-landing.tsx +473 -460
  39. package/templates/ai-chat.template.mjs +5 -4
  40. package/templates/ai-chat.tsx +543 -612
  41. package/templates/blank.template.mjs +5 -4
  42. package/templates/blank.tsx +18 -14
  43. package/templates/centered-hero.template.mjs +5 -4
  44. package/templates/centered-hero.tsx +55 -68
  45. package/templates/classic-gallery.template.mjs +5 -4
  46. package/templates/classic-gallery.tsx +97 -104
  47. package/templates/contact-form.template.mjs +5 -4
  48. package/templates/contact-form.tsx +247 -282
  49. package/templates/dashboard-portfolio.template.mjs +5 -4
  50. package/templates/dashboard-portfolio.tsx +717 -548
  51. package/templates/dashboard.template.mjs +5 -4
  52. package/templates/dashboard.tsx +596 -567
  53. package/templates/detail-page.template.mjs +5 -4
  54. package/templates/detail-page.tsx +546 -603
  55. package/templates/documentation-design.template.mjs +5 -4
  56. package/templates/documentation-design.tsx +231 -243
  57. package/templates/documentation-technical.template.mjs +5 -4
  58. package/templates/documentation-technical.tsx +236 -239
  59. package/templates/documentation.template.mjs +5 -4
  60. package/templates/documentation.tsx +267 -279
  61. package/templates/editor.template.mjs +5 -4
  62. package/templates/editor.tsx +844 -877
  63. package/templates/file-explorer.template.mjs +5 -4
  64. package/templates/file-explorer.tsx +566 -590
  65. package/templates/form-two-column.template.mjs +5 -4
  66. package/templates/form-two-column.tsx +195 -210
  67. package/templates/gallery-hero.template.mjs +5 -4
  68. package/templates/gallery-hero.tsx +93 -114
  69. package/templates/ide.template.mjs +5 -4
  70. package/templates/ide.tsx +407 -453
  71. package/templates/incident-console.template.mjs +5 -4
  72. package/templates/incident-console.tsx +593 -575
  73. package/templates/kanban-board.template.mjs +5 -4
  74. package/templates/kanban-board.tsx +665 -683
  75. package/templates/library.template.mjs +5 -4
  76. package/templates/library.tsx +409 -430
  77. package/templates/login-card.template.mjs +5 -4
  78. package/templates/login-card.tsx +63 -72
  79. package/templates/login-split.template.mjs +5 -4
  80. package/templates/login-split.tsx +192 -193
  81. package/templates/login-sso.template.mjs +5 -4
  82. package/templates/login-sso.tsx +257 -258
  83. package/templates/login.template.mjs +5 -4
  84. package/templates/login.tsx +53 -49
  85. package/templates/messaging-shell.template.mjs +5 -4
  86. package/templates/messaging-shell.tsx +556 -574
  87. package/templates/mixed-gallery.template.mjs +5 -4
  88. package/templates/mixed-gallery.tsx +60 -58
  89. package/templates/payment-form.template.mjs +5 -4
  90. package/templates/payment-form.tsx +882 -991
  91. package/templates/product-detail.template.mjs +5 -4
  92. package/templates/product-detail.tsx +261 -272
  93. package/templates/product-gallery.template.mjs +5 -4
  94. package/templates/product-gallery.tsx +126 -137
  95. package/templates/product-tour.template.mjs +5 -4
  96. package/templates/product-tour.tsx +741 -773
  97. package/templates/settings-dialog.template.mjs +5 -4
  98. package/templates/settings-dialog.tsx +688 -717
  99. package/templates/settings-sidebar.template.mjs +5 -4
  100. package/templates/settings-sidebar.tsx +762 -807
  101. package/templates/settings.template.mjs +5 -4
  102. package/templates/settings.tsx +289 -304
  103. package/templates/shell-nav.template.mjs +5 -4
  104. package/templates/shell-nav.tsx +212 -219
  105. package/templates/shell-side-nav.template.mjs +5 -4
  106. package/templates/shell-side-nav.tsx +135 -157
  107. package/templates/shell-top-nav.template.mjs +5 -4
  108. package/templates/shell-top-nav.tsx +182 -203
  109. package/templates/side-gallery.template.mjs +5 -4
  110. package/templates/side-gallery.tsx +85 -90
  111. package/templates/table-grouped.template.mjs +5 -4
  112. package/templates/table-grouped.tsx +1000 -1053
  113. package/templates/table-page-chart.template.mjs +5 -4
  114. package/templates/table-page-chart.tsx +400 -398
  115. package/templates/table-page-heatmap-status.template.mjs +5 -4
  116. package/templates/table-page-heatmap-status.tsx +483 -512
  117. package/templates/table-page-shoe-store-heatmap.template.mjs +5 -4
  118. package/templates/table-page-shoe-store-heatmap.tsx +748 -746
  119. package/templates/table-page.template.mjs +5 -4
  120. package/templates/table-page.tsx +396 -405
  121. package/templates/table.template.mjs +5 -4
  122. package/templates/table.tsx +92 -93
  123. package/templates/tech-report.template.mjs +5 -4
  124. package/templates/tech-report.tsx +1316 -1304
  125. package/templates/theme-showcase.template.mjs +5 -4
  126. package/templates/theme-showcase.tsx +871 -951
  127. package/theme.css +35 -77
  128. package/theme.css.d.ts +9 -0
  129. package/tokens.css +373 -380
@@ -3,42 +3,43 @@
3
3
  // (nothing meaningful below 12px, ever). The mono family resolves from the
4
4
  // `--font-family-mono` theme token, single-sourced with the body/code faces.
5
5
 
6
- import type {ReactNode} from 'react';
6
+ import type { ReactNode } from "react";
7
7
 
8
- export const CHART_LABEL_FILL = 'var(--color-text-paragraph)';
8
+ export const CHART_LABEL_FILL = "var(--color-text-paragraph)";
9
9
  export const CHART_LABEL_SIZE = 13;
10
10
 
11
11
  interface ChartLabelProps {
12
- x: number | string;
13
- y: number | string;
14
- textAnchor?: 'start' | 'middle' | 'end';
15
- /** Defaults to the paragraph token; pass an explicit fill for values drawn
16
- * on colored fills (e.g. in-cell heatmap counts).
17
- *
18
- * Deliberately NOT the chart-legend union: this paints TEXT, so its arms
19
- * are a text role and the on-fill role, and neither is a --color-data-*
20
- * var. Reusing the legend type here would reject the one use it exists
21
- * for. */
22
- fill?: `var(--color-text-${string})` | `var(--color-on-${string})`;
23
- children: ReactNode;
12
+ x: number | string;
13
+ y: number | string;
14
+ textAnchor?: "start" | "middle" | "end";
15
+ /** Defaults to the paragraph token; pass an explicit fill for values drawn
16
+ * on colored fills (e.g. in-cell heatmap counts).
17
+ *
18
+ * Deliberately NOT the chart-legend union: this paints TEXT, so its arms
19
+ * are a text role and the on-fill role, and neither is a --color-data-*
20
+ * var. Reusing the legend type here would reject the one use it exists
21
+ * for. */
22
+ fill?: `var(--color-text-${string})` | `var(--color-on-${string})`;
23
+ children: ReactNode;
24
24
  }
25
25
 
26
26
  export function ChartLabel({
27
- x,
28
- y,
29
- textAnchor = 'middle',
30
- fill = CHART_LABEL_FILL,
31
- children,
27
+ x,
28
+ y,
29
+ textAnchor = "middle",
30
+ fill = CHART_LABEL_FILL,
31
+ children,
32
32
  }: ChartLabelProps) {
33
- return (
34
- <text
35
- x={x}
36
- y={y}
37
- textAnchor={textAnchor}
38
- fontSize={CHART_LABEL_SIZE}
39
- fill={fill}
40
- fontFamily="var(--font-family-mono)">
41
- {children}
42
- </text>
43
- );
33
+ return (
34
+ <text
35
+ x={x}
36
+ y={y}
37
+ textAnchor={textAnchor}
38
+ fontSize={CHART_LABEL_SIZE}
39
+ fill={fill}
40
+ fontFamily="var(--font-family-mono)"
41
+ >
42
+ {children}
43
+ </text>
44
+ );
44
45
  }
@@ -21,21 +21,21 @@
21
21
  //
22
22
  // Not interactive. A legend is a key, not a control: no hover, no focus ring.
23
23
 
24
- import type {ReactNode} from 'react';
25
- import {HStack, StackItem, VStack} from '@astryxdesign/core/Stack';
26
- import {Text} from '@astryxdesign/core/Text';
27
- import type {ChartHue} from 'astryx-dracula/shared/chart-hues';
24
+ import { HStack, StackItem, VStack } from "@astryxdesign/core/Stack";
25
+ import { Text } from "@astryxdesign/core/Text";
26
+ import type { ChartHue, DataColor } from "astryx-dracula/shared/chart-hues";
27
+ import type { ReactNode } from "react";
28
28
 
29
29
  export interface ChartLegendEntry {
30
- /** REQUIRED. The hue never travels alone — this is the 1.4.1 contract. */
31
- label: string;
32
- /** A --color-data-* var, or a --color-background-* surface. Never a raw
33
- * hex, never a --dracula-* primitive.
34
- *
35
- * The surface arm is not optional: a legend whose first row is a recessive
36
- * surface is normal (the heatmap's zero cell), so a data-only type would
37
- * reject the value the heatmap is supposed to paint. */
38
- color: ChartHue | `var(--color-data-${string})` | `var(--color-background-${string})`;
30
+ /** REQUIRED. The hue never travels alone — this is the 1.4.1 contract. */
31
+ label: string;
32
+ /** A --color-data-* var, or a --color-background-* surface. Never a raw
33
+ * hex, never a --dracula-* primitive.
34
+ *
35
+ * The surface arm is not optional: a legend whose first row is a recessive
36
+ * surface is normal (the heatmap's zero cell), so a data-only type would
37
+ * reject the value the heatmap is supposed to paint. */
38
+ color: ChartHue | DataColor | `var(--color-background-${string})`;
39
39
  }
40
40
 
41
41
  // Kept as a string-keyed fall-through on purpose, and DO NOT narrow it: call
@@ -47,53 +47,52 @@ export interface ChartLegendEntry {
47
47
  // none does. The behaviour is correct; only the comment was wrong.
48
48
 
49
49
  export interface ChartLegendProps {
50
- entries: readonly ChartLegendEntry[];
51
- /** Spacing step between entries. @default 2 */
52
- gap?: 1 | 2 | 3 | 4 | 6;
53
- /** Accessible name for the legend as a group. */
54
- label?: string;
55
- /** Swatch size in px. @default 10 */
56
- swatchSize?: number;
57
- /** Caption under the legend, e.g. "Hourly intervals · trailing 24 hours". */
58
- caption?: ReactNode;
59
- /** Orientation. @default 'row' */
60
- direction?: 'row' | 'column';
50
+ entries: readonly ChartLegendEntry[];
51
+ /** Spacing step between entries. @default 2 */
52
+ gap?: 1 | 2 | 3 | 4 | 6;
53
+ /** Accessible name for the legend as a group. */
54
+ label?: string;
55
+ /** Swatch size in px. @default 10 */
56
+ swatchSize?: number;
57
+ /** Caption under the legend, e.g. "Hourly intervals · trailing 24 hours". */
58
+ caption?: ReactNode;
59
+ /** Orientation. @default 'row' */
60
+ direction?: "row" | "column";
61
61
  }
62
62
 
63
-
64
63
  export function ChartLegend({
65
- entries,
66
- gap = 2,
67
- swatchSize = 10,
68
- label,
69
- caption,
70
- direction = 'row',
64
+ entries,
65
+ gap = 2,
66
+ swatchSize = 10,
67
+ label,
68
+ caption,
69
+ direction = "row",
71
70
  }: ChartLegendProps) {
72
- const Stack = direction === 'row' ? HStack : VStack;
73
- return (
74
- <VStack gap={1}>
75
- <Stack gap={gap} vAlign="center" role="list" aria-label={label}>
76
- {entries.map(entry => (
77
- <HStack key={entry.label} gap={1} vAlign="center" role="listitem">
78
- <StackItem
79
- style={{
80
- width: `${swatchSize}px`,
81
- height: `${swatchSize}px`,
82
- flexShrink: 0,
83
- background: entry.color,
84
- borderRadius: 'var(--radius-inner)',
85
- }}
86
- />
87
- {/* supporting, not body: a legend label is metadata by the
71
+ const Stack = direction === "row" ? HStack : VStack;
72
+ return (
73
+ <VStack gap={1}>
74
+ <Stack gap={gap} vAlign="center" role="list" aria-label={label}>
75
+ {entries.map((entry) => (
76
+ <HStack key={entry.label} gap={1} vAlign="center" role="listitem">
77
+ <StackItem
78
+ style={{
79
+ width: `${swatchSize}px`,
80
+ height: `${swatchSize}px`,
81
+ flexShrink: 0,
82
+ background: entry.color,
83
+ borderRadius: "var(--radius-inner)",
84
+ }}
85
+ />
86
+ {/* supporting, not body: a legend label is metadata by the
88
87
  typography doctrine, and it never carries a sentence the page
89
88
  needs the reader to have. */}
90
- <Text type="supporting" color="secondary" hasTabularNumbers>
91
- {entry.label}
92
- </Text>
93
- </HStack>
94
- ))}
95
- </Stack>
96
- {caption}
97
- </VStack>
98
- );
89
+ <Text type="supporting" color="secondary" hasTabularNumbers>
90
+ {entry.label}
91
+ </Text>
92
+ </HStack>
93
+ ))}
94
+ </Stack>
95
+ {caption}
96
+ </VStack>
97
+ );
99
98
  }
@@ -2,12 +2,12 @@
2
2
  // kit (dashboard, portfolio, chart, heatmap-status, shoe-store). Split from
3
3
  // shared/revenue-chart.tsx so that file exports only components and React Fast
4
4
  // Refresh stays intact; see shared/scene-hues.ts for the same reason.
5
- import type {CSSProperties} from 'react';
5
+ import type { CSSProperties } from "react";
6
6
 
7
7
  // One chart-panel look for every hand-drawn SVG chart panel in this wave's
8
8
  // templates (dashboard, portfolio, chart, heatmap-status, shoe-store). Restate
9
9
  // nowhere: theme Card variant work lands in wave 2.
10
10
  export const CHART_PANEL_STYLE: CSSProperties = {
11
- backgroundColor: 'var(--color-background)',
12
- border: 'var(--border-width) solid var(--color-separator)',
11
+ backgroundColor: "var(--color-background)",
12
+ border: "var(--border-width) solid var(--color-separator)",
13
13
  };
@@ -36,125 +36,120 @@
36
36
  // Not interactive. A data bar is not a control, so it carries no hover channel
37
37
  // and no focus ring; the inset-ring decision belongs to interactive surfaces.
38
38
 
39
- import type {CSSProperties} from 'react';
40
- import {HStack, StackItem, VStack} from '@astryxdesign/core/Stack';
41
- import {Text} from '@astryxdesign/core/Text';
42
- import type {ChartHue} from './chart-hues';
39
+ import { HStack, StackItem, VStack } from "@astryxdesign/core/Stack";
40
+ import { Text } from "@astryxdesign/core/Text";
41
+ import type { CSSProperties } from "react";
42
+ import type { ChartHue, DataColor } from "./chart-hues";
43
43
 
44
44
  export interface DataBarSegment {
45
- /** Stable key for the segment. */
46
- id: string;
47
- value: number;
48
- /** MUST be a --color-data-categorical-* or --color-data-<family>-N var.
49
- * Never a raw hex, never a --dracula-* primitive. Typed rather than
50
- * documented: `string` made the rule a comment nobody could break. */
51
- color: ChartHue | `var(--color-data-${string})`;
45
+ /** Stable key for the segment. */
46
+ id: string;
47
+ value: number;
48
+ /** MUST be a --color-data-categorical-* or --color-data-<family>-N var.
49
+ * Never a raw hex, never a --dracula-* primitive. Typed rather than
50
+ * documented: `string` made the rule a comment nobody could break. */
51
+ color: ChartHue | DataColor;
52
52
  }
53
53
 
54
54
  /** Minimum segment share, so a tiny non-zero value stays visible. */
55
55
  const MIN_SEGMENT_PCT = 2;
56
56
 
57
57
  export interface DataBarProps {
58
- /** Accessible name. Required — a bar with no name is an unlabelled graphic. */
59
- label: string;
60
- segments: readonly DataBarSegment[];
61
- /** Bar thickness in px. @default 12 */
62
- height?: number;
63
- /** Show the summed value under the bar. @default false */
64
- hasValueLabel?: boolean;
65
- /** Formats the summed value. @default toLocaleString() */
66
- formatValue?: (total: number) => string;
67
- /** Gap between segments as a spacing-scale step. @default 0 */
68
- gap?: 0 | 0.5 | 1 | 1.5 | 2 | 3;
58
+ /** Accessible name. Required — a bar with no name is an unlabelled graphic. */
59
+ label: string;
60
+ segments: readonly DataBarSegment[];
61
+ /** Bar thickness in px. @default 12 */
62
+ height?: number;
63
+ /** Show the summed value under the bar. @default false */
64
+ hasValueLabel?: boolean;
65
+ /** Formats the summed value. @default toLocaleString() */
66
+ formatValue?: (total: number) => string;
67
+ /** Gap between segments as a spacing-scale step. @default 0 */
68
+ gap?: 0 | 0.5 | 1 | 1.5 | 2 | 3;
69
69
  }
70
70
 
71
71
  const segmentStyle: CSSProperties = {
72
- borderRadius: 'var(--radius-inner)',
72
+ borderRadius: "var(--radius-inner)",
73
73
  };
74
74
 
75
75
  export function DataBar({
76
- label,
77
- segments,
78
- height = 12,
79
- hasValueLabel = false,
80
- formatValue = total => total.toLocaleString(),
81
- gap = 0,
76
+ label,
77
+ segments,
78
+ height = 12,
79
+ hasValueLabel = false,
80
+ formatValue = (total) => total.toLocaleString(),
81
+ gap = 0,
82
82
  }: DataBarProps) {
83
- // Negative values are not a magnitude and are clamped out before the ratio is
84
- // taken: summing a negative into `total` inflates every other segment's
85
- // share and can push the bar past 100%. An empty or all-zero bar falls
86
- // through to the track fill below.
87
- const total = segments.reduce(
88
- (sum, segment) => sum + Math.max(0, segment.value),
89
- 0,
90
- );
91
- const visible = segments
92
- .map(segment => {
93
- if (segment.value <= 0) return null;
94
- return {
95
- id: segment.id,
96
- color: segment.color,
97
- share: (segment.value / total) * 100,
98
- };
99
- })
100
- .filter((segment): segment is NonNullable<typeof segment> => segment !== null);
83
+ // Negative values are not a magnitude and are clamped out before the ratio is
84
+ // taken: summing a negative into `total` inflates every other segment's
85
+ // share and can push the bar past 100%. An empty or all-zero bar falls
86
+ // through to the track fill below.
87
+ const total = segments.reduce((sum, segment) => sum + Math.max(0, segment.value), 0);
88
+ const visible = segments
89
+ .map((segment) => {
90
+ if (segment.value <= 0) return null;
91
+ return {
92
+ id: segment.id,
93
+ color: segment.color,
94
+ share: (segment.value / total) * 100,
95
+ };
96
+ })
97
+ .filter((segment): segment is NonNullable<typeof segment> => segment !== null);
101
98
 
102
- // A tiny non-zero value must stay visible, so each segment gets a floor —
103
- // but the floors are taken OUT of the segments that can afford it. Flooring
104
- // every segment independently overflows the bar (3 tiny + 1 huge measured
105
- // 106%), and a bar wider than its track is worse than a missing speck.
106
- const floorTotal = visible.length * MIN_SEGMENT_PCT;
107
- const surplus = Math.max(0, 100 - floorTotal);
108
- const slack = Math.max(
109
- 0,
110
- visible.reduce(
111
- (sum, segment) => sum + Math.max(0, segment.share - MIN_SEGMENT_PCT),
112
- 0,
113
- ),
114
- );
115
- const widths = visible.map(segment => {
116
- const room = Math.max(0, segment.share - MIN_SEGMENT_PCT);
117
- const claim = slack > 0 ? (room / slack) * surplus : 0;
118
- return Math.min(100, MIN_SEGMENT_PCT + claim);
119
- });
120
- const drawn = visible
121
- .map((segment, index) => ({...segment, width: widths[index]}))
122
- .filter(segment => segment.width > 0);
99
+ // A tiny non-zero value must stay visible, so each segment gets a floor —
100
+ // but the floors are taken OUT of the segments that can afford it. Flooring
101
+ // every segment independently overflows the bar (3 tiny + 1 huge measured
102
+ // 106%), and a bar wider than its track is worse than a missing speck.
103
+ const floorTotal = visible.length * MIN_SEGMENT_PCT;
104
+ const surplus = Math.max(0, 100 - floorTotal);
105
+ const slack = Math.max(
106
+ 0,
107
+ visible.reduce((sum, segment) => sum + Math.max(0, segment.share - MIN_SEGMENT_PCT), 0),
108
+ );
109
+ const widths = visible.map((segment) => {
110
+ const room = Math.max(0, segment.share - MIN_SEGMENT_PCT);
111
+ const claim = slack > 0 ? (room / slack) * surplus : 0;
112
+ return Math.min(100, MIN_SEGMENT_PCT + claim);
113
+ });
114
+ const drawn = visible
115
+ .map((segment, index) => ({ ...segment, width: widths[index] }))
116
+ .filter((segment) => segment.width > 0);
123
117
 
124
- const bar = (
125
- <HStack
126
- gap={gap}
127
- height={height}
128
- width="100%"
129
- vAlign="stretch"
130
- role="img"
131
- aria-label={label}
132
- style={
133
- total > 0
134
- ? undefined
135
- : {
136
- // Deliberately --color-progress-value, NOT a text token — see the header.
137
- background: 'var(--color-progress-value)',
138
- borderRadius: 'var(--radius-inner)',
139
- }
140
- }>
141
- {drawn.map(segment => (
142
- <StackItem
143
- key={segment.id}
144
- style={{...segmentStyle, flexBasis: `${segment.width}%`, background: segment.color}}
145
- />
146
- ))}
147
- </HStack>
148
- );
118
+ const bar = (
119
+ <HStack
120
+ gap={gap}
121
+ height={height}
122
+ width="100%"
123
+ vAlign="stretch"
124
+ role="img"
125
+ aria-label={label}
126
+ style={
127
+ total > 0
128
+ ? undefined
129
+ : {
130
+ // Deliberately --color-progress-value, NOT a text token — see the header.
131
+ background: "var(--color-progress-value)",
132
+ borderRadius: "var(--radius-inner)",
133
+ }
134
+ }
135
+ >
136
+ {drawn.map((segment) => (
137
+ <StackItem
138
+ key={segment.id}
139
+ style={{ ...segmentStyle, flexBasis: `${segment.width}%`, background: segment.color }}
140
+ />
141
+ ))}
142
+ </HStack>
143
+ );
149
144
 
150
- if (!hasValueLabel) return bar;
145
+ if (!hasValueLabel) return bar;
151
146
 
152
- return (
153
- <VStack gap={1}>
154
- {bar}
155
- <Text type="supporting" color="secondary" hasTabularNumbers>
156
- {formatValue(total)}
157
- </Text>
158
- </VStack>
159
- );
147
+ return (
148
+ <VStack gap={1}>
149
+ {bar}
150
+ <Text type="supporting" color="secondary" hasTabularNumbers>
151
+ {formatValue(total)}
152
+ </Text>
153
+ </VStack>
154
+ );
160
155
  }
@@ -1,3 +1,3 @@
1
1
  export function formatMoney(n: number): string {
2
- return `$${n.toFixed(2)}`;
2
+ return `$${n.toFixed(2)}`;
3
3
  }
@@ -3,18 +3,18 @@
3
3
  // primitive, so gallery SVG scenes fill their box and clip to rounded corners
4
4
  // through these two styles. Replace with component props once #2582 lands.
5
5
 
6
- import type {CSSProperties} from 'react';
6
+ import type { CSSProperties } from "react";
7
7
 
8
8
  // Fills the AspectRatio box. No objectFit prop on AspectRatio (#2582).
9
9
  export const galleryImage: CSSProperties = {
10
- width: '100%',
11
- height: '100%',
12
- display: 'block',
10
+ width: "100%",
11
+ height: "100%",
12
+ display: "block",
13
13
  };
14
14
 
15
15
  // Rounds the gallery corners (element radius; chapter art uses container — see
16
16
  // shared/chaptered-doc sceneClip). No radius prop on AspectRatio (#2582);
17
17
  export const galleryImageClip: CSSProperties = {
18
- borderRadius: 'var(--radius-element)',
19
- overflow: 'clip',
18
+ borderRadius: "var(--radius-element)",
19
+ overflow: "clip",
20
20
  };
@@ -3,5 +3,5 @@
3
3
  // Promise.withResolvers — the ES2022 target breaks strict consumers), awaited
4
4
  // inside each template's own startTransition.
5
5
  export function demoLogin(): Promise<void> {
6
- return new Promise(resolve => setTimeout(resolve, 2000));
6
+ return new Promise((resolve) => setTimeout(resolve, 2000));
7
7
  }