@opendata-ai/openchart-core 7.11.0 → 8.0.0-rc.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 (58) hide show
  1. package/dist/index.d.ts +1154 -119
  2. package/dist/index.js +533 -42
  3. package/dist/index.js.map +1 -1
  4. package/dist/styles.css +2 -1
  5. package/dist/styles.css.map +1 -0
  6. package/package.json +9 -3
  7. package/schema/chart.schema.json +8957 -0
  8. package/schema/table.schema.json +861 -0
  9. package/schema/vizspec.schema.json +17694 -0
  10. package/src/accessibility/alt-text.ts +39 -1
  11. package/src/accessibility/aria.ts +4 -2
  12. package/src/colors/__tests__/colorblind.test.ts +12 -0
  13. package/src/colors/colorblind.ts +11 -2
  14. package/src/colors/contrast.ts +11 -0
  15. package/src/colors/index.ts +9 -1
  16. package/src/colors/palettes.ts +47 -0
  17. package/src/helpers/__tests__/deep-merge.test.ts +54 -0
  18. package/src/helpers/__tests__/spec-builders.test.ts +3 -3
  19. package/src/helpers/deep-merge.ts +29 -0
  20. package/src/helpers/spec-builders.ts +7 -7
  21. package/src/index.ts +29 -1
  22. package/src/labels/collision.ts +1 -0
  23. package/src/layout/chrome.ts +24 -0
  24. package/src/layout/index.ts +1 -1
  25. package/src/locale/__tests__/format.test.ts +347 -11
  26. package/src/locale/format.ts +243 -18
  27. package/src/locale/index.ts +10 -1
  28. package/src/responsive/index.ts +2 -0
  29. package/src/responsive/metrics.ts +77 -10
  30. package/src/schema/__tests__/generated-artifacts.test.ts +42 -0
  31. package/src/schema/__tests__/schema.test.ts +179 -0
  32. package/src/styles/__tests__/reduced-motion-selectors.test.ts +28 -0
  33. package/src/styles/animation.css +197 -168
  34. package/src/styles/base.css +89 -62
  35. package/src/styles/chrome.css +173 -179
  36. package/src/styles/dark.css +38 -35
  37. package/src/styles/graph.css +116 -111
  38. package/src/styles/index.css +5 -0
  39. package/src/styles/keyframes.css +115 -100
  40. package/src/styles/legend.css +14 -12
  41. package/src/styles/reduced-motion.css +61 -40
  42. package/src/styles/series-search.css +153 -0
  43. package/src/styles/sparkline.css +14 -12
  44. package/src/styles/table-animation.css +82 -80
  45. package/src/styles/table.css +314 -302
  46. package/src/styles/token-definitions.ts +199 -0
  47. package/src/styles/tokens.css +132 -128
  48. package/src/styles/tooltip.css +109 -115
  49. package/src/styles/you-draw-it.css +126 -0
  50. package/src/types/__tests__/encoding.test.ts +86 -17
  51. package/src/types/__tests__/spec.test.ts +16 -12
  52. package/src/types/encoding.ts +69 -25
  53. package/src/types/events.ts +12 -0
  54. package/src/types/index.ts +39 -3
  55. package/src/types/layout.ts +414 -8
  56. package/src/types/schema-roots.ts +32 -0
  57. package/src/types/spec.ts +643 -84
  58. package/src/types/table.ts +3 -2
@@ -1,180 +1,174 @@
1
- /* ---------------------------------------------------------------------------
2
- * Chrome (eyebrow, title, subtitle, source, byline, footer, brand)
3
- * --------------------------------------------------------------------------- */
4
-
5
- .oc-chrome {
6
- font-family: var(--oc-font-family);
7
- }
8
-
9
- .oc-eyebrow {
10
- font-size: var(--oc-eyebrow-size);
11
- font-weight: var(--oc-eyebrow-weight);
12
- letter-spacing: var(--oc-eyebrow-tracking);
13
- text-transform: uppercase;
14
- fill: var(--oc-accent);
15
- }
16
-
17
- .oc-title {
18
- font-size: var(--oc-title-size);
19
- font-weight: var(--oc-title-weight);
20
- letter-spacing: var(--oc-title-tracking);
21
- fill: var(--oc-text);
22
- }
23
-
24
- .oc-subtitle {
25
- font-size: var(--oc-subtitle-size);
26
- font-weight: var(--oc-subtitle-weight);
27
- fill: var(--oc-text-muted);
28
- }
29
-
30
- .oc-source,
31
- .oc-byline,
32
- .oc-footer {
33
- font-size: var(--oc-source-size);
34
- font-weight: var(--oc-source-weight);
35
- fill: var(--oc-text-muted);
36
- }
37
-
38
- .oc-brand {
39
- font-size: 11px;
40
- font-weight: 510;
41
- letter-spacing: 0.02em;
42
- fill: var(--oc-text-faint);
43
- }
44
-
45
- .oc-brand-dot {
46
- fill: var(--oc-accent);
47
- }
48
-
49
- .oc-eyebrow-dot {
50
- fill: var(--oc-accent);
51
- }
52
-
53
- /* ---------------------------------------------------------------------------
54
- * Metric bar (.oc-metrics) — optional row of label/value cells above chart
55
- * --------------------------------------------------------------------------- */
56
-
57
- .oc-metrics {
58
- font-family: var(--oc-font-family);
59
- }
60
-
61
- .oc-metric-label {
62
- font-size: 10px;
63
- font-weight: 510;
64
- letter-spacing: 0.08em;
65
- text-transform: uppercase;
66
- fill: var(--oc-text-muted);
67
- }
68
-
69
- .oc-metric-value {
70
- font-size: 22px;
71
- font-weight: 510;
72
- letter-spacing: -0.01em;
73
- fill: var(--oc-text);
74
- font-variant-numeric: tabular-nums;
75
- }
76
-
77
- .oc-metric-delta-up {
78
- fill: var(--oc-positive);
79
- font-size: 12px;
80
- font-weight: 510;
81
- }
82
-
83
- .oc-metric-delta-down {
84
- fill: var(--oc-negative);
85
- font-size: 12px;
86
- font-weight: 510;
87
- }
88
-
89
- .oc-metric-secondary {
90
- fill: var(--oc-positive);
91
- font-size: 12px;
92
- font-weight: 400;
93
- }
94
-
95
- /* ---------------------------------------------------------------------------
96
- * Axis tick labels — inline variant sits above each gridline at the
97
- * chart-area left edge (no left gutter). Used by line/area y-axes.
98
- * --------------------------------------------------------------------------- */
99
-
100
- .oc-axis-tick-inline {
101
- font-size: 11px;
102
- font-weight: 400;
103
- fill: var(--oc-text-muted);
104
- }
105
-
106
- /* ---------------------------------------------------------------------------
107
- * Endpoint labels (right-side per-series column for line/area charts)
108
- *
109
- * Swatch idiom: a thicker colored stroke segment with an optional thinner
110
- * lighter strip below. Mirrors the refreshed traditional legend swatch so a
111
- * single chart never shows two swatch idioms (mock-2 alignment). Per-series
112
- * color is stamped inline by the renderer; CSS tokens here cover the muted
113
- * value text and the leader line so theme overrides flow through naturally.
114
- * --------------------------------------------------------------------------- */
115
-
116
- .oc-endpoint-labels {
117
- font-family: var(--oc-font-family);
118
- }
119
-
120
- .oc-endpoint-label {
121
- /* Color set inline per series; this fallback only kicks in if the renderer
122
- ever omits the inline fill. */
123
- fill: var(--oc-endpoint-label-color, var(--oc-text));
124
- }
125
-
126
- .oc-endpoint-value {
127
- fill: var(--oc-endpoint-value-color, var(--oc-text-muted));
128
- }
129
-
130
- .oc-endpoint-leader {
131
- stroke: var(--oc-endpoint-leader-color, currentColor);
132
- }
133
-
134
- .oc-endpoint-marker {
135
- /* Fill/stroke set inline (open-ring style: bg fill, series stroke). */
136
- }
137
-
138
- .oc-endpoint-swatch {
139
- /* Color set inline per series. */
140
- }
141
-
142
- /* ---------------------------------------------------------------------------
143
- * Annotation dot + subtitle (text-annotation extensions)
144
- * --------------------------------------------------------------------------- */
145
-
146
- .oc-annotation-dot {
147
- /* Fill/stroke resolved by the engine. Open-ring default = bg fill, series
148
- stroke. */
149
- }
150
-
151
- .oc-annotation-subtitle {
152
- fill: var(--oc-annotation-subtitle-color, var(--oc-text-muted));
153
- }
154
-
155
- /* ---------------------------------------------------------------------------
156
- * Table footer chrome
157
- * --------------------------------------------------------------------------- */
158
-
159
- /* Footer row: source/footer text on the left, brand watermark on the right,
160
- * sharing a single baseline. gap keeps them apart if the row gets narrow. */
161
- .oc-table-footer-row {
162
- display: flex;
163
- align-items: baseline;
164
- gap: 16px;
165
- padding: 16px 16px 4px;
166
- }
167
-
168
- /* Chrome block sits inside the flex row now, so it no longer owns the
169
- * footer's top padding or horizontal inset (the row provides both). */
170
- .oc-chrome-footer {
171
- padding-top: 0;
172
- }
173
-
174
- /* Brand watermark: pushed to the right edge (margin-left:auto holds it there
175
- * even when the source text is absent), never shrinks below its own text. */
176
- .oc-table-footer-row .oc-table-ref {
177
- margin-left: auto;
178
- flex-shrink: 0;
179
- text-align: right;
1
+ @layer oc.components {
2
+ /* ---------------------------------------------------------------------------
3
+ * Chrome (eyebrow, title, subtitle, source, byline, footer, brand)
4
+ * --------------------------------------------------------------------------- */
5
+
6
+ .oc-chrome {
7
+ font-family: var(--oc-font-family);
8
+ }
9
+
10
+ .oc-eyebrow {
11
+ font-size: var(--oc-eyebrow-size);
12
+ font-weight: var(--oc-eyebrow-weight);
13
+ letter-spacing: var(--oc-eyebrow-tracking);
14
+ text-transform: uppercase;
15
+ fill: var(--oc-accent);
16
+ }
17
+
18
+ .oc-title {
19
+ font-size: var(--oc-title-size);
20
+ font-weight: var(--oc-title-weight);
21
+ letter-spacing: var(--oc-title-tracking);
22
+ fill: var(--oc-text);
23
+ }
24
+
25
+ .oc-subtitle {
26
+ font-size: var(--oc-subtitle-size);
27
+ font-weight: var(--oc-subtitle-weight);
28
+ fill: var(--oc-text-muted);
29
+ }
30
+
31
+ .oc-source,
32
+ .oc-byline,
33
+ .oc-footer {
34
+ font-size: var(--oc-source-size);
35
+ font-weight: var(--oc-source-weight);
36
+ fill: var(--oc-text-muted);
37
+ }
38
+
39
+ .oc-brand {
40
+ font-size: 11px;
41
+ font-weight: 510;
42
+ letter-spacing: 0.02em;
43
+ fill: var(--oc-text-faint);
44
+ }
45
+
46
+ .oc-brand-dot {
47
+ fill: var(--oc-accent);
48
+ }
49
+
50
+ .oc-eyebrow-dot {
51
+ fill: var(--oc-accent);
52
+ }
53
+
54
+ /* ---------------------------------------------------------------------------
55
+ * Metric bar (.oc-metrics) — optional row of label/value cells above chart
56
+ * --------------------------------------------------------------------------- */
57
+
58
+ .oc-metrics {
59
+ font-family: var(--oc-font-family);
60
+ }
61
+
62
+ .oc-metric-label {
63
+ font-size: 10px;
64
+ font-weight: 510;
65
+ letter-spacing: 0.08em;
66
+ text-transform: uppercase;
67
+ fill: var(--oc-text-muted);
68
+ }
69
+
70
+ .oc-metric-value {
71
+ font-size: 22px;
72
+ font-weight: 510;
73
+ letter-spacing: -0.01em;
74
+ fill: var(--oc-text);
75
+ font-variant-numeric: tabular-nums;
76
+ }
77
+
78
+ .oc-metric-delta-up {
79
+ fill: var(--oc-positive);
80
+ font-size: 12px;
81
+ font-weight: 510;
82
+ }
83
+
84
+ .oc-metric-delta-down {
85
+ fill: var(--oc-negative);
86
+ font-size: 12px;
87
+ font-weight: 510;
88
+ }
89
+
90
+ .oc-metric-secondary {
91
+ fill: var(--oc-positive);
92
+ font-size: 12px;
93
+ font-weight: 400;
94
+ }
95
+
96
+ /* ---------------------------------------------------------------------------
97
+ * Axis tick labels — inline variant sits above each gridline at the
98
+ * chart-area left edge (no left gutter). Used by line/area y-axes.
99
+ * --------------------------------------------------------------------------- */
100
+
101
+ .oc-axis-tick-inline {
102
+ font-size: 11px;
103
+ font-weight: 400;
104
+ fill: var(--oc-text-muted);
105
+ }
106
+
107
+ /* ---------------------------------------------------------------------------
108
+ * Endpoint labels (right-side per-series column for line/area charts)
109
+ *
110
+ * Swatch idiom: a thicker colored stroke segment with an optional thinner
111
+ * lighter strip below. Mirrors the refreshed traditional legend swatch so a
112
+ * single chart never shows two swatch idioms (mock-2 alignment). Per-series
113
+ * color is stamped inline by the renderer; CSS tokens here cover the muted
114
+ * value text and the leader line so theme overrides flow through naturally.
115
+ * --------------------------------------------------------------------------- */
116
+
117
+ .oc-endpoint-labels {
118
+ font-family: var(--oc-font-family);
119
+ }
120
+
121
+ .oc-endpoint-label {
122
+ /* Color set inline per series; this fallback only kicks in if the renderer
123
+ ever omits the inline fill. */
124
+ fill: var(--oc-endpoint-label-color, var(--oc-text));
125
+ }
126
+
127
+ .oc-endpoint-value {
128
+ fill: var(--oc-endpoint-value-color, var(--oc-text-muted));
129
+ }
130
+
131
+ .oc-endpoint-leader {
132
+ stroke: var(--oc-endpoint-leader-color, currentColor);
133
+ }
134
+
135
+ /* .oc-endpoint-marker — fill/stroke set inline (open-ring style: bg fill, series stroke). */
136
+ /* .oc-endpoint-swatch — color set inline per series. */
137
+
138
+ /* ---------------------------------------------------------------------------
139
+ * Annotation dot + subtitle (text-annotation extensions)
140
+ * --------------------------------------------------------------------------- */
141
+
142
+ /* .oc-annotation-dot — fill/stroke resolved by the engine (open-ring default = bg fill, series stroke). */
143
+
144
+ .oc-annotation-subtitle {
145
+ fill: var(--oc-annotation-subtitle-color, var(--oc-text-muted));
146
+ }
147
+
148
+ /* ---------------------------------------------------------------------------
149
+ * Table footer chrome
150
+ * --------------------------------------------------------------------------- */
151
+
152
+ /* Footer row: source/footer text on the left, brand watermark on the right,
153
+ * sharing a single baseline. gap keeps them apart if the row gets narrow. */
154
+ .oc-table-footer-row {
155
+ display: flex;
156
+ align-items: baseline;
157
+ gap: 16px;
158
+ padding: 16px 16px 4px;
159
+ }
160
+
161
+ /* Chrome block sits inside the flex row now, so it no longer owns the
162
+ * footer's top padding or horizontal inset (the row provides both). */
163
+ .oc-chrome-footer {
164
+ padding-top: 0;
165
+ }
166
+
167
+ /* Brand watermark: pushed to the right edge (margin-left:auto holds it there
168
+ * even when the source text is absent), never shrinks below its own text. */
169
+ .oc-table-footer-row .oc-table-ref {
170
+ margin-left: auto;
171
+ flex-shrink: 0;
172
+ text-align: right;
173
+ }
180
174
  }
@@ -1,41 +1,44 @@
1
- /* ---------------------------------------------------------------------------
2
- * Dark mode overrides (fallback defaults)
3
- *
4
- * At runtime, the JS theme engine stamps dark-adapted --oc-* custom
5
- * properties on each .oc-root container. These CSS values serve as
6
- * fallbacks for contexts where JS hasn't mounted yet.
7
- * --------------------------------------------------------------------------- */
1
+ /* GENERATED FILE — do not edit. Source: packages/core/src/styles/token-definitions.ts. Regenerate: bun run generate:tokens */
8
2
 
9
- .oc-dark {
10
- /* Surfaces (zinc-based achromatic ramp) */
11
- --oc-bg: #09090b;
12
- --oc-card: #111113;
13
- --oc-secondary: #27272a;
3
+ @layer oc.tokens {
4
+ /* ---------------------------------------------------------------------------
5
+ * Dark mode overrides (fallback defaults)
6
+ *
7
+ * At runtime, the JS theme engine stamps dark-adapted --oc-* custom
8
+ * properties on each .oc-root container. These CSS values serve as
9
+ * fallbacks for contexts where JS hasn't mounted yet.
10
+ * --------------------------------------------------------------------------- */
14
11
 
15
- /* Text levels — see tokens.css for the cross-mode naming rationale */
16
- --oc-text: #f7f8f8;
17
- --oc-text-secondary: #d0d6e0;
18
- --oc-text-muted: #a1a1aa;
19
- --oc-text-subtle: #71717a;
20
- --oc-text-faint: #52525b;
12
+ .oc-dark {
13
+ /* Surfaces (zinc-based achromatic ramp) */
14
+ --oc-bg: #09090b;
15
+ --oc-card: #111113;
16
+ --oc-secondary: #27272a;
21
17
 
22
- /* Lines */
23
- --oc-gridline: rgba(255, 255, 255, 0.05);
24
- --oc-axis: rgba(255, 255, 255, 0.1);
25
- --oc-border: rgba(255, 255, 255, 0.1);
18
+ /* Text levels — see tokens.css for the cross-mode naming rationale */
19
+ --oc-text: #f7f8f8;
20
+ --oc-text-secondary: #d0d6e0;
21
+ --oc-text-muted: #a1a1aa;
22
+ --oc-text-faint: #52525b;
26
23
 
27
- /* Brand and semantic — accent stays cyan (no darkening on dark bg) */
28
- --oc-accent: #06b6d4;
29
- --oc-accent-strong: #06b6d4;
30
- --oc-positive: #34d399;
31
- --oc-negative: #fb7185;
32
- --oc-focus: #60a5fa;
24
+ /* Lines */
25
+ --oc-gridline: rgba(255, 255, 255, 0.05);
26
+ --oc-axis: rgba(255, 255, 255, 0.1);
27
+ --oc-border: rgba(255, 255, 255, 0.1);
33
28
 
34
- /* Interactive states */
35
- --oc-hover-bg: rgba(255, 255, 255, 0.05);
36
- --oc-tooltip-bg: rgba(17, 17, 19, 0.92);
37
- --oc-tooltip-border: rgba(255, 255, 255, 0.08);
38
- --oc-tooltip-shadow: 0 2px 8px rgba(0, 0, 0, 0.3), 0 0 1px rgba(0, 0, 0, 0.4);
39
- --oc-tooltip-text: #f7f8f8;
40
- --oc-legend-text: #d0d6e0;
29
+ /* Brand and semantic — accent stays cyan (no darkening on dark bg) */
30
+ --oc-accent: #06b6d4;
31
+ --oc-accent-strong: #06b6d4;
32
+ --oc-positive: #34d399;
33
+ --oc-negative: #fb7185;
34
+ --oc-focus: #60a5fa;
35
+
36
+ /* Interactive states */
37
+ --oc-hover-bg: rgba(255, 255, 255, 0.05);
38
+ --oc-tooltip-bg: rgba(17, 17, 19, 0.92);
39
+ --oc-tooltip-border: rgba(255, 255, 255, 0.08);
40
+ --oc-tooltip-shadow: 0 2px 8px rgba(0, 0, 0, 0.3), 0 0 1px rgba(0, 0, 0, 0.4);
41
+ --oc-tooltip-text: #f7f8f8;
42
+ --oc-legend-text: #d0d6e0;
43
+ }
41
44
  }