@utopia-studio-design/design-system-cli 0.4.1 → 0.6.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 (126) hide show
  1. package/README.md +23 -0
  2. package/bin/utopia-ds-mcp.mjs +9 -2
  3. package/bin/utopia-ds.mjs +106 -8
  4. package/data/blocks/activity-feed/ActivityFeedBlock.tsx +255 -0
  5. package/data/blocks/activity-feed/README.md +16 -0
  6. package/data/blocks/activity-feed/activity-feed-contract.ts +96 -0
  7. package/data/blocks/activity-feed/activity-feed-fixture.ts +22 -0
  8. package/data/blocks/activity-feed/activity-feed.css +154 -0
  9. package/data/blocks/agent-status/AgentStatusBlock.tsx +313 -0
  10. package/data/blocks/agent-status/README.md +15 -0
  11. package/data/blocks/agent-status/agent-status-contract.ts +133 -0
  12. package/data/blocks/agent-status/agent-status-fixture.ts +79 -0
  13. package/data/blocks/agent-status/agent-status.css +216 -0
  14. package/data/blocks/analytics-dashboard/AnalyticsDashboardBlocks.tsx +107 -0
  15. package/data/blocks/analytics-dashboard/AnalyticsMetricBlocks.tsx +87 -0
  16. package/data/blocks/analytics-dashboard/README.md +17 -0
  17. package/data/blocks/analytics-dashboard/analytics-dashboard.css +92 -0
  18. package/data/blocks/calendar-application/CalendarApplicationBlocks.tsx +519 -0
  19. package/data/blocks/calendar-application/README.md +20 -0
  20. package/data/blocks/calendar-application/calendar-application.css +216 -0
  21. package/data/blocks/calendar-application/calendar-localization.ts +90 -0
  22. package/data/blocks/calendar-application/calendar-panel-contract.ts +72 -0
  23. package/data/blocks/dashboard-composer/CeramicPuckAdapter.tsx +380 -0
  24. package/data/blocks/dashboard-composer/DashboardComposerBlocks.tsx +337 -0
  25. package/data/blocks/dashboard-composer/README.md +16 -0
  26. package/data/blocks/dashboard-composer/RegistryDataBlocks.tsx +238 -0
  27. package/data/blocks/dashboard-composer/dashboard-composer.css +328 -0
  28. package/data/blocks/database-workspace/DatabaseWorkspaceBlocks.tsx +240 -0
  29. package/data/blocks/database-workspace/README.md +9 -0
  30. package/data/blocks/database-workspace/database-workspace.css +107 -0
  31. package/data/blocks/interaction-workbench/InteractionWorkbench.tsx +271 -0
  32. package/data/blocks/interaction-workbench/README.md +9 -0
  33. package/data/blocks/interaction-workbench/interaction-workbench.css +226 -0
  34. package/data/docs/calendar.md +164 -0
  35. package/data/docs/charts.md +95 -0
  36. package/data/docs/composition-registry.md +174 -0
  37. package/data/docs/dashboard-composer.md +94 -0
  38. package/data/docs/database.md +68 -0
  39. package/data/docs/examples/dashboard-layout.v1.json +65 -0
  40. package/data/docs/foundations.md +1 -1
  41. package/data/docs/interaction-foundation.md +109 -0
  42. package/data/docs/quick-start-ai.md +23 -1
  43. package/data/manifests/blocks.json +392 -0
  44. package/data/manifests/catalog.json +7 -4
  45. package/data/manifests/components.json +179 -0
  46. package/data/manifests/composition-registry.json +427 -0
  47. package/data/manifests/motion-profiles.json +1 -1
  48. package/data/manifests/patterns.json +113 -1
  49. package/data/manifests/templates.json +346 -0
  50. package/data/manifests/theme-renacore.json +142 -0
  51. package/data/manifests/theme-vyapti.json +125 -0
  52. package/data/manifests/themes.json +400 -28
  53. package/data/templates/analytics-dashboard/AnalyticsDashboardBlocks.tsx +107 -0
  54. package/data/templates/analytics-dashboard/AnalyticsMetricBlocks.tsx +87 -0
  55. package/data/templates/analytics-dashboard/README.md +6 -0
  56. package/data/templates/analytics-dashboard/analytics-dashboard.css +92 -0
  57. package/data/templates/analytics-dashboard/index.html +13 -0
  58. package/data/templates/analytics-dashboard/main.tsx +196 -0
  59. package/data/templates/analytics-dashboard/styles.css +120 -0
  60. package/data/templates/analytics-dashboard/template.manifest.json +17 -0
  61. package/data/templates/calendar-application/CalendarApplicationBlocks.tsx +519 -0
  62. package/data/templates/calendar-application/README.md +10 -0
  63. package/data/templates/calendar-application/calendar-application.css +216 -0
  64. package/data/templates/calendar-application/calendar-localization.ts +90 -0
  65. package/data/templates/calendar-application/index.html +14 -0
  66. package/data/templates/calendar-application/main.tsx +409 -0
  67. package/data/templates/calendar-application/styles.css +248 -0
  68. package/data/templates/calendar-application/template.manifest.json +27 -0
  69. package/data/templates/composition-registry-lab/README.md +9 -0
  70. package/data/templates/composition-registry-lab/index.html +14 -0
  71. package/data/templates/composition-registry-lab/main.tsx +217 -0
  72. package/data/templates/composition-registry-lab/sample-layout.json +63 -0
  73. package/data/templates/composition-registry-lab/styles.css +343 -0
  74. package/data/templates/composition-registry-lab/template.manifest.json +34 -0
  75. package/data/templates/dashboard-composer/ActivityFeedBlock.tsx +255 -0
  76. package/data/templates/dashboard-composer/AgentStatusBlock.tsx +313 -0
  77. package/data/templates/dashboard-composer/AnalyticsDashboardBlocks.tsx +107 -0
  78. package/data/templates/dashboard-composer/AnalyticsMetricBlocks.tsx +87 -0
  79. package/data/templates/dashboard-composer/CalendarApplicationBlocks.tsx +519 -0
  80. package/data/templates/dashboard-composer/CeramicPuckAdapter.tsx +380 -0
  81. package/data/templates/dashboard-composer/DashboardComposerBlocks.tsx +337 -0
  82. package/data/templates/dashboard-composer/DatabaseWorkspaceBlocks.tsx +240 -0
  83. package/data/templates/dashboard-composer/README.md +53 -0
  84. package/data/templates/dashboard-composer/RegistryDataBlocks.tsx +238 -0
  85. package/data/templates/dashboard-composer/activity-feed-contract.ts +96 -0
  86. package/data/templates/dashboard-composer/activity-feed-fixture.ts +22 -0
  87. package/data/templates/dashboard-composer/activity-feed.css +154 -0
  88. package/data/templates/dashboard-composer/agent-status-contract.ts +133 -0
  89. package/data/templates/dashboard-composer/agent-status-fixture.ts +79 -0
  90. package/data/templates/dashboard-composer/agent-status-lab/index.html +19 -0
  91. package/data/templates/dashboard-composer/agent-status-lab/main.tsx +151 -0
  92. package/data/templates/dashboard-composer/agent-status-lab/styles.css +139 -0
  93. package/data/templates/dashboard-composer/agent-status.css +216 -0
  94. package/data/templates/dashboard-composer/analytics-dashboard.css +92 -0
  95. package/data/templates/dashboard-composer/calendar-application.css +216 -0
  96. package/data/templates/dashboard-composer/calendar-localization.ts +90 -0
  97. package/data/templates/dashboard-composer/calendar-panel-contract.ts +72 -0
  98. package/data/templates/dashboard-composer/dashboard-composer.css +328 -0
  99. package/data/templates/dashboard-composer/database-workspace.css +107 -0
  100. package/data/templates/dashboard-composer/index.html +14 -0
  101. package/data/templates/dashboard-composer/main.tsx +550 -0
  102. package/data/templates/dashboard-composer/sample-layout.json +147 -0
  103. package/data/templates/dashboard-composer/styles.css +198 -0
  104. package/data/templates/dashboard-composer/template.manifest.json +34 -0
  105. package/data/templates/dashboard-composer/visual-editor/PuckEditorSurface.tsx +132 -0
  106. package/data/templates/dashboard-composer/visual-editor/VisualEditorApp.tsx +343 -0
  107. package/data/templates/dashboard-composer/visual-editor/index.html +17 -0
  108. package/data/templates/dashboard-composer/visual-editor/main.tsx +95 -0
  109. package/data/templates/dashboard-composer/visual-editor/styles.css +310 -0
  110. package/data/templates/database-workspace/DatabaseWorkspaceBlocks.tsx +240 -0
  111. package/data/templates/database-workspace/README.md +5 -0
  112. package/data/templates/database-workspace/database-workspace.css +107 -0
  113. package/data/templates/database-workspace/index.html +14 -0
  114. package/data/templates/database-workspace/main.tsx +247 -0
  115. package/data/templates/database-workspace/styles.css +121 -0
  116. package/data/templates/database-workspace/template.manifest.json +19 -0
  117. package/data/templates/interaction-lab/InteractionWorkbench.tsx +271 -0
  118. package/data/templates/interaction-lab/README.md +7 -0
  119. package/data/templates/interaction-lab/index.html +14 -0
  120. package/data/templates/interaction-lab/interaction-workbench.css +226 -0
  121. package/data/templates/interaction-lab/main.tsx +104 -0
  122. package/data/templates/interaction-lab/styles.css +224 -0
  123. package/data/templates/interaction-lab/template.manifest.json +36 -0
  124. package/lib/api.mjs +72 -5
  125. package/lib/composition-runtime.mjs +382 -0
  126. package/package.json +4 -1
@@ -0,0 +1,87 @@
1
+ import * as React from 'react'
2
+ import { Badge } from '@utopia-studio-design/design-system/Badge'
3
+ import type { ChartState } from '@utopia-studio-design/design-system/Charts'
4
+ import './analytics-dashboard.css'
5
+
6
+ export interface MetricDefinition {
7
+ change?: number
8
+ changeLabel?: string
9
+ format?: 'number' | 'percent' | 'currency'
10
+ id: string
11
+ label: string
12
+ value: number
13
+ }
14
+
15
+ export interface KPIGridProps extends React.HTMLAttributes<HTMLDivElement> {
16
+ currency?: string
17
+ density?: 'compact' | 'default' | 'comfortable'
18
+ locale?: string
19
+ metrics: MetricDefinition[]
20
+ state?: ChartState
21
+ }
22
+
23
+ export function KPIGrid({
24
+ className,
25
+ currency = 'USD',
26
+ density = 'default',
27
+ locale,
28
+ metrics,
29
+ state = metrics.length === 0 ? 'empty' : 'ready',
30
+ ...props
31
+ }: KPIGridProps) {
32
+ const formatMetric = (metric: MetricDefinition) => {
33
+ if (metric.format === 'percent') {
34
+ return new Intl.NumberFormat(locale, { style: 'percent', maximumFractionDigits: 1 }).format(metric.value)
35
+ }
36
+ if (metric.format === 'currency') {
37
+ return new Intl.NumberFormat(locale, {
38
+ style: 'currency',
39
+ currency,
40
+ notation: 'compact',
41
+ maximumFractionDigits: 1,
42
+ }).format(metric.value)
43
+ }
44
+ return new Intl.NumberFormat(locale, { notation: 'compact', maximumFractionDigits: 1 }).format(metric.value)
45
+ }
46
+
47
+ return (
48
+ <section
49
+ aria-busy={state === 'loading'}
50
+ aria-label="Key performance indicators"
51
+ className={['ceramic-kpi-grid', className].filter(Boolean).join(' ')}
52
+ data-density={density}
53
+ data-state={state}
54
+ {...props}
55
+ >
56
+ {state === 'loading' ? (
57
+ <>
58
+ {[0, 1, 2].map((item) => <span aria-hidden="true" className="ceramic-kpi-skeleton" key={item} />)}
59
+ <span className="uds-visually-hidden" role="status">Loading metrics…</span>
60
+ </>
61
+ ) : null}
62
+ {state === 'empty' ? (
63
+ <div className="ceramic-kpi-state" role="status">
64
+ <strong>No metrics available</strong>
65
+ <span>Choose another period or data source.</span>
66
+ </div>
67
+ ) : null}
68
+ {state === 'error' ? (
69
+ <div className="ceramic-kpi-state" role="alert">
70
+ <strong>Metrics unavailable</strong>
71
+ <span>Reconnect the data source and try again.</span>
72
+ </div>
73
+ ) : null}
74
+ {state === 'ready' || state === 'partial' ? metrics.map((metric) => (
75
+ <article className="ceramic-kpi" key={metric.id}>
76
+ <span>{metric.label}</span>
77
+ <strong>{formatMetric(metric)}</strong>
78
+ {metric.change !== undefined ? (
79
+ <Badge variant="outline">
80
+ {metric.change > 0 ? '+' : ''}{metric.change.toFixed(1)}% {metric.changeLabel ?? 'vs previous'}
81
+ </Badge>
82
+ ) : <span className="ceramic-kpi-unavailable">No comparison</span>}
83
+ </article>
84
+ )) : null}
85
+ </section>
86
+ )
87
+ }
@@ -0,0 +1,6 @@
1
+ # Ceramic analytics dashboard
2
+
3
+ Runnable integration template for the Ceramic Charts system. It demonstrates all
4
+ chart families, governed data states, semantic series, keyboard exploration,
5
+ RTL, light/dark modes, benchmark annotations, KPI blocks, and an accessible data
6
+ table alternative.
@@ -0,0 +1,92 @@
1
+ .ceramic-kpi-grid {
2
+ display: grid;
3
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
4
+ gap: var(--interaction-density-default);
5
+ min-inline-size: 0;
6
+ }
7
+
8
+ .ceramic-kpi-grid[data-density='compact'] {
9
+ gap: var(--interaction-density-compact);
10
+ }
11
+
12
+ .ceramic-kpi-grid[data-density='comfortable'] {
13
+ gap: var(--interaction-density-comfortable);
14
+ }
15
+
16
+ .ceramic-kpi {
17
+ display: grid;
18
+ align-content: start;
19
+ gap: var(--space-2);
20
+ min-inline-size: 0;
21
+ padding: var(--space-4);
22
+ border: var(--border-width-hairline) solid var(--border);
23
+ background: var(--surface);
24
+ }
25
+
26
+ .ceramic-kpi > span:first-child,
27
+ .ceramic-kpi-unavailable {
28
+ color: var(--muted-foreground);
29
+ font-size: var(--font-size-small);
30
+ }
31
+
32
+ .ceramic-kpi > strong {
33
+ font-size: var(--font-size-marketing-h3);
34
+ font-weight: var(--font-weight-medium);
35
+ line-height: var(--line-height-tight);
36
+ }
37
+
38
+ .ceramic-kpi .uds-badge {
39
+ inline-size: fit-content;
40
+ }
41
+
42
+ .ceramic-kpi-state {
43
+ display: grid;
44
+ grid-column: 1 / -1;
45
+ justify-items: center;
46
+ gap: var(--space-2);
47
+ min-block-size: 10rem;
48
+ padding: var(--space-6);
49
+ border: var(--border-width-hairline) dashed var(--border);
50
+ color: var(--muted-foreground);
51
+ text-align: center;
52
+ }
53
+
54
+ .ceramic-kpi-state strong {
55
+ color: var(--foreground);
56
+ }
57
+
58
+ .ceramic-kpi-skeleton {
59
+ min-block-size: 9rem;
60
+ background: var(--surface-strong);
61
+ }
62
+
63
+ .ceramic-analytics-header {
64
+ display: flex;
65
+ align-items: end;
66
+ justify-content: space-between;
67
+ gap: var(--space-6);
68
+ padding-block: var(--space-6);
69
+ border-block-end: var(--border-width-hairline) solid var(--border);
70
+ }
71
+
72
+ .ceramic-analytics-header > div {
73
+ display: grid;
74
+ gap: var(--space-1);
75
+ }
76
+
77
+ .ceramic-analytics-header :is(h1, p) {
78
+ margin: 0;
79
+ }
80
+
81
+ .ceramic-analytics-header > div > span,
82
+ .ceramic-analytics-header p {
83
+ color: var(--muted-foreground);
84
+ font-size: var(--font-size-small);
85
+ }
86
+
87
+ @media (max-width: 44rem) {
88
+ .ceramic-analytics-header {
89
+ align-items: stretch;
90
+ flex-direction: column;
91
+ }
92
+ }
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Ceramic Analytics Dashboard</title>
7
+ </head>
8
+ <body>
9
+ <a class="skip-link" href="#main-content">Skip to analytics dashboard</a>
10
+ <div id="root"></div>
11
+ <script type="module" src="./main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,196 @@
1
+ import { StrictMode, useMemo, useState } from 'react'
2
+ import { createRoot } from 'react-dom/client'
3
+ import { Button } from '@utopia-studio-design/design-system/Button'
4
+ import type {
5
+ ChartDatum,
6
+ ChartSeriesDefinition,
7
+ ChartState,
8
+ ChartType,
9
+ } from '@utopia-studio-design/design-system/Charts'
10
+ import {
11
+ AnalyticsChartCard,
12
+ AnalyticsDashboardHeader,
13
+ ChartWithDataTable,
14
+ ComparisonChartPanel,
15
+ KPIGrid,
16
+ } from './AnalyticsDashboardBlocks'
17
+ import '@utopia-studio-design/design-system/core.css'
18
+ import '@utopia-studio-design/design-system/themes/utopia-default.css'
19
+ import './analytics-dashboard.css'
20
+ import './styles.css'
21
+
22
+ const baseData: ChartDatum[] = [
23
+ { id: 'jan', label: 'Jan', values: { revenue: 42, previous: 35, forecast: 44 } },
24
+ { id: 'feb', label: 'Feb', values: { revenue: 46, previous: 38, forecast: 47 } },
25
+ { id: 'mar', label: 'Mar', values: { revenue: 51, previous: 43, forecast: 52 } },
26
+ { id: 'apr', label: 'Apr', values: { revenue: 49, previous: 45, forecast: 55 } },
27
+ { id: 'may', label: 'May', values: { revenue: 58, previous: 48, forecast: 59 } },
28
+ { id: 'jun', label: 'Jun', values: { revenue: 63, previous: 52, forecast: 64 } },
29
+ { id: 'jul', label: 'Jul', values: { revenue: 68, previous: 57, forecast: 70 } },
30
+ { id: 'aug', label: 'Aug', values: { revenue: 74, previous: 61, forecast: 76 } },
31
+ ]
32
+
33
+ const series: ChartSeriesDefinition[] = [
34
+ { id: 'revenue', label: 'Current revenue', tone: 'primary' },
35
+ { id: 'previous', label: 'Previous period', tone: 'benchmark', pattern: 'dash' },
36
+ { id: 'forecast', label: 'Forecast', tone: 'forecast', pattern: 'dot' },
37
+ ]
38
+
39
+ const chartTypes: ChartType[] = ['line', 'bar', 'area', 'stacked', 'scatter', 'donut']
40
+ const chartStates: ChartState[] = ['ready', 'partial', 'loading', 'empty', 'error']
41
+
42
+ function App() {
43
+ const [dark, setDark] = useState(false)
44
+ const [direction, setDirection] = useState<'ltr' | 'rtl'>('ltr')
45
+ const [state, setState] = useState<ChartState>('ready')
46
+ const [type, setType] = useState<ChartType>('line')
47
+ const [refreshCount, setRefreshCount] = useState(0)
48
+ const data = useMemo(() => {
49
+ if (state !== 'partial') return baseData
50
+ return baseData.map((datum, index) => index === 3
51
+ ? { ...datum, values: { ...datum.values, previous: null } }
52
+ : datum)
53
+ }, [state])
54
+
55
+ return (
56
+ <main
57
+ className="analytics-template"
58
+ data-color-mode={dark ? 'dark' : 'light'}
59
+ data-theme="utopia-default"
60
+ dir="ltr"
61
+ id="main-content"
62
+ >
63
+ <AnalyticsDashboardHeader
64
+ onRefresh={() => setRefreshCount((value) => value + 1)}
65
+ periodLabel={`Jan–Aug 2026 · refreshed ${refreshCount} times`}
66
+ title="Growth performance"
67
+ />
68
+
69
+ <section aria-label="Analytics preview controls" className="analytics-controls">
70
+ <div>
71
+ <span>Chart family</span>
72
+ <div className="analytics-control-row">
73
+ {chartTypes.map((chartType) => (
74
+ <Button
75
+ aria-pressed={type === chartType}
76
+ key={chartType}
77
+ size="sm"
78
+ variant={type === chartType ? 'default' : 'outline'}
79
+ onClick={() => setType(chartType)}
80
+ >
81
+ {chartType}
82
+ </Button>
83
+ ))}
84
+ </div>
85
+ </div>
86
+ <div>
87
+ <span>Data state</span>
88
+ <div className="analytics-control-row">
89
+ {chartStates.map((chartState) => (
90
+ <Button
91
+ aria-pressed={state === chartState}
92
+ key={chartState}
93
+ size="sm"
94
+ variant={state === chartState ? 'default' : 'outline'}
95
+ onClick={() => setState(chartState)}
96
+ >
97
+ {chartState}
98
+ </Button>
99
+ ))}
100
+ </div>
101
+ </div>
102
+ <div className="analytics-mode-controls">
103
+ <Button
104
+ aria-pressed={direction === 'rtl'}
105
+ size="sm"
106
+ variant="outline"
107
+ onClick={() => setDirection((value) => value === 'ltr' ? 'rtl' : 'ltr')}
108
+ >
109
+ {direction === 'ltr' ? 'Switch charts to RTL' : 'Switch charts to LTR'}
110
+ </Button>
111
+ <Button
112
+ aria-pressed={dark}
113
+ size="sm"
114
+ variant="outline"
115
+ onClick={() => setDark((value) => !value)}
116
+ >
117
+ {dark ? 'Switch to light' : 'Switch to dark'}
118
+ </Button>
119
+ </div>
120
+ </section>
121
+
122
+ <section aria-labelledby="metrics-title" className="analytics-section">
123
+ <header>
124
+ <span>Overview</span>
125
+ <h2 id="metrics-title">Key metrics</h2>
126
+ </header>
127
+ <KPIGrid
128
+ metrics={[
129
+ { id: 'revenue', label: 'Revenue', value: 742000, format: 'currency', change: 12.4 },
130
+ { id: 'conversion', label: 'Conversion', value: 0.184, format: 'percent', change: 2.1 },
131
+ { id: 'accounts', label: 'Active accounts', value: 12840, change: 8.7 },
132
+ { id: 'retention', label: 'Retention', value: 0.916, format: 'percent' },
133
+ ]}
134
+ state={state === 'partial' ? 'ready' : state}
135
+ />
136
+ </section>
137
+
138
+ <section aria-labelledby="trend-title" className="analytics-section analytics-grid">
139
+ <header>
140
+ <span>Analysis</span>
141
+ <h2 id="trend-title">Revenue trend</h2>
142
+ <p>Keyboard exploration, semantic series, benchmark, forecast, and exact-value alternatives.</p>
143
+ </header>
144
+ <div className="analytics-primary-chart" dir={direction}>
145
+ <AnalyticsChartCard
146
+ data={state === 'empty' ? [] : data}
147
+ description="Monthly recurring revenue, USD thousands"
148
+ direction={direction}
149
+ series={series}
150
+ state={state}
151
+ title="Monthly revenue"
152
+ type={type}
153
+ />
154
+ </div>
155
+ <div dir={direction}>
156
+ <ComparisonChartPanel
157
+ benchmarkLabel="Annual target"
158
+ benchmarkValue={70}
159
+ data={state === 'empty' ? [] : data}
160
+ description="Current period against target"
161
+ direction={direction}
162
+ series={series.slice(0, 2)}
163
+ state={state}
164
+ title="Target comparison"
165
+ type="bar"
166
+ />
167
+ </div>
168
+ </section>
169
+
170
+ <section aria-labelledby="evidence-title" className="analytics-section">
171
+ <header>
172
+ <span>Accessible evidence</span>
173
+ <h2 id="evidence-title">Chart with data table</h2>
174
+ <p>The table remains the exact-value and screen-reader alternative to the visual plot.</p>
175
+ </header>
176
+ <div dir={direction}>
177
+ <ChartWithDataTable
178
+ data={state === 'empty' ? [] : data}
179
+ description="Exact values for audit and comparison"
180
+ direction={direction}
181
+ series={series}
182
+ state={state}
183
+ title="Revenue evidence"
184
+ type="line"
185
+ />
186
+ </div>
187
+ </section>
188
+ </main>
189
+ )
190
+ }
191
+
192
+ createRoot(document.getElementById('root')!).render(
193
+ <StrictMode>
194
+ <App />
195
+ </StrictMode>,
196
+ )
@@ -0,0 +1,120 @@
1
+ * {
2
+ box-sizing: border-box;
3
+ }
4
+
5
+ html,
6
+ body,
7
+ #root {
8
+ min-block-size: 100%;
9
+ margin: 0;
10
+ }
11
+
12
+ body {
13
+ background: var(--background);
14
+ color: var(--foreground);
15
+ font-family: var(--font-sans);
16
+ }
17
+
18
+ .skip-link {
19
+ position: fixed;
20
+ inset-block-start: var(--space-2);
21
+ inset-inline-start: var(--space-2);
22
+ z-index: var(--z-modal);
23
+ translate: 0 -200%;
24
+ }
25
+
26
+ .skip-link:focus {
27
+ translate: 0;
28
+ }
29
+
30
+ .analytics-template {
31
+ min-block-size: 100vh;
32
+ padding-inline: var(--page-gutter);
33
+ background: var(--background);
34
+ color: var(--foreground);
35
+ }
36
+
37
+ .analytics-template > * {
38
+ max-inline-size: var(--container-standard);
39
+ margin-inline: auto;
40
+ }
41
+
42
+ .analytics-controls {
43
+ display: grid;
44
+ grid-template-columns: repeat(2, minmax(0, 1fr)) auto;
45
+ align-items: end;
46
+ gap: var(--space-4);
47
+ padding-block: var(--space-4);
48
+ border-block-end: var(--border-width-hairline) solid var(--border);
49
+ }
50
+
51
+ .analytics-controls > div {
52
+ display: grid;
53
+ gap: var(--space-2);
54
+ min-inline-size: 0;
55
+ }
56
+
57
+ .analytics-controls > div > span,
58
+ .analytics-section > header > span {
59
+ color: var(--muted-foreground);
60
+ font-size: var(--font-size-eyebrow);
61
+ font-weight: var(--font-weight-semibold);
62
+ letter-spacing: var(--tracking-eyebrow);
63
+ text-transform: uppercase;
64
+ }
65
+
66
+ .analytics-control-row,
67
+ .analytics-mode-controls {
68
+ display: flex;
69
+ flex-wrap: wrap;
70
+ gap: var(--space-1);
71
+ }
72
+
73
+ .analytics-section {
74
+ display: grid;
75
+ gap: var(--space-4);
76
+ padding-block: var(--space-6);
77
+ border-block-end: var(--border-width-hairline) solid var(--border);
78
+ }
79
+
80
+ .analytics-section > header {
81
+ display: grid;
82
+ gap: var(--space-1);
83
+ }
84
+
85
+ .analytics-section > header :is(h2, p) {
86
+ margin: 0;
87
+ }
88
+
89
+ .analytics-section > header p {
90
+ color: var(--muted-foreground);
91
+ }
92
+
93
+ .analytics-grid {
94
+ grid-template-columns: minmax(0, 2fr) minmax(18rem, 1fr);
95
+ }
96
+
97
+ .analytics-grid > header {
98
+ grid-column: 1 / -1;
99
+ }
100
+
101
+ .analytics-grid > div {
102
+ min-inline-size: 0;
103
+ }
104
+
105
+ @media (max-width: 64rem) {
106
+ .analytics-controls,
107
+ .analytics-grid {
108
+ grid-template-columns: 1fr;
109
+ }
110
+
111
+ .analytics-grid > header {
112
+ grid-column: auto;
113
+ }
114
+ }
115
+
116
+ @media (max-width: 44rem) {
117
+ .analytics-mode-controls {
118
+ flex-direction: column;
119
+ }
120
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "id": "analytics-dashboard",
3
+ "title": "Analytics Dashboard",
4
+ "status": "available",
5
+ "route": "/templates/analytics-dashboard/",
6
+ "entry": "templates/analytics-dashboard/main.tsx",
7
+ "blocks": [
8
+ "analytics-chart-card",
9
+ "kpi-grid",
10
+ "comparison-chart-panel",
11
+ "chart-with-data-table"
12
+ ],
13
+ "states": ["loading", "empty", "error", "partial", "ready"],
14
+ "themes": ["utopia-default"],
15
+ "directions": ["ltr", "rtl"],
16
+ "responsiveMinimum": "320px"
17
+ }