@lerianstudio/sindarian-ui 1.4.0-beta.6 → 1.4.0-beta.8

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.
@@ -88,7 +88,7 @@ function DropdownMenuRadioItem({ className, children, ...props }) {
88
88
  return ((0, jsx_runtime_1.jsxs)(DropdownMenuPrimitive.RadioItem, { "data-slot": "dropdown-menu-radio-item", className: (0, utils_1.cn)('focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden transition-colors select-none data-disabled:pointer-events-none data-disabled:opacity-50', className), ...props, children: [(0, jsx_runtime_1.jsx)("span", { className: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center", children: (0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.ItemIndicator, { children: (0, jsx_runtime_1.jsx)(lucide_react_1.Circle, { className: "size-2 fill-current" }) }) }), children] }));
89
89
  }
90
90
  function DropdownMenuLabel({ className, inset, ...props }) {
91
- return ((0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.Label, { "data-slot": "dropdown-menu-label", "data-inset": inset, className: (0, utils_1.cn)('hover:bg-shadcn-100 text-muted-foreground flex items-center gap-3 px-3 py-2 text-sm font-bold transition-colors', className), ...props }));
91
+ return ((0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.Label, { "data-slot": "dropdown-menu-label", "data-inset": inset, className: (0, utils_1.cn)('hover:bg-shadcn-100 hover:text-accent-foreground text-muted-foreground flex items-center gap-3 px-3 py-2 text-sm font-bold transition-colors', className), ...props }));
92
92
  }
93
93
  function DropdownMenuSeparator({ className, ...props }) {
94
94
  return ((0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.Separator, { "data-slot": "dropdown-menu-separator", className: (0, utils_1.cn)('bg-muted -mx-1 my-1 h-px', className), ...props }));
@@ -100,7 +100,7 @@ function DropdownMenuSub({ ...props }) {
100
100
  return (0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.Sub, { "data-slot": "dropdown-menu-sub", ...props });
101
101
  }
102
102
  function DropdownMenuSubTrigger({ className, inset, children, ...props }) {
103
- return ((0, jsx_runtime_1.jsxs)(DropdownMenuPrimitive.SubTrigger, { "data-slot": "dropdown-menu-sub-trigger", "data-inset": inset, className: (0, utils_1.cn)('focus:bg-shadcn-100 data-[state=open]:bg-accent text-muted-foreground flex cursor-default items-center gap-3 px-3 py-2 text-sm font-medium outline-hidden select-none', className), ...props, children: [children, (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: "ml-auto h-4 w-4" })] }));
103
+ return ((0, jsx_runtime_1.jsxs)(DropdownMenuPrimitive.SubTrigger, { "data-slot": "dropdown-menu-sub-trigger", "data-inset": inset, className: (0, utils_1.cn)('focus:bg-shadcn-100 focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground text-muted-foreground flex cursor-default items-center gap-3 px-3 py-2 text-sm font-medium outline-hidden select-none', className), ...props, children: [children, (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: "ml-auto h-4 w-4" })] }));
104
104
  }
105
105
  function DropdownMenuSubContent({ className, ...props }) {
106
106
  return ((0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.SubContent, { "data-slot": "dropdown-menu-sub-content", className: (0, utils_1.cn)('bg-popover text-popover-foreground motion-safe:data-[state=open]:animate-in motion-safe:data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-32 overflow-hidden rounded-md border p-1 shadow-lg', className), ...props }));
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/domain/money-text/index.tsx"],"names":[],"mappings":"AAwBA,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAC1C,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,gDAAgD;IAChD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,uCAAuC;IACvC,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,mDAAmD;IACnD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAOtE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,cAAc,EAAE,MAAM,EACtB,MAAM,CAAC,EAAE,MAAM,GACd;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAiDjD;AAED,wBAAgB,SAAS,CAAC,EACxB,MAAM,EACN,QAAQ,EACR,cAAkB,EAClB,YAAoB,EACpB,SAAgB,EAChB,MAAM,EACN,SAAS,EACV,EAAE,cAAc,+BA8BhB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/domain/money-text/index.tsx"],"names":[],"mappings":"AAgCA,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAC1C,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,gDAAgD;IAChD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,uCAAuC;IACvC,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,mDAAmD;IACnD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAOtE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,cAAc,EAAE,MAAM,EACtB,MAAM,CAAC,EAAE,MAAM,GACd;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAiDjD;AAED,wBAAgB,SAAS,CAAC,EACxB,MAAM,EACN,QAAQ,EACR,cAAkB,EAClB,YAAoB,EACpB,SAAgB,EAChB,MAAM,EACN,SAAS,EACV,EAAE,cAAc,+BA8BhB"}
@@ -14,15 +14,23 @@ const jsx_runtime_1 = require("react/jsx-runtime");
14
14
  * preserve that guarantee.
15
15
  *
16
16
  * The currency code is shown as a suffix rather than a symbol so mixed-currency
17
- * ledgers stay unambiguous. Negative amounts get the destructive color;
17
+ * ledgers stay unambiguous. Negative amounts get the error sign color;
18
18
  * null/undefined/empty render the muted no-value placeholder.
19
19
  *
20
- * SIGN COLOR IS NOT THE CREDIT ROLE. `--destructive` marks a negative/alarm
20
+ * SIGN COLOR IS NOT THE CREDIT ROLE. The sign ink marks a negative/alarm
21
21
  * amount; `--credit` is the accounting reading of a credit amount. They are two
22
22
  * roles, and a consumer layers the credit role on top (matcher's fork passes
23
23
  * `signColor={false}` with `tone="credit"` precisely so the two never fight).
24
- * Collapsing the sign color into `text-credit` would erase that distinction
25
- * this class stays `text-destructive`, as in sindarian-x@0.15.0.
24
+ * Collapsing the sign color into `text-credit` would erase that distinction.
25
+ *
26
+ * THE SIGN INK IS THE `system-error` H1A INK, not `--destructive`. A signed
27
+ * amount is TEXT, so it answers to WCAG AA on whatever surface holds it, and
28
+ * `--destructive` is a FILL family (buttons, badges, tinted chips) whose dark
29
+ * value reads 3.80:1 as ink on `--card`, an axe finding on a live console.
30
+ * `--system-error-h1a` is the error INK of the same family and reads 8.28:1
31
+ * light / 7.12:1 dark on that surface. `__tests__/tokens-contract.test.ts`
32
+ * re-measures whichever token this line applies, so the pairing is measured
33
+ * rather than trusted.
26
34
  */
27
35
  const utils_1 = require("../../lib/utils");
28
36
  const format_1 = require("../format");
@@ -111,5 +119,5 @@ function MoneyText({ amount, currency, fractionDigits = 2, hideCurrency = false,
111
119
  if (result === null)
112
120
  return noValue;
113
121
  const { formatted, negative } = result;
114
- return ((0, jsx_runtime_1.jsxs)("span", { className: (0, utils_1.cn)('tabular-nums', signColor && negative && 'text-destructive', className), children: [formatted, !hideCurrency && currency ? ((0, jsx_runtime_1.jsx)("span", { className: "text-muted-foreground ml-1 text-xs", children: currency })) : null] }));
122
+ return ((0, jsx_runtime_1.jsxs)("span", { className: (0, utils_1.cn)('tabular-nums', signColor && negative && 'text-system-error-h1a', className), children: [formatted, !hideCurrency && currency ? ((0, jsx_runtime_1.jsx)("span", { className: "text-muted-foreground ml-1 text-xs", children: currency })) : null] }));
115
123
  }
@@ -37,7 +37,7 @@ function DropdownMenuRadioItem({ className, children, ...props }) {
37
37
  return (_jsxs(DropdownMenuPrimitive.RadioItem, { "data-slot": "dropdown-menu-radio-item", className: cn('focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden transition-colors select-none data-disabled:pointer-events-none data-disabled:opacity-50', className), ...props, children: [_jsx("span", { className: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center", children: _jsx(DropdownMenuPrimitive.ItemIndicator, { children: _jsx(Circle, { className: "size-2 fill-current" }) }) }), children] }));
38
38
  }
39
39
  function DropdownMenuLabel({ className, inset, ...props }) {
40
- return (_jsx(DropdownMenuPrimitive.Label, { "data-slot": "dropdown-menu-label", "data-inset": inset, className: cn('hover:bg-shadcn-100 text-muted-foreground flex items-center gap-3 px-3 py-2 text-sm font-bold transition-colors', className), ...props }));
40
+ return (_jsx(DropdownMenuPrimitive.Label, { "data-slot": "dropdown-menu-label", "data-inset": inset, className: cn('hover:bg-shadcn-100 hover:text-accent-foreground text-muted-foreground flex items-center gap-3 px-3 py-2 text-sm font-bold transition-colors', className), ...props }));
41
41
  }
42
42
  function DropdownMenuSeparator({ className, ...props }) {
43
43
  return (_jsx(DropdownMenuPrimitive.Separator, { "data-slot": "dropdown-menu-separator", className: cn('bg-muted -mx-1 my-1 h-px', className), ...props }));
@@ -49,7 +49,7 @@ function DropdownMenuSub({ ...props }) {
49
49
  return _jsx(DropdownMenuPrimitive.Sub, { "data-slot": "dropdown-menu-sub", ...props });
50
50
  }
51
51
  function DropdownMenuSubTrigger({ className, inset, children, ...props }) {
52
- return (_jsxs(DropdownMenuPrimitive.SubTrigger, { "data-slot": "dropdown-menu-sub-trigger", "data-inset": inset, className: cn('focus:bg-shadcn-100 data-[state=open]:bg-accent text-muted-foreground flex cursor-default items-center gap-3 px-3 py-2 text-sm font-medium outline-hidden select-none', className), ...props, children: [children, _jsx(ChevronRight, { className: "ml-auto h-4 w-4" })] }));
52
+ return (_jsxs(DropdownMenuPrimitive.SubTrigger, { "data-slot": "dropdown-menu-sub-trigger", "data-inset": inset, className: cn('focus:bg-shadcn-100 focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground text-muted-foreground flex cursor-default items-center gap-3 px-3 py-2 text-sm font-medium outline-hidden select-none', className), ...props, children: [children, _jsx(ChevronRight, { className: "ml-auto h-4 w-4" })] }));
53
53
  }
54
54
  function DropdownMenuSubContent({ className, ...props }) {
55
55
  return (_jsx(DropdownMenuPrimitive.SubContent, { "data-slot": "dropdown-menu-sub-content", className: cn('bg-popover text-popover-foreground motion-safe:data-[state=open]:animate-in motion-safe:data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-32 overflow-hidden rounded-md border p-1 shadow-lg', className), ...props }));
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/domain/money-text/index.tsx"],"names":[],"mappings":"AAwBA,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAC1C,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,gDAAgD;IAChD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,uCAAuC;IACvC,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,mDAAmD;IACnD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAOtE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,cAAc,EAAE,MAAM,EACtB,MAAM,CAAC,EAAE,MAAM,GACd;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAiDjD;AAED,wBAAgB,SAAS,CAAC,EACxB,MAAM,EACN,QAAQ,EACR,cAAkB,EAClB,YAAoB,EACpB,SAAgB,EAChB,MAAM,EACN,SAAS,EACV,EAAE,cAAc,+BA8BhB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/domain/money-text/index.tsx"],"names":[],"mappings":"AAgCA,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAC1C,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,gDAAgD;IAChD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,uCAAuC;IACvC,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,mDAAmD;IACnD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAOtE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,cAAc,EAAE,MAAM,EACtB,MAAM,CAAC,EAAE,MAAM,GACd;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAiDjD;AAED,wBAAgB,SAAS,CAAC,EACxB,MAAM,EACN,QAAQ,EACR,cAAkB,EAClB,YAAoB,EACpB,SAAgB,EAChB,MAAM,EACN,SAAS,EACV,EAAE,cAAc,+BA8BhB"}
@@ -9,15 +9,23 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
9
  * preserve that guarantee.
10
10
  *
11
11
  * The currency code is shown as a suffix rather than a symbol so mixed-currency
12
- * ledgers stay unambiguous. Negative amounts get the destructive color;
12
+ * ledgers stay unambiguous. Negative amounts get the error sign color;
13
13
  * null/undefined/empty render the muted no-value placeholder.
14
14
  *
15
- * SIGN COLOR IS NOT THE CREDIT ROLE. `--destructive` marks a negative/alarm
15
+ * SIGN COLOR IS NOT THE CREDIT ROLE. The sign ink marks a negative/alarm
16
16
  * amount; `--credit` is the accounting reading of a credit amount. They are two
17
17
  * roles, and a consumer layers the credit role on top (matcher's fork passes
18
18
  * `signColor={false}` with `tone="credit"` precisely so the two never fight).
19
- * Collapsing the sign color into `text-credit` would erase that distinction
20
- * this class stays `text-destructive`, as in sindarian-x@0.15.0.
19
+ * Collapsing the sign color into `text-credit` would erase that distinction.
20
+ *
21
+ * THE SIGN INK IS THE `system-error` H1A INK, not `--destructive`. A signed
22
+ * amount is TEXT, so it answers to WCAG AA on whatever surface holds it, and
23
+ * `--destructive` is a FILL family (buttons, badges, tinted chips) whose dark
24
+ * value reads 3.80:1 as ink on `--card`, an axe finding on a live console.
25
+ * `--system-error-h1a` is the error INK of the same family and reads 8.28:1
26
+ * light / 7.12:1 dark on that surface. `__tests__/tokens-contract.test.ts`
27
+ * re-measures whichever token this line applies, so the pairing is measured
28
+ * rather than trusted.
21
29
  */
22
30
  import { cn } from '../../lib/utils.js';
23
31
  import { NO_VALUE, isValidDigitCount, safeLocale } from '../format.js';
@@ -106,5 +114,5 @@ export function MoneyText({ amount, currency, fractionDigits = 2, hideCurrency =
106
114
  if (result === null)
107
115
  return noValue;
108
116
  const { formatted, negative } = result;
109
- return (_jsxs("span", { className: cn('tabular-nums', signColor && negative && 'text-destructive', className), children: [formatted, !hideCurrency && currency ? (_jsx("span", { className: "text-muted-foreground ml-1 text-xs", children: currency })) : null] }));
117
+ return (_jsxs("span", { className: cn('tabular-nums', signColor && negative && 'text-system-error-h1a', className), children: [formatted, !hideCurrency && currency ? (_jsx("span", { className: "text-muted-foreground ml-1 text-xs", children: currency })) : null] }));
110
118
  }
package/dist/globals.css CHANGED
@@ -216,10 +216,33 @@
216
216
 
217
217
  /* Application tokens (from Figma ring-tokens) */
218
218
  --body-surface: 240 5% 96%; /* base/100 = #F4F4F5 */
219
- --body-text: 240 4% 46%; /* base/500 = #71717A */
219
+ /*
220
+ --body-text and --container-text below both step one notch off base/500.
221
+ base/500 clears AA on white (4.86:1) but not on --body-surface, the
222
+ recessed page ground, where it read 4.43:1. That ground is exactly where
223
+ the components carrying these two tokens sit: the resting tabs trigger
224
+ (text-sm font-medium, and its hover fill is --card precisely because the
225
+ resting ground is not white), the breadcrumb page and separator, the
226
+ stepper item text and description. An axe crawl of the br-sfn cockpit
227
+ flagged .text-body-text on 2026-09-06; the ratio it printed does not
228
+ reproduce here, and the measured pair on this token's own ground is
229
+ 4.40:1 at 8-bit precision (4.43:1 in float).
230
+
231
+ 45% is the smallest step that clears it: 4.59:1 on --body-surface, up
232
+ from 4.43:1, and 5.04:1 on white. Both stay eleven notches clear of
233
+ --container-title (base/600), so the text-versus-title step holds.
234
+
235
+ Deliberately not stepped far enough to clear --muted (#E4E4E7), which
236
+ would take base/500 minus five notches and visibly darken tabs,
237
+ breadcrumbs and steppers. No component in THIS kit paints either token on
238
+ that fill. A consumer that does is a call-site fix on its side, not a
239
+ clean bill from here: product-console's extract-table pairs bg-muted with
240
+ text-body-text and reads 3.98:1 in light.
241
+ */
242
+ --body-text: 240 4% 45%; /* one notch off base/500: #6E6E77, AA on body-surface */
220
243
  --body-title: 240 5% 26%; /* base/700 = #3F3F46 */
221
244
  --container-surface: 0 0% 100%; /* base/white = #FFFFFF */
222
- --container-text: 240 4% 46%; /* base/500 = #71717A */
245
+ --container-text: 240 4% 45%; /* one notch off base/500: #6E6E77, AA on body-surface */
223
246
  --container-title: 240 5% 34%; /* base/600 = #52525B */
224
247
  --container-border: 0 0% 100%; /* #FFFFFF */
225
248
  --link: 240 4% 16%; /* base/800 = #27272A */
@@ -232,7 +255,14 @@
232
255
  --background: 0 0% 100%; /* base/white = #FFFFFF */
233
256
  --foreground: 240 5% 26%; /* base/700 = #3F3F46 */
234
257
  --card: 0 0% 100%; /* = container-surface (white) */
235
- --card-foreground: 240 4% 46%; /* = container-text (base/500) */
258
+ /* Held at base/500, the step --container-text just moved off: this token's
259
+ own ground is --card (white), where base/500 clears AA at 4.86:1. It
260
+ measures 3.84:1 over --muted, but no component in this kit renders it
261
+ there: the data table pins its own inks rather than inheriting this one
262
+ (`ui/table/index.tsx`: cells take text-foreground, heads take
263
+ text-muted-foreground), so the hovered-row ground never sits under this
264
+ token. */
265
+ --card-foreground: 240 4% 46%; /* base/500 = #71717A */
236
266
  --popover: 0 0% 100%; /* base/white */
237
267
  --popover-foreground: 240 5% 34%; /* base/600 = #52525B */
238
268
  --primary: 240 4% 16%; /* base/800 = #27272A */
@@ -254,10 +284,16 @@
254
284
  --destructive-foreground: 0 0% 100%; /* white */
255
285
  --border: 240 5% 90%; /* base/200 = #E4E4E7 */
256
286
  --input: 0 0% 100%; /* base/white */
257
- /* One step lighter than --muted-foreground, which is also the label colour:
258
- a placeholder that matches its own label has no hierarchy left to give.
259
- Still 4.4:1 on the shadcn-100 field, so it stays legible as a hint. */
260
- --input-placeholder: 240 4% 46%; /* base/500 = #71717A */
287
+ /* Lighter than --muted-foreground, which is also the label colour: a
288
+ placeholder that matches its own label has no hierarchy left to give.
289
+ Stepped off base/500 with --body-text, and for the same reason. The
290
+ field paints no fill of its own (`input/styles.css`: .input-wrapper is
291
+ border-only, .input-base is bg-transparent), so the hint sits on
292
+ whatever ground contains the form, and on --body-surface base/500 read
293
+ 4.40:1. 45% brings that to 4.59:1 and 5.04:1 on white. The retired note
294
+ here claimed 4.4:1 was acceptable "on the shadcn-100 field", which named
295
+ a fill the component never paints. */
296
+ --input-placeholder: 240 4% 45%; /* one notch off base/500: #6E6E77, AA on body-surface */
261
297
  --ring: 240 5% 65%; /* focus ring */
262
298
  --radius: 0.5rem;
263
299
 
@@ -349,7 +385,36 @@
349
385
  --background: 240 4% 16%; /* base/800 = #27272A */
350
386
  --foreground: 240 5% 96%; /* base/100 = #F4F4F5 */
351
387
  --muted: 240 5% 34%; /* base/600 = #52525B */
352
- --muted-foreground: 240 5% 65%; /* base/400 = #A1A1AA */
388
+ /*
389
+ Lifted off the base scale. base/400 read 4.09:1 on --container-surface
390
+ (which --card aliases) and 3.01:1 on --muted, both under the 4.5 AA floor
391
+ for normal text, and this is the kit's universal secondary ink: every
392
+ TableHead column label (text-sm font-medium inside a text-sm Table),
393
+ every empty-state line, every form label through LABEL_VOICE_CLASS, every
394
+ MoneyText caption. An axe crawl of the br-sfn cockpit measured both pairs
395
+ on 2026-09-06; every console on this library inherited them.
396
+
397
+ 80% is the smallest round step that clears both: 6.36:1 on
398
+ --container-surface and 4.68:1 on --muted. base/300 is the nearest scale
399
+ step that would also clear, but it is byte-identical to this theme's
400
+ --body-text, so adopting it would collapse the muted-versus-body step the
401
+ two tokens exist to express. Staying on the scale is worth less than
402
+ keeping that step, so the value leaves the scale instead.
403
+
404
+ That step is now thin, and the trade was made with eyes open: 80% sits at
405
+ L* 81 against --body-text at L* 85, so the two inks are 1.12:1 apart and
406
+ barely separable side by side. Legibility won because the weight is all
407
+ on one side: this token has 86 call sites and is read as prose on every
408
+ surface, while --body-text has one (an uppercase sidebar group label) plus
409
+ two derived theme keys. A reader who needs the step to be visible should
410
+ move --body-text, which is cheap to reposition, rather than push this one
411
+ back under the AA floor.
412
+
413
+ Repairs a fourth pair on the way: the disabled chip in autocomplete and
414
+ multiple-select paints text-muted on bg-muted-foreground, 3.01:1 before
415
+ and 4.68:1 now.
416
+ */
417
+ --muted-foreground: 240 5% 80%; /* between base/400 and base/300: #C9C9CF, AA on container-surface and muted */
353
418
  --card: var(--container-surface);
354
419
  --card-foreground: var(--container-text);
355
420
  --popover: 240 4% 16%; /* base/800 = #27272A */
@@ -359,7 +424,7 @@
359
424
  /* Held at base/400 rather than stepped down with the light theme: on the
360
425
  dark field that is already only 5.8:1, and going lighter here means
361
426
  darker, which would push the hint under the AA floor. */
362
- --input-placeholder: 240 5% 65%; /* base/400 = #A1A1AA */
427
+ --input-placeholder: 240 5% 80%; /* between base/400 and base/300: #C9C9CF, AA on card (6.3:1); base/400 read 4.07:1 there */
363
428
  --primary: 240 5% 96%; /* base/100 = #F4F4F5 */
364
429
  --primary-foreground: 240 4% 16%; /* base/800 = #27272A */
365
430
  --secondary: 240 4% 16%; /* base/800 = #27272A */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lerianstudio/sindarian-ui",
3
- "version": "1.4.0-beta.6",
3
+ "version": "1.4.0-beta.8",
4
4
  "description": "Sindarian UI - A UI library for Midaz Console",
5
5
  "license": "ISC",
6
6
  "author": {