@aiaiai-pt/design-system 0.46.3 → 0.48.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.
@@ -142,9 +142,13 @@
142
142
  return buildChartOption(chartData, {
143
143
  tokens: {
144
144
  accent: token("--color-accent", "#2563eb"),
145
- textPrimary: token("--color-text-primary", "#1f2937"),
145
+ // Semantic tokens name the primary text colour `--color-text` (see
146
+ // tokens/semantic.css); `--color-text-primary` never existed, so this
147
+ // read silently fell back to #1f2937 — invisible on dark schemes (#89).
148
+ textPrimary: token("--color-text", token("--color-text-primary", "#1f2937")),
146
149
  textSecondary: token("--color-text-secondary", "#6b7280"),
147
150
  border: token("--color-border", "#e5e7eb"),
151
+ surface: token("--color-surface", "#ffffff"),
148
152
  },
149
153
  locale,
150
154
  legend,
@@ -167,11 +171,12 @@
167
171
 
168
172
  (async () => {
169
173
  try {
170
- const [core, charts, components, renderers] = await Promise.all([
174
+ const [core, charts, components, renderers, features] = await Promise.all([
171
175
  import("echarts/core"),
172
176
  import("echarts/charts"),
173
177
  import("echarts/components"),
174
178
  import("echarts/renderers"),
179
+ import("echarts/features"),
175
180
  ]);
176
181
  if (disposed || !container) return;
177
182
 
@@ -184,6 +189,11 @@
184
189
  components.TooltipComponent,
185
190
  components.LegendComponent,
186
191
  renderers.CanvasRenderer,
192
+ // echarts 6 (#773): `grid.containLabel` moved behind this opt-in
193
+ // feature. Registering it preserves the v5 layout (labels contained
194
+ // inside the grid) our `buildChartOption` relies on — without it
195
+ // echarts 6 silently ignores `containLabel` and clips axis labels.
196
+ features.LegacyGridContainLabel,
187
197
  ]);
188
198
 
189
199
  chart = core.init(container, null, { renderer: "canvas" });
@@ -186,7 +186,7 @@ export function watchTheme(onchange) {
186
186
  const mo = new MutationObserver(() => onchange());
187
187
  mo.observe(document.documentElement, {
188
188
  attributes: true,
189
- attributeFilter: ["data-theme", "class"],
189
+ attributeFilter: ["data-theme", "data-scheme", "data-contrast", "class"],
190
190
  });
191
191
 
192
192
  const mql = window.matchMedia("(prefers-color-scheme: dark)");
@@ -26,6 +26,9 @@ export interface ChartTokens {
26
26
  textPrimary: string;
27
27
  textSecondary: string;
28
28
  border: string;
29
+ /** Panel surface behind floating chrome (tooltip); themes the default-white
30
+ ECharts tooltip for dark/high-contrast schemes (#89). */
31
+ surface: string;
29
32
  }
30
33
  export interface BuildChartOptionOpts {
31
34
  tokens: ChartTokens;
@@ -28,6 +28,9 @@ export interface ChartTokens {
28
28
  textPrimary: string;
29
29
  textSecondary: string;
30
30
  border: string;
31
+ /** Panel surface behind floating chrome (tooltip); themes the default-white
32
+ ECharts tooltip for dark/high-contrast schemes (#89). */
33
+ surface: string;
31
34
  }
32
35
 
33
36
  export interface BuildChartOptionOpts {
@@ -44,6 +47,16 @@ export interface BuildChartOptionOpts {
44
47
  innerRadius?: number;
45
48
  }
46
49
 
50
+ /** Token-driven tooltip chrome — ECharts defaults to a white panel that
51
+ * ignores the scheme entirely (#89). */
52
+ function tooltipChrome(tokens: ChartTokens): Record<string, unknown> {
53
+ return {
54
+ backgroundColor: tokens.surface,
55
+ borderColor: tokens.border,
56
+ textStyle: { color: tokens.textPrimary },
57
+ };
58
+ }
59
+
47
60
  /** A localised number formatter (DS-supplied — never operator code). */
48
61
  function makeFmt(locale: string): (value: number) => string {
49
62
  const nf = new Intl.NumberFormat(locale);
@@ -78,12 +91,13 @@ function buildPieOption(
78
91
  return {
79
92
  animation: false,
80
93
  tooltip: {
94
+ ...tooltipChrome(tokens),
81
95
  trigger: "item",
82
96
  formatter: (p: { name: string; value: number; percent: number }) =>
83
97
  `${p.name}: ${fmt(p.value)} (${p.percent}%)`,
84
98
  },
85
99
  ...(opts.legend
86
- ? { legend: { show: true, textStyle: { color: tokens.textPrimary } } }
100
+ ? { legend: { show: true, top: 0, textStyle: { color: tokens.textPrimary } } }
87
101
  : {}),
88
102
  color: palette,
89
103
  series: [
@@ -208,6 +222,7 @@ function buildCartesianOption(
208
222
  containLabel: true,
209
223
  },
210
224
  tooltip: {
225
+ ...tooltipChrome(tokens),
211
226
  trigger: "axis",
212
227
  axisPointer: { type: "shadow" },
213
228
  formatter: (
@@ -227,6 +242,9 @@ function buildCartesianOption(
227
242
  ? {
228
243
  legend: {
229
244
  show: true,
245
+ // echarts 6 lays the unpositioned legend over the bottom category
246
+ // labels; pin it to the top strip grid.top already reserves (#89).
247
+ top: 0,
230
248
  data: series.map((s) => s.name),
231
249
  textStyle: { color: tokens.textPrimary },
232
250
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiaiai-pt/design-system",
3
- "version": "0.46.3",
3
+ "version": "0.48.0",
4
4
  "description": "Design system tokens and Svelte components for aiaiai products",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -12,7 +12,8 @@
12
12
  "access": "public"
13
13
  },
14
14
  "workspaces": [
15
- "site"
15
+ "site",
16
+ "packages/widget-system"
16
17
  ],
17
18
  "scripts": {
18
19
  "build:types": "rm -f components/*.d.ts components/renderer/*.d.ts && svelte-package --input . --output dist && cp dist/components/*.d.ts components/ && cp dist/components/renderer/*.d.ts components/renderer/ && rm -rf dist",
@@ -83,14 +84,14 @@
83
84
  "@codemirror/view": "^6.40.0",
84
85
  "@lezer/highlight": "^1.0.0",
85
86
  "date-fns": "^4.1.0",
86
- "echarts": "^5.5.0",
87
+ "echarts": "^6.1.0",
87
88
  "ol": "^10.0.0",
88
89
  "svelte": "^5.0.0"
89
90
  },
90
91
  "devDependencies": {
91
92
  "@sveltejs/package": "^2.5.7",
92
93
  "date-fns": "^4.1.0",
93
- "echarts": "^5.6.0",
94
+ "echarts": "^6.1.0",
94
95
  "jsdom": "^29.1.1",
95
96
  "ol": "^10.8.0",
96
97
  "svelte": "^5.55.3",
@@ -230,11 +230,11 @@
230
230
  dark wash for free. Per-theme bespoke dark = a `[data-theme="x"]`
231
231
  block in the theme file, not here.
232
232
 
233
- Selector is `:root[data-scheme="dark"]` (0,1,1): it TIES tenant theme
234
- blocks (`:root[data-theme="x"]`, runtime-injected in <head>) and wins
235
- on order — DS styles load after the injected theme block. Consumers
236
- resolving an "auto" preference must set the RESOLVED value on <html>
237
- (a pre-paint `prefers-color-scheme` read), never "auto" itself. */
233
+ Selector is `:root[data-scheme="dark"]` (0,2,0 pseudo-class + attribute):
234
+ it TIES runtime-injected tenant theme blocks (`:root[data-theme="x"]`,
235
+ also 0,2,0) and wins on order — DS styles load after the injected theme
236
+ block. Consumers resolving an "auto" preference must set the RESOLVED value
237
+ on <html> (a pre-paint `prefers-color-scheme` read), never "auto" itself. */
238
238
 
239
239
  :root[data-scheme="dark"] {
240
240
  color-scheme: dark;
@@ -0,0 +1,224 @@
1
+ /*
2
+ * aiaiai Design System — UBP Theme (ubp.css)
3
+ *
4
+ * Professional instrument theme for the urban intelligence workspace
5
+ * (ubp-ai-workspace). Tier 2 (Bespoke): distinct cool-steel identity
6
+ * optimised for data-dense, map-centric, dark-primary interfaces.
7
+ *
8
+ * Selector: [data-theme="ubp"]
9
+ * Paired scheme: [data-scheme="dark"] for the workspace default.
10
+ * The generic dark layer (semantic.css) handles neutral role inversion;
11
+ * the [data-theme="ubp"][data-scheme="dark"] block below adjusts only
12
+ * the brand values that need different treatment on a cool-dark surface.
13
+ *
14
+ * ─── Neutral palette — owned, not verbatim Tailwind slate ───────────────
15
+ * The UBP neutral ramp is derived from Tailwind slate shifted 10° toward
16
+ * teal/steel in OKLCH. This is a deliberate identity decision: the hue
17
+ * drift gives mid-tones (400–600) a visible cool-steel signature while
18
+ * keeping the lightest stops (50–200) effectively neutral, making the
19
+ * palette perceptually distinct from Tailwind defaults without tipping
20
+ * into obvious teal.
21
+ *
22
+ * Before → After (10° OKLCH hue shift, all other OKLCH params unchanged):
23
+ * #f8fafc → #f8fafc (slate-50, unchanged at 8-bit precision)
24
+ * #f1f5f9 → #f0f5f9 (slate-100, surface-secondary)
25
+ * #e2e8f0 → #e1e9f0 (slate-200, surface-tertiary)
26
+ * #cbd5e1 → #c9d6e0 (slate-300, border)
27
+ * #94a3b8 → #91a4b7 (slate-400, border-strong / dark text-secondary)
28
+ * #64748b → #60758a (slate-500, text-muted)
29
+ * #475569 → #435668 (slate-600, dark border-strong)
30
+ * #334155 → #2f4254 (slate-700, text-secondary light / dark surfaces)
31
+ * #1e293b → #1a2a3a (slate-800, dark surface-secondary)
32
+ * #0f172a → #0b182a (slate-900, text light / dark surface)
33
+ *
34
+ * ─── Font decision: inherit DS pairing ──────────────────────────────────
35
+ * UBP inherits Sistema's default type pairing unchanged:
36
+ * sans → Instrument Sans (--font-sans / --raw-font-sans)
37
+ * mono → JetBrains Mono, ui-monospace (--font-mono / --raw-font-mono)
38
+ * (Berkeley Mono leads the mono stack in the token but ships no @font-face;
39
+ * JetBrains Mono is the effective shipped face — see sistema #82.)
40
+ * The theme sets NO --font-sans or --font-mono override; font role-tokens
41
+ * resolve to the DS defaults via semantic.css inheritance.
42
+ * Full decision record: dev_docs/solutions/ubp-font-self-hosting.md
43
+ *
44
+ * ─── Historical 3.0:1 subtitle regression control ────────────────────────
45
+ * The 3.0:1 defect occurred when --color-text-muted was used in subtitle
46
+ * roles, giving only 3.0:1 contrast on the surface. This theme prevents it
47
+ * by ensuring --color-text-secondary (the subtitle token) clears WCAG AA
48
+ * (≥4.5:1) on every declared surface. See tests/ubp-theme-contrast.test.ts.
49
+ *
50
+ * ─── Owned by: DS-H0 #65, implemented under #66 ─────────────────────────
51
+ * Apply via: <html data-theme="ubp"> (light) or
52
+ * <html data-theme="ubp" data-scheme="dark"> (dark / workspace default)
53
+ * Layer independently with data-contrast, data-text-size, data-link-highlight.
54
+ */
55
+
56
+ [data-theme="ubp"] {
57
+ /* ═══════════════════════════════════════════════════════════════
58
+ TYPOGRAPHY — inherited from DS (no override)
59
+ Instrument Sans (sans) + JetBrains Mono (mono) via semantic.css.
60
+ ═══════════════════════════════════════════════════════════════ */
61
+
62
+ /* ═══════════════════════════════════════════════════════════════
63
+ COLOR — Cool steel palette (light-scheme base)
64
+ Neutrals: 10° OKLCH teal-shift from Tailwind slate — see file header.
65
+ ═══════════════════════════════════════════════════════════════ */
66
+
67
+ /* Surfaces: cool off-white — sterile precision vs aiaiai warm paper */
68
+ --color-surface: #f8fafc; /* steel-50 (unchanged at 8-bit) */
69
+ --color-surface-secondary: #f0f5f9; /* steel-100 */
70
+ --color-surface-tertiary: #e1e9f0; /* steel-200 */
71
+ --color-surface-raised: #ffffff; /* pure white for popovers/menus */
72
+ --color-overlay: rgba(
73
+ 11,
74
+ 24,
75
+ 42,
76
+ 0.5
77
+ ); /* cool dark overlay (steel-900 base) */
78
+
79
+ /* Borders: cool steel */
80
+ --color-border: #c9d6e0; /* steel-300 */
81
+ --color-border-strong: #91a4b7; /* steel-400 */
82
+
83
+ /* Text: cool dark steel — contrast validated in ubp-theme-contrast.test.ts
84
+ text (#0b182a) on surface (#f8fafc): 17.03:1 [AAA]
85
+ text-secondary (#2f4254) on surface (#f8fafc): 9.90:1 [AAA — subtitle safe]
86
+ text-muted (#60758a) on surface (#f8fafc): 4.55:1 [AA — above 3.0 defect floor] */
87
+ --color-text: #0b182a; /* steel-900 */
88
+ --color-text-secondary: #2f4254; /* steel-700 */
89
+ --color-text-muted: #60758a; /* steel-500 */
90
+ --color-text-on-accent: #ffffff; /* white on blue-600: 5.17:1 [AA] */
91
+
92
+ /* Accent: UBP blue — maps, focus rings, primary actions
93
+ accent (#2563eb) on surface (#f8fafc): 4.94:1 [AA for normal text, ≥3:1 for UI] */
94
+ --color-accent: #2563eb; /* blue-600 */
95
+ --color-accent-hover: #1d4ed8; /* blue-700 */
96
+ --color-accent-subtle: #eff6ff; /* blue-50 equivalent — selected states */
97
+
98
+ /* Status semantic colours: override for cool-palette compatibility.
99
+ WCAG AA on --color-surface (#f8fafc) and --color-surface-secondary (#f1f5f9).
100
+ Subtle washes re-tuned for the cool surfaces.
101
+ NOTE: --color-info is NOT overridden; inherits DS teal-blue
102
+ (--raw-color-blue-600, #2e63a3 — 5.85:1 on surface), distinct from
103
+ --color-accent (#2563eb). See contrast suite: tests/ubp-theme-contrast.test.ts */
104
+ --color-destructive: #dc2626; /* red-600, 4.62:1 on surface */
105
+ --color-destructive-hover: #b91c1c; /* red-700 */
106
+ --color-destructive-subtle: #fef2f2;
107
+
108
+ --color-success: #16a34a; /* green-600, 3.15:1 on surface — large UI only */
109
+ --color-success-subtle: #f0fdf4;
110
+
111
+ --color-warning: #ca8a04; /* amber-600, 3.1:1 on surface — large UI only */
112
+ --color-warning-subtle: #fffbeb;
113
+
114
+ /* Focus: accent blue, visible on all surfaces */
115
+ --focus-ring-color: var(--color-accent);
116
+
117
+ /* ═══════════════════════════════════════════════════════════════
118
+ RADIUS — Keep sharp (precision instrument)
119
+ Already: sm=2px md=4px lg=8px from semantic.css defaults
120
+ ═══════════════════════════════════════════════════════════════ */
121
+ /* No override — sharp radii match the professional platform aesthetic */
122
+
123
+ /* ═══════════════════════════════════════════════════════════════
124
+ ELEVATION — Subtle shadow on raised surfaces (instrument feel)
125
+ Uses steel-900 (#0b182a) as the shadow base (10° teal-shifted).
126
+ ═══════════════════════════════════════════════════════════════ */
127
+ --elevation-raised:
128
+ 0 1px 3px rgba(11, 24, 42, 0.08), 0 1px 2px rgba(11, 24, 42, 0.04);
129
+ --elevation-overlay:
130
+ 0 4px 16px rgba(11, 24, 42, 0.12), 0 2px 6px rgba(11, 24, 42, 0.06);
131
+
132
+ /* ═══════════════════════════════════════════════════════════════
133
+ MOTION — unchanged from DS defaults.
134
+ Product choreography (map transitions, panel morphs) belongs in
135
+ the host's ws.css, not the theme.
136
+ ═══════════════════════════════════════════════════════════════ */
137
+
138
+ /* Total color overrides: 22. Elevation: 2. Focus alias: 1.
139
+ Grand total: ~25 tokens. Typography/motion/spacing/grid unchanged.
140
+ --color-info and --color-info-subtle NOT overridden (inherit DS teal-blue). */
141
+ }
142
+
143
+ /* Desktop density: compact button heights for toolbar/sidebar layouts.
144
+ Scoped to ≥1024px so mobile targets stay at the DS default (44px for
145
+ --button-lg-height), satisfying the Tier-2 a11y checklist minimum.
146
+ Analogous to how other bespoke themes adjust density only where a
147
+ pointer device makes smaller targets safe (WCAG 2.5.5 advisory). */
148
+ @media (min-width: 1024px) {
149
+ [data-theme="ubp"] {
150
+ --button-sm-height: 28px; /* DS Tier-2 floor: 28px min (reference/theming.md:221) */
151
+ --button-md-height: 32px;
152
+ --button-lg-height: 44px; /* DS Tier-2 floor: 44px min (reference/theming.md:221) */
153
+ }
154
+ }
155
+
156
+ /*
157
+ * Dark-scheme brand override.
158
+ *
159
+ * The generic dark layer (`:root[data-scheme="dark"]` in semantic.css, specificity
160
+ * 0,2,0 — pseudo-class + attribute) inverts neutral-derived roles to the warm
161
+ * neutral-950 ramp. This block overrides only the brand values that require a
162
+ * cool-steel dark base:
163
+ * - Surfaces / borders / text / overlay → cool steel (not warm neutral)
164
+ * - Accent → lighter blue (link contrast: 7.03:1 on steel-900 [AAA])
165
+ * - text-on-accent → dark text on the lighter blue fill (7.03:1 [AAA])
166
+ * - Subtle washes re-derived against the cool base via color-mix
167
+ *
168
+ * Selector specificity: (0,2,0) — ties the generic dark layer (also 0,2,0) when
169
+ * both `data-theme="ubp"` and `data-scheme="dark"` are present; wins by source
170
+ * order because ubp.css loads after semantic.css. The `auto` scheme is resolved
171
+ * before paint; "dark" here always matches an explicit decision.
172
+ *
173
+ * Subtitle regression control:
174
+ * text (#f8fafc) on surface (#0b182a): 17.03:1 [AAA]
175
+ * text-secondary (#91a4b7) on surface (#0b182a): 6.96:1 [AA — subtitle safe]
176
+ * text-muted (#60758a) on surface (#0b182a): 3.74:1 — correctly limited to
177
+ * non-essential UI labels, NOT body text or subtitles.
178
+ */
179
+ [data-theme="ubp"][data-scheme="dark"] {
180
+ /* Surfaces: cool steel (deeper than the warm neutral-950 generic layer)
181
+ 10° OKLCH teal-shift from Tailwind slate — see file header for before/after. */
182
+ --color-surface: #0b182a; /* steel-900 */
183
+ --color-surface-secondary: #1a2a3a; /* steel-800 */
184
+ --color-surface-tertiary: #2f4254; /* steel-700 */
185
+ --color-surface-raised: #1a2a3a; /* steel-800 */
186
+ --color-overlay: rgba(0, 0, 0, 0.7);
187
+
188
+ /* Borders */
189
+ --color-border: #2f4254; /* steel-700 */
190
+ --color-border-strong: #435668; /* steel-600 */
191
+
192
+ /* Text */
193
+ --color-text: #f8fafc; /* steel-50: 17.03:1 on surface [AAA] */
194
+ --color-text-secondary: #91a4b7; /* steel-400: 6.96:1 on surface [AA] */
195
+ --color-text-muted: #60758a; /* steel-500: 3.74:1 — non-essential only */
196
+
197
+ /* Accent: blue-400 lifts to 7.03:1 on steel-900, clearing AAA for link text.
198
+ Dual-use: as a fill, dark text (#0b182a) on blue-400 gives 7.03:1 [AAA]. */
199
+ --color-accent: #60a5fa; /* blue-400 */
200
+ --color-accent-hover: #93c5fd; /* blue-300 */
201
+ --color-text-on-accent: #0b182a; /* steel-900 on blue-400: 7.03:1 [AAA] */
202
+
203
+ /* Accent subtle: re-derived for the cool-steel base */
204
+ --color-accent-subtle: color-mix(in srgb, #60a5fa 16%, #0b182a);
205
+
206
+ /* Status subtles: re-derived for cool-steel surface */
207
+ --color-destructive-subtle: color-mix(
208
+ in srgb,
209
+ var(--color-destructive) 14%,
210
+ #0b182a
211
+ );
212
+ --color-success-subtle: color-mix(in srgb, var(--color-success) 14%, #0b182a);
213
+ --color-warning-subtle: color-mix(in srgb, var(--color-warning) 14%, #0b182a);
214
+ --color-info-subtle: color-mix(in srgb, var(--color-info) 14%, #0b182a);
215
+
216
+ /* Elevation: more dramatic on dark */
217
+ --elevation-raised:
218
+ 0 1px 4px rgba(0, 0, 0, 0.4), 0 1px 2px rgba(0, 0, 0, 0.3);
219
+ --elevation-overlay:
220
+ 0 8px 24px rgba(0, 0, 0, 0.5), 0 3px 8px rgba(0, 0, 0, 0.3);
221
+
222
+ /* Focus ring: accent blue, visible on all dark surfaces */
223
+ --focus-ring-color: var(--color-accent);
224
+ }
@@ -1056,6 +1056,15 @@
1056
1056
  letter-spacing: var(--type-caption-tracking);
1057
1057
  }
1058
1058
 
1059
+ .type-overline {
1060
+ font-family: var(--type-overline-font);
1061
+ font-size: var(--type-overline-size);
1062
+ font-weight: var(--type-overline-weight);
1063
+ line-height: var(--type-overline-leading);
1064
+ letter-spacing: var(--type-overline-tracking);
1065
+ text-transform: uppercase;
1066
+ }
1067
+
1059
1068
  /* ═══════════════════════════════════════════════
1060
1069
  TYPOGRAPHY UTILITIES
1061
1070
  ═══════════════════════════════════════════════ */