@robr0/design-system 0.9.0 → 0.10.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.
package/README.md CHANGED
@@ -84,10 +84,15 @@ import { BarChart, LineChart } from '@robr0/design-system/charts';
84
84
  ```css
85
85
  :root { --font-family-primary: 'Inter', sans-serif; }
86
86
  ```
87
- - **Colours, radius, spacing**: every semantic token chains to a primitive, so overriding a primitive re-themes everything built on it:
87
+ - **Colours, radius, spacing**: every semantic token chains to a primitive, so overriding a primitive re-themes everything built on it. The action colour is theme-split by design (light fills run teal-08/09/10, dark inverts to teal-05/04/03), so rebranding it means re-keying those steps:
88
88
  ```css
89
89
  :root {
90
- --primitive-teal-07: #7C3AED; /* your brand color becomes the action color */
90
+ --primitive-teal-08: #6D31D3; /* light fill */
91
+ --primitive-teal-09: #4C2293; /* light hover */
92
+ --primitive-teal-10: #2E1560; /* light active, dark label */
93
+ --primitive-teal-05: #A78BFA; /* dark fill */
94
+ --primitive-teal-04: #C4B5FD; /* dark hover */
95
+ --primitive-teal-03: #DDD6FE; /* dark active */
91
96
  --primitive-radius-full: 12px; /* pill buttons become rounded rectangles */
92
97
  }
93
98
  ```
@@ -99,9 +104,9 @@ Icons use a bundled Material Symbols Rounded variable font (woff2), with no extr
99
104
  Tokens flow in one direction, and primitives are never referenced directly in components:
100
105
 
101
106
  ```
102
- tokens-primitives.css --primitive-teal-07: #118AB2
107
+ tokens-primitives.css --primitive-teal-08: #0E6E8F
103
108
 
104
- tokens-light/dark.css --color-action-primary-bg: var(--primitive-teal-07)
109
+ tokens-light/dark.css --color-action-primary-bg: var(--primitive-teal-08)
105
110
 
106
111
  Component CSS background-color: var(--color-action-primary-bg)
107
112
  ```
@@ -166,12 +166,17 @@
166
166
  cursor: pointer;
167
167
  overflow: hidden;
168
168
  text-align: left;
169
- transition: background-color var(--motion-duration-fast) var(--motion-ease-standard), padding var(--motion-duration-slow) var(--motion-ease-emphasized);
169
+ /* The row renders as an <a> when the item carries an href. */
170
+ text-decoration: none;
171
+ transition: background-color var(--motion-duration-fast) var(--motion-ease-standard), padding var(--motion-duration-slow) var(--motion-ease-emphasized), margin var(--motion-duration-slow) var(--motion-ease-emphasized);
170
172
  }
171
173
 
174
+ /* Inset via margin + reduced padding (12 + 8 = the 20px the label sat at
175
+ before), so the pill floats off the rail edges while the icon and label
176
+ keep their exact x-positions against the category labels above. */
172
177
  .ds-app-sidebar--expanded .ds-app-sidebar__btn {
173
- width: 100%;
174
- padding: var(--padding-sm) var(--padding-lg);
178
+ margin: 0 var(--padding-sm-md);
179
+ padding: var(--padding-sm);
175
180
  justify-content: space-between;
176
181
  }
177
182
 
@@ -282,6 +287,9 @@
282
287
  align-items: center;
283
288
  gap: 8px;
284
289
  height: 40px;
290
+ /* The same edge inset the parent pills get; sub-items only render while
291
+ the rail is expanded, so this needs no state selector. */
292
+ margin-right: var(--padding-sm-md);
285
293
  padding: var(--padding-sm);
286
294
  border-radius: var(--radius-full);
287
295
  background: transparent;
@@ -292,6 +300,8 @@
292
300
  text-align: left;
293
301
  overflow: hidden;
294
302
  white-space: nowrap;
303
+ /* The row renders as an <a> when the sub-item carries an href. */
304
+ text-decoration: none;
295
305
  transition: background-color var(--motion-duration-fast) var(--motion-ease-standard);
296
306
  }
297
307
 
@@ -4,8 +4,10 @@ export interface AppSidebarSubItem {
4
4
  key: string;
5
5
  /** Display label */
6
6
  label: string;
7
- /** Click handler */
7
+ /** Click handler — also fires on a link sub-item, so a consumer can route client-side */
8
8
  onClick?: () => void;
9
+ /** Renders the sub-item as a real link to this URL instead of a button */
10
+ href?: string;
9
11
  }
10
12
  export interface AppSidebarItem {
11
13
  /** Unique key for this item */
@@ -14,8 +16,10 @@ export interface AppSidebarItem {
14
16
  icon: string;
15
17
  /** Display label (shown when expanded) */
16
18
  label: string;
17
- /** Click handler */
19
+ /** Click handler — also fires on a link item, so a consumer can route client-side */
18
20
  onClick?: () => void;
21
+ /** Renders the item as a real link to this URL instead of a button. Ignored when the item has children, whose row is the accordion toggle */
22
+ href?: string;
19
23
  /** Sub-items — turns this into an accordion */
20
24
  children?: AppSidebarSubItem[];
21
25
  }
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
3
3
  import React, { useState, useCallback } from "react";
4
4
  import "./AppSidebar.css";
5
5
  import "../../fonts/material-symbols.css";
@@ -187,14 +187,43 @@ const AppSidebar = ({
187
187
  const hasChildren = item.children && item.children.length > 0;
188
188
  const isOpen = openKeys.has(item.key);
189
189
  const isActive = activeKey === item.key;
190
- return /* @__PURE__ */ jsxs(React.Fragment, { children: [
191
- /* @__PURE__ */ jsxs(
192
- "button",
190
+ const isLink = Boolean(item.href) && !hasChildren;
191
+ const itemClasses = [
192
+ `${baseClass}__btn`,
193
+ isActive ? `${baseClass}__btn--active` : ""
194
+ ].filter(Boolean).join(" ");
195
+ const itemContent = /* @__PURE__ */ jsxs(Fragment, { children: [
196
+ /* @__PURE__ */ jsxs("span", { className: `${baseClass}__btn-left`, children: [
197
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__btn-icon material-symbols-rounded`, children: item.icon }),
198
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__btn-label`, children: item.label })
199
+ ] }),
200
+ hasChildren && /* @__PURE__ */ jsx(
201
+ "span",
193
202
  {
194
203
  className: [
195
- `${baseClass}__btn`,
196
- isActive ? `${baseClass}__btn--active` : ""
204
+ `${baseClass}__btn-chevron material-symbols-rounded`,
205
+ isOpen ? `${baseClass}__btn-chevron--open` : ""
197
206
  ].filter(Boolean).join(" "),
207
+ children: "chevron_right"
208
+ }
209
+ )
210
+ ] });
211
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
212
+ isLink ? /* @__PURE__ */ jsx(
213
+ "a",
214
+ {
215
+ className: itemClasses,
216
+ href: item.href,
217
+ onClick: () => item.onClick?.(),
218
+ "aria-current": isActive ? "page" : void 0,
219
+ title: !isExpanded ? item.label : void 0,
220
+ children: itemContent
221
+ }
222
+ ) : /* @__PURE__ */ jsx(
223
+ "button",
224
+ {
225
+ type: "button",
226
+ className: itemClasses,
198
227
  onClick: () => {
199
228
  if (hasChildren && isExpanded) {
200
229
  toggleAccordion(item.key);
@@ -202,23 +231,9 @@ const AppSidebar = ({
202
231
  item.onClick?.();
203
232
  },
204
233
  "aria-expanded": hasChildren ? isOpen : void 0,
234
+ "aria-current": isActive ? "page" : void 0,
205
235
  title: !isExpanded ? item.label : void 0,
206
- children: [
207
- /* @__PURE__ */ jsxs("span", { className: `${baseClass}__btn-left`, children: [
208
- /* @__PURE__ */ jsx("span", { className: `${baseClass}__btn-icon material-symbols-rounded`, children: item.icon }),
209
- /* @__PURE__ */ jsx("span", { className: `${baseClass}__btn-label`, children: item.label })
210
- ] }),
211
- hasChildren && /* @__PURE__ */ jsx(
212
- "span",
213
- {
214
- className: [
215
- `${baseClass}__btn-chevron material-symbols-rounded`,
216
- isOpen ? `${baseClass}__btn-chevron--open` : ""
217
- ].filter(Boolean).join(" "),
218
- children: "chevron_right"
219
- }
220
- )
221
- ]
236
+ children: itemContent
222
237
  }
223
238
  ),
224
239
  hasChildren && /* @__PURE__ */ jsx(
@@ -228,20 +243,36 @@ const AppSidebar = ({
228
243
  `${baseClass}__sub-items`,
229
244
  isOpen && isExpanded ? `${baseClass}__sub-items--open` : ""
230
245
  ].filter(Boolean).join(" "),
231
- children: item.children.map((sub) => /* @__PURE__ */ jsxs("div", { className: `${baseClass}__sub-item`, children: [
232
- /* @__PURE__ */ jsx("div", { className: `${baseClass}__sub-line` }),
233
- /* @__PURE__ */ jsx(
234
- "button",
235
- {
236
- className: [
237
- `${baseClass}__sub-btn`,
238
- activeSubKey === sub.key ? `${baseClass}__sub-btn--active` : ""
239
- ].filter(Boolean).join(" "),
240
- onClick: sub.onClick,
241
- children: /* @__PURE__ */ jsx("span", { className: `${baseClass}__sub-label`, children: sub.label })
242
- }
243
- )
244
- ] }, sub.key))
246
+ children: item.children.map((sub) => {
247
+ const subActive = activeSubKey === sub.key;
248
+ const subClasses = [
249
+ `${baseClass}__sub-btn`,
250
+ subActive ? `${baseClass}__sub-btn--active` : ""
251
+ ].filter(Boolean).join(" ");
252
+ const subLabel = /* @__PURE__ */ jsx("span", { className: `${baseClass}__sub-label`, children: sub.label });
253
+ return /* @__PURE__ */ jsxs("div", { className: `${baseClass}__sub-item`, children: [
254
+ /* @__PURE__ */ jsx("div", { className: `${baseClass}__sub-line` }),
255
+ sub.href ? /* @__PURE__ */ jsx(
256
+ "a",
257
+ {
258
+ className: subClasses,
259
+ href: sub.href,
260
+ onClick: sub.onClick,
261
+ "aria-current": subActive ? "page" : void 0,
262
+ children: subLabel
263
+ }
264
+ ) : /* @__PURE__ */ jsx(
265
+ "button",
266
+ {
267
+ type: "button",
268
+ className: subClasses,
269
+ onClick: sub.onClick,
270
+ "aria-current": subActive ? "page" : void 0,
271
+ children: subLabel
272
+ }
273
+ )
274
+ ] }, sub.key);
275
+ })
245
276
  }
246
277
  )
247
278
  ] }, item.key);
@@ -32,7 +32,7 @@ const AreaChart = ({
32
32
  const classes = [baseClass, className].filter(Boolean).join(" ");
33
33
  const textSecondary = getCSSVar("--color-text-secondary", "#A2A2A2");
34
34
  const gridColor = getCSSVar("--color-divider", "#232323");
35
- const defaultColor = getCSSVar("--color-action-primary-bg", "#118AB2");
35
+ const defaultColor = getCSSVar("--color-action-primary-bg", "#0E6E8F");
36
36
  const renderTooltip = useCallback(
37
37
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
38
38
  (props) => /* @__PURE__ */ jsx(AreaTooltip, { ...props }),
@@ -37,7 +37,7 @@ const BarChart = ({
37
37
  }) => {
38
38
  const baseClass = "ds-chart";
39
39
  const classes = [baseClass, className].filter(Boolean).join(" ");
40
- const resolvedBarColor = barColor || getCSSVar("--color-action-primary-bg", "#118AB2");
40
+ const resolvedBarColor = barColor || getCSSVar("--color-action-primary-bg", "#0E6E8F");
41
41
  const textSecondary = getCSSVar("--color-text-secondary", "#A2A2A2");
42
42
  const gridColor = getCSSVar("--color-divider", "#232323");
43
43
  const cursorColor = getCSSVar("--color-bg-container-secondary", "#303030");
@@ -31,7 +31,7 @@ const LineChart = ({
31
31
  const classes = [baseClass, className].filter(Boolean).join(" ");
32
32
  const textSecondary = getCSSVar("--color-text-secondary", "#A2A2A2");
33
33
  const gridColor = getCSSVar("--color-divider", "#232323");
34
- const defaultColor = getCSSVar("--color-action-primary-bg", "#118AB2");
34
+ const defaultColor = getCSSVar("--color-action-primary-bg", "#0E6E8F");
35
35
  const renderTooltip = useCallback(
36
36
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
37
37
  (props) => /* @__PURE__ */ jsx(LineChartTooltip, { ...props }),
@@ -4,11 +4,11 @@ import { ResponsiveContainer, PieChart as PieChart$1, Pie, Cell, Tooltip, Legend
4
4
  import "./Chart.css";
5
5
  function getDefaultColors() {
6
6
  if (typeof window === "undefined") {
7
- return ["#118AB2", "#06D6A0", "#FFD166", "#EF476F", "#9E47EF", "#EF8247"];
7
+ return ["#0E6E8F", "#06D6A0", "#FFD166", "#EF476F", "#9E47EF", "#EF8247"];
8
8
  }
9
9
  const get = (v, fb) => getComputedStyle(document.documentElement).getPropertyValue(v).trim() || fb;
10
10
  return [
11
- get("--color-action-primary-bg", "#118AB2"),
11
+ get("--color-action-primary-bg", "#0E6E8F"),
12
12
  get("--color-core-accent-mint", "#06D6A0"),
13
13
  get("--color-core-accent-gold", "#FFD166"),
14
14
  get("--color-core-accent-coral", "#EF476F"),
@@ -31,7 +31,7 @@ const RadarChart = ({
31
31
  const classes = [baseClass, className].filter(Boolean).join(" ");
32
32
  const textSecondary = getCSSVar("--color-text-secondary", "#A2A2A2");
33
33
  const gridColor = getCSSVar("--color-divider", "#232323");
34
- const defaultColor = getCSSVar("--color-action-primary-bg", "#118AB2");
34
+ const defaultColor = getCSSVar("--color-action-primary-bg", "#0E6E8F");
35
35
  const renderTooltip = useCallback(
36
36
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
37
37
  (props) => /* @__PURE__ */ jsx(RadarTooltip, { ...props }),
@@ -4,11 +4,11 @@ import { ResponsiveContainer, RadialBarChart, PolarAngleAxis, RadialBar, Tooltip
4
4
  import "./Chart.css";
5
5
  function getDefaultColors() {
6
6
  if (typeof window === "undefined") {
7
- return ["#118AB2", "#06D6A0", "#FFD166", "#EF476F", "#9E47EF", "#EF8247"];
7
+ return ["#0E6E8F", "#06D6A0", "#FFD166", "#EF476F", "#9E47EF", "#EF8247"];
8
8
  }
9
9
  const get = (v, fb) => getComputedStyle(document.documentElement).getPropertyValue(v).trim() || fb;
10
10
  return [
11
- get("--color-action-primary-bg", "#118AB2"),
11
+ get("--color-action-primary-bg", "#0E6E8F"),
12
12
  get("--color-core-accent-mint", "#06D6A0"),
13
13
  get("--color-core-accent-gold", "#FFD166"),
14
14
  get("--color-core-accent-coral", "#EF476F"),
@@ -8,11 +8,11 @@ function getCSSVar(name, fallback) {
8
8
  }
9
9
  function getDefaultColors() {
10
10
  if (typeof window === "undefined") {
11
- return ["#118AB2", "#06D6A0", "#FFD166", "#EF476F", "#9E47EF", "#EF8247"];
11
+ return ["#0E6E8F", "#06D6A0", "#FFD166", "#EF476F", "#9E47EF", "#EF8247"];
12
12
  }
13
13
  const get = (v, fb) => getComputedStyle(document.documentElement).getPropertyValue(v).trim() || fb;
14
14
  return [
15
- get("--color-action-primary-bg", "#118AB2"),
15
+ get("--color-action-primary-bg", "#0E6E8F"),
16
16
  get("--color-core-accent-mint", "#06D6A0"),
17
17
  get("--color-core-accent-gold", "#FFD166"),
18
18
  get("--color-core-accent-coral", "#EF476F"),
@@ -11,6 +11,14 @@
11
11
  padding-bottom: var(--padding-xl);
12
12
  }
13
13
 
14
+ /* Divider-less variant: for headings above content that draws its own
15
+ lines, where a second rule would double up. The generous clearance
16
+ the divider needed drops to a small breathing gap. */
17
+ .ds-section-title--no-divider {
18
+ border-bottom: none;
19
+ padding-bottom: var(--padding-md);
20
+ }
21
+
14
22
  /* ============================================
15
23
  HEADING
16
24
  ============================================ */
@@ -4,7 +4,9 @@ export interface SectionTitleProps {
4
4
  title: string;
5
5
  /** Optional trailing content (count, badge, metadata) */
6
6
  trailing?: React.ReactNode;
7
+ /** Whether to draw the bottom divider line. Set false above content that draws its own lines (bordered tables, calendars), so the section separates by whitespace alone. */
8
+ divider?: boolean;
7
9
  /** Additional CSS classes */
8
10
  className?: string;
9
11
  }
10
- export declare function SectionTitle({ title, trailing, className, }: SectionTitleProps): React.JSX.Element;
12
+ export declare function SectionTitle({ title, trailing, divider, className, }: SectionTitleProps): React.JSX.Element;
@@ -4,9 +4,14 @@ import "./SectionTitle.css";
4
4
  function SectionTitle({
5
5
  title,
6
6
  trailing,
7
+ divider = true,
7
8
  className
8
9
  }) {
9
- const classes = ["ds-section-title", className].filter(Boolean).join(" ");
10
+ const classes = [
11
+ "ds-section-title",
12
+ !divider && "ds-section-title--no-divider",
13
+ className
14
+ ].filter(Boolean).join(" ");
10
15
  return /* @__PURE__ */ jsxs("div", { className: classes, children: [
11
16
  /* @__PURE__ */ jsx("h2", { className: "ds-section-title__heading", children: title }),
12
17
  trailing !== void 0 && trailing !== null && /* @__PURE__ */ jsx("span", { className: "ds-section-title__trailing", children: trailing })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robr0/design-system",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "An AI-ready React design system: accessible components on composable tokens, light/dark theming, and CSS-variable overrides.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -48,21 +48,25 @@
48
48
  --shadow-floating: 0 4px 16px rgba(0, 0, 0, 0.55);
49
49
  --shadow-modal: 0 8px 32px rgba(0, 0, 0, 0.6);
50
50
 
51
- /* Action - Primary */
52
- --color-action-primary-bg: var(--primitive-teal-07);
53
- --color-action-primary-bg-hover: var(--primitive-teal-08);
54
- --color-action-primary-bg-active: var(--primitive-teal-09);
55
- --color-action-primary-text: var(--primitive-teal-02);
56
- --color-action-primary-text-active: var(--primitive-neutral-01);
51
+ /* Action - Primary. Inverted from light: the fill is the light half of
52
+ the teal ramp under a dark label, because a dark fill cannot clear 3:1
53
+ against the near-black page. Hover and active brighten rather than
54
+ deepen, so the fill walks toward the page's opposite, not into it. */
55
+ --color-action-primary-bg: var(--primitive-teal-05);
56
+ --color-action-primary-bg-hover: var(--primitive-teal-04);
57
+ --color-action-primary-bg-active: var(--primitive-teal-03);
58
+ --color-action-primary-text: var(--primitive-teal-10);
59
+ --color-action-primary-text-active: var(--primitive-teal-10);
57
60
  --color-action-primary-text-secondary: var(--primitive-teal-10);
58
- --color-action-primary-text-tertiary: var(--primitive-teal-07);
59
- --color-action-primary-border: var(--primitive-teal-09);
61
+ --color-action-primary-text-tertiary: var(--primitive-teal-04);
62
+ --color-action-primary-border: var(--primitive-teal-05);
60
63
  --color-action-primary-border-secondary: var(--primitive-teal-06);
61
64
  --color-action-primary-border-tertiary: var(--primitive-teal-04);
62
65
 
63
- /* Action - Icon */
66
+ /* Action - Icon. The active icon sits on the hover/active fills, which
67
+ are the light half of the ramp here — so it inverts with the label. */
64
68
  --color-action-icon-default: var(--primitive-neutral-01);
65
- --color-action-icon-active: var(--primitive-neutral-01);
69
+ --color-action-icon-active: var(--primitive-teal-10);
66
70
 
67
71
  /* Action - Passive (Secondary button) */
68
72
  --color-action-passive-text: var(--primitive-neutral-01);
@@ -99,7 +103,7 @@
99
103
  stays luminous on dark surfaces. */
100
104
  --color-ai-gradient-start: var(--primitive-red-05);
101
105
  --color-ai-gradient-mid: var(--primitive-blue-05);
102
- --color-ai-gradient-end: var(--primitive-teal-05);
106
+ --color-ai-gradient-end: var(--primitive-teal-07);
103
107
 
104
108
  /* Chart - Contribution heatmap ramp (level 0 = none → level 4 = most) */
105
109
  --color-chart-contribution-0: var(--primitive-neutral-08);
@@ -114,14 +118,14 @@
114
118
  --color-input-text-disabled: var(--primitive-neutral-07);
115
119
  --color-input-text-inverse: var(--primitive-neutral-07);
116
120
  --color-input-border-primary: var(--primitive-neutral-08);
117
- --color-input-border-hover: var(--primitive-teal-09);
121
+ --color-input-border-hover: var(--primitive-teal-07);
118
122
  --color-input-border-selected: var(--primitive-teal-06);
119
123
  --color-input-border-disabled: var(--primitive-neutral-08);
120
124
  --color-input-bg-primary: var(--primitive-neutral-10);
121
125
  --color-input-bg-disabled: var(--primitive-neutral-09);
122
126
 
123
127
  /* Core colours */
124
- --color-core-ui-primary: var(--primitive-teal-07);
128
+ --color-core-ui-primary: var(--primitive-teal-05);
125
129
  --color-core-ui-secondary: var(--primitive-teal-09);
126
130
  --color-core-accent-coral: var(--primitive-red-07);
127
131
  --color-core-accent-violet: var(--primitive-purple-07);
@@ -51,17 +51,20 @@
51
51
  --shadow-floating: 0 4px 16px rgba(0, 0, 0, 0.12);
52
52
  --shadow-modal: 0 8px 32px rgba(0, 0, 0, 0.2);
53
53
 
54
- /* Action - Primary */
55
- --color-action-primary-bg: var(--primitive-teal-07);
56
- --color-action-primary-bg-hover: var(--primitive-teal-08);
57
- --color-action-primary-bg-active: var(--primitive-teal-09);
54
+ /* Action - Primary. The action colour is theme-dependent: one teal step
55
+ cannot clear 3:1 against both a white page and a near-black one while
56
+ also carrying a 4.5:1 label, so light takes a deep fill under a light
57
+ label and the dark theme inverts it (see tokens-dark.css). */
58
+ --color-action-primary-bg: var(--primitive-teal-08);
59
+ --color-action-primary-bg-hover: var(--primitive-teal-09);
60
+ --color-action-primary-bg-active: var(--primitive-teal-10);
58
61
  --color-action-primary-text: var(--primitive-teal-02);
59
62
  --color-action-primary-text-active: var(--primitive-neutral-01);
60
63
  --color-action-primary-text-secondary: var(--primitive-teal-10);
61
- --color-action-primary-text-tertiary: var(--primitive-teal-07);
62
- --color-action-primary-border: var(--primitive-teal-09);
63
- --color-action-primary-border-secondary: var(--primitive-teal-06);
64
- --color-action-primary-border-tertiary: var(--primitive-teal-04);
64
+ --color-action-primary-text-tertiary: var(--primitive-teal-09);
65
+ --color-action-primary-border: var(--primitive-teal-10);
66
+ --color-action-primary-border-secondary: var(--primitive-teal-08);
67
+ --color-action-primary-border-tertiary: var(--primitive-teal-08);
65
68
 
66
69
  /* Action - Icon */
67
70
  --color-action-icon-default: var(--primitive-neutral-10);
@@ -74,7 +77,7 @@
74
77
  --color-action-passive-bg-active: var(--primitive-neutral-03-semi);
75
78
 
76
79
  /* Core colours */
77
- --color-core-ui-primary: var(--primitive-teal-07);
80
+ --color-core-ui-primary: var(--primitive-teal-08);
78
81
  --color-core-ui-secondary: var(--primitive-teal-10);
79
82
  --color-core-accent-coral: var(--primitive-red-07);
80
83
  --color-core-accent-violet: var(--primitive-purple-07);
@@ -114,7 +117,7 @@
114
117
  (AiButton's ring and glow), never as fills for ordinary chrome. */
115
118
  --color-ai-gradient-start: var(--primitive-red-06);
116
119
  --color-ai-gradient-mid: var(--primitive-blue-06);
117
- --color-ai-gradient-end: var(--primitive-teal-06);
120
+ --color-ai-gradient-end: var(--primitive-teal-08);
118
121
 
119
122
  /* Chart - Contribution heatmap ramp (level 0 = none → level 4 = most) */
120
123
  --color-chart-contribution-0: var(--color-bg-container-primary);
@@ -129,8 +132,8 @@
129
132
  --color-input-text-disabled: var(--primitive-neutral-04);
130
133
  --color-input-text-inverse: var(--primitive-neutral-04);
131
134
  --color-input-border-primary: var(--primitive-neutral-02);
132
- --color-input-border-hover: var(--primitive-teal-04);
133
- --color-input-border-selected: var(--primitive-teal-06);
135
+ --color-input-border-hover: var(--primitive-teal-06);
136
+ --color-input-border-selected: var(--primitive-teal-08);
134
137
  --color-input-border-disabled: var(--primitive-neutral-03);
135
138
  --color-input-bg-primary: var(--primitive-neutral-00);
136
139
  --color-input-bg-disabled: var(--primitive-neutral-02);