@utopia-studio-design/design-system-cli 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 (127) hide show
  1. package/README.md +23 -0
  2. package/bin/utopia-ds-mcp.mjs +9 -2
  3. package/bin/utopia-ds.mjs +115 -7
  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 +2 -1
  41. package/data/docs/interaction-foundation.md +109 -0
  42. package/data/docs/observability.md +165 -0
  43. package/data/docs/quick-start-ai.md +55 -1
  44. package/data/manifests/blocks.json +392 -0
  45. package/data/manifests/catalog.json +7 -4
  46. package/data/manifests/components.json +191 -3
  47. package/data/manifests/composition-registry.json +427 -0
  48. package/data/manifests/motion-profiles.json +1 -1
  49. package/data/manifests/patterns.json +113 -1
  50. package/data/manifests/templates.json +346 -0
  51. package/data/manifests/theme-renacore.json +142 -0
  52. package/data/manifests/themes.json +314 -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/lib/telemetry.mjs +74 -0
  127. package/package.json +4 -1
@@ -0,0 +1,216 @@
1
+ .ceramic-agent-status {
2
+ container-type: inline-size;
3
+ display: grid;
4
+ align-content: start;
5
+ gap: var(--space-3);
6
+ min-inline-size: 0;
7
+ block-size: 100%;
8
+ padding: var(--space-4);
9
+ background: var(--surface);
10
+ color: var(--foreground);
11
+ text-align: start;
12
+ }
13
+
14
+ .ceramic-agent-status > header,
15
+ .ceramic-agent-status > header > div,
16
+ .ceramic-agent-status-heading,
17
+ .ceramic-agent-status-meta,
18
+ .ceramic-agent-approval,
19
+ .ceramic-agent-approval > div:last-child {
20
+ display: flex;
21
+ align-items: center;
22
+ }
23
+
24
+ .ceramic-agent-status > header {
25
+ justify-content: space-between;
26
+ gap: var(--space-3);
27
+ padding-block-end: var(--space-3);
28
+ border-block-end: var(--border-width-hairline) solid var(--border);
29
+ }
30
+
31
+ .ceramic-agent-status > header > div {
32
+ align-items: start;
33
+ flex-direction: column;
34
+ gap: var(--space-1);
35
+ min-inline-size: 0;
36
+ }
37
+
38
+ .ceramic-agent-status :is(h2, p) {
39
+ margin: 0;
40
+ }
41
+
42
+ .ceramic-agent-status > header span {
43
+ color: var(--muted-foreground);
44
+ font-size: var(--font-size-eyebrow);
45
+ font-weight: var(--font-weight-semibold);
46
+ letter-spacing: var(--tracking-eyebrow);
47
+ text-transform: uppercase;
48
+ }
49
+
50
+ .ceramic-agent-status-list {
51
+ display: grid;
52
+ gap: var(--space-3);
53
+ margin: 0;
54
+ padding: 0;
55
+ list-style: none;
56
+ }
57
+
58
+ .ceramic-agent-status-list > li {
59
+ display: grid;
60
+ gap: var(--space-3);
61
+ min-inline-size: 0;
62
+ padding: var(--space-3);
63
+ border: var(--border-width-hairline) solid var(--border);
64
+ background: var(--background);
65
+ }
66
+
67
+ .ceramic-agent-status[data-density='compact'] .ceramic-agent-status-list {
68
+ gap: var(--space-2);
69
+ }
70
+
71
+ .ceramic-agent-status[data-density='compact'] .ceramic-agent-status-list > li {
72
+ gap: var(--space-2);
73
+ padding: var(--space-2);
74
+ }
75
+
76
+ .ceramic-agent-status[data-density='comfortable'] .ceramic-agent-status-list > li {
77
+ gap: var(--space-4);
78
+ padding: var(--space-4);
79
+ }
80
+
81
+ .ceramic-agent-status-heading {
82
+ align-items: start;
83
+ gap: var(--space-3);
84
+ min-inline-size: 0;
85
+ }
86
+
87
+ .ceramic-agent-status-heading > .uds-status-dot {
88
+ flex: 0 0 auto;
89
+ margin-block-start: var(--space-2);
90
+ background: var(--muted-foreground);
91
+ }
92
+
93
+ .ceramic-agent-status-heading > .uds-status-dot[data-execution-state='running'] {
94
+ background: var(--primary);
95
+ }
96
+
97
+ .ceramic-agent-status-heading > .uds-status-dot[data-execution-state='waiting-approval'],
98
+ .ceramic-agent-status-heading > .uds-status-dot[data-execution-state='blocked'] {
99
+ background: var(--warning);
100
+ }
101
+
102
+ .ceramic-agent-status-heading > .uds-status-dot[data-execution-state='failed'] {
103
+ background: var(--destructive);
104
+ }
105
+
106
+ .ceramic-agent-status-heading > .uds-status-dot[data-execution-state='completed'] {
107
+ background: var(--success);
108
+ }
109
+
110
+ .ceramic-agent-status-heading > div {
111
+ display: grid;
112
+ flex: 1 1 auto;
113
+ gap: var(--space-1);
114
+ min-inline-size: 0;
115
+ }
116
+
117
+ .ceramic-agent-status-heading > div > span,
118
+ .ceramic-agent-status-list > li > p,
119
+ .ceramic-agent-status-meta {
120
+ color: var(--muted-foreground);
121
+ font-size: var(--font-size-small);
122
+ }
123
+
124
+ .ceramic-agent-status-heading > div > :is(strong, span),
125
+ .ceramic-agent-status-list > li > p,
126
+ .ceramic-agent-approval :is(span, p) {
127
+ overflow-wrap: anywhere;
128
+ }
129
+
130
+ .ceramic-agent-status-heading > .uds-badge {
131
+ flex: 0 0 auto;
132
+ }
133
+
134
+ .ceramic-agent-status-list > li > p {
135
+ line-height: var(--line-height-body);
136
+ }
137
+
138
+ .ceramic-agent-status-meta {
139
+ flex-wrap: wrap;
140
+ gap: var(--space-2);
141
+ }
142
+
143
+ .ceramic-agent-status-meta [data-risk='high'] {
144
+ border-color: var(--destructive-border);
145
+ color: var(--destructive-text);
146
+ }
147
+
148
+ .ceramic-agent-status-meta [data-risk='medium'] {
149
+ border-color: var(--warning-border);
150
+ color: var(--warning-text);
151
+ }
152
+
153
+ .ceramic-agent-approval {
154
+ align-items: start;
155
+ justify-content: space-between;
156
+ gap: var(--space-3);
157
+ padding: var(--space-3);
158
+ border-inline-start: var(--border-width-strong) solid var(--warning-border);
159
+ background: var(--warning-surface);
160
+ }
161
+
162
+ .ceramic-agent-approval > div:first-child {
163
+ display: grid;
164
+ gap: var(--space-1);
165
+ min-inline-size: 0;
166
+ }
167
+
168
+ .ceramic-agent-approval > div:first-child > span,
169
+ .ceramic-agent-approval > div:first-child > p {
170
+ color: var(--muted-foreground);
171
+ font-size: var(--font-size-small);
172
+ }
173
+
174
+ .ceramic-agent-approval > div:last-child {
175
+ flex: 0 0 auto;
176
+ flex-wrap: wrap;
177
+ gap: var(--space-2);
178
+ }
179
+
180
+ .ceramic-agent-status-state {
181
+ display: grid;
182
+ align-content: center;
183
+ justify-items: start;
184
+ gap: var(--space-2);
185
+ min-block-size: 10rem;
186
+ color: var(--muted-foreground);
187
+ }
188
+
189
+ .ceramic-agent-status-state strong {
190
+ color: var(--foreground);
191
+ }
192
+
193
+ @container (min-width: 48rem) {
194
+ .ceramic-agent-status-list {
195
+ grid-template-columns: repeat(2, minmax(0, 1fr));
196
+ }
197
+ }
198
+
199
+ @media (max-width: 32rem) {
200
+ .ceramic-agent-status {
201
+ padding: var(--space-3);
202
+ }
203
+
204
+ .ceramic-agent-approval {
205
+ align-items: stretch;
206
+ flex-direction: column;
207
+ }
208
+
209
+ .ceramic-agent-approval > div:last-child {
210
+ inline-size: 100%;
211
+ }
212
+
213
+ .ceramic-agent-approval > div:last-child > .uds-button {
214
+ flex: 1 1 auto;
215
+ }
216
+ }
@@ -0,0 +1,107 @@
1
+ import * as React from 'react'
2
+ import { Button } from '@utopia-studio-design/design-system/Button'
3
+ import {
4
+ ChartRoot,
5
+ type ChartAnnotationDefinition,
6
+ type ChartDatum,
7
+ type ChartSeriesDefinition,
8
+ type ChartState,
9
+ type ChartType,
10
+ } from '@utopia-studio-design/design-system/Charts'
11
+ import './analytics-dashboard.css'
12
+
13
+ export {
14
+ KPIGrid,
15
+ type KPIGridProps,
16
+ type MetricDefinition,
17
+ } from './AnalyticsMetricBlocks'
18
+
19
+ export interface AnalyticsChartCardProps extends Omit<React.HTMLAttributes<HTMLElement>, 'title'> {
20
+ annotations?: ChartAnnotationDefinition[]
21
+ data: ChartDatum[]
22
+ description?: React.ReactNode
23
+ direction?: 'ltr' | 'rtl'
24
+ series: ChartSeriesDefinition[]
25
+ showDataTable?: boolean
26
+ state?: ChartState
27
+ title: React.ReactNode
28
+ type?: ChartType
29
+ }
30
+
31
+ export function AnalyticsChartCard({
32
+ annotations = [],
33
+ data,
34
+ description,
35
+ direction,
36
+ series,
37
+ showDataTable,
38
+ state,
39
+ title,
40
+ type,
41
+ ...props
42
+ }: AnalyticsChartCardProps) {
43
+ return (
44
+ <ChartRoot
45
+ accessibleSummary={`${String(title)} across ${data.length} periods. Use the chart explorer or data table for exact values.`}
46
+ annotations={annotations}
47
+ axis={{ showGrid: true }}
48
+ data={data}
49
+ description={description}
50
+ direction={direction}
51
+ series={series}
52
+ showDataTable={showDataTable}
53
+ state={state}
54
+ title={title}
55
+ type={type}
56
+ {...props}
57
+ />
58
+ )
59
+ }
60
+
61
+ export interface ComparisonChartPanelProps extends AnalyticsChartCardProps {
62
+ benchmarkLabel?: string
63
+ benchmarkValue?: number
64
+ }
65
+
66
+ export function ComparisonChartPanel({
67
+ benchmarkLabel = 'Target',
68
+ benchmarkValue,
69
+ annotations = [],
70
+ ...props
71
+ }: ComparisonChartPanelProps) {
72
+ const nextAnnotations = benchmarkValue === undefined
73
+ ? annotations
74
+ : [
75
+ ...annotations,
76
+ { id: 'benchmark', label: benchmarkLabel, value: benchmarkValue, tone: 'neutral' as const },
77
+ ]
78
+ return <AnalyticsChartCard annotations={nextAnnotations} {...props} />
79
+ }
80
+
81
+ export function ChartWithDataTable(props: AnalyticsChartCardProps) {
82
+ return <AnalyticsChartCard {...props} showDataTable />
83
+ }
84
+
85
+ export interface AnalyticsDashboardHeaderProps extends React.HTMLAttributes<HTMLElement> {
86
+ onRefresh?: () => void
87
+ periodLabel: string
88
+ title: string
89
+ }
90
+
91
+ export function AnalyticsDashboardHeader({
92
+ onRefresh,
93
+ periodLabel,
94
+ title,
95
+ ...props
96
+ }: AnalyticsDashboardHeaderProps) {
97
+ return (
98
+ <header className="ceramic-analytics-header" {...props}>
99
+ <div>
100
+ <span>Analytics dashboard</span>
101
+ <h1>{title}</h1>
102
+ <p>{periodLabel}</p>
103
+ </div>
104
+ <Button variant="outline" onClick={onRefresh}>Refresh data</Button>
105
+ </header>
106
+ )
107
+ }
@@ -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,17 @@
1
+ # Analytics dashboard blocks
2
+
3
+ Copyable Ceramic blocks for governed analytics dashboards:
4
+
5
+ - `AnalyticsChartCard`
6
+ - `KPIGrid`
7
+ - `ComparisonChartPanel`
8
+ - `ChartWithDataTable`
9
+
10
+ The blocks consume `@utopia-studio-design/design-system/Charts`, keep product
11
+ queries and persistence outside the design system, and use semantic
12
+ visualization tokens. Exact values remain available through the keyboard chart
13
+ explorer and optional data table.
14
+
15
+ `KPIGrid` lives in `AnalyticsMetricBlocks.tsx` so metric-only dashboards do not
16
+ load the chart runtime. `AnalyticsDashboardBlocks.tsx` re-exports it for
17
+ compatibility with existing analytics-template imports.
@@ -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
+ }