@motir/design-system 0.4.0 → 0.4.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.
@@ -28,7 +28,7 @@ function Switch({
28
28
  onClick: () => onCheckedChange(!checked),
29
29
  className: cn(
30
30
  "relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border transition-colors focus-visible:ring-2 focus-visible:ring-(--focus-ring-color) focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
31
- checked ? "border-(--el-switch-on) bg-(--el-switch-on)" : "border-(--el-border-strong) bg-(--el-muted)",
31
+ checked ? "border-(--el-switch-on-border) bg-(--el-switch-on)" : "border-(--el-border-strong) bg-(--el-muted)",
32
32
  className
33
33
  ),
34
34
  children: /* @__PURE__ */ jsx(
@@ -36,8 +36,8 @@ function Switch({
36
36
  {
37
37
  "aria-hidden": true,
38
38
  className: cn(
39
- "inline-block size-3.5 rounded-full bg-(--el-switch-knob) shadow-(--shadow-subtle) transition-transform",
40
- checked ? "translate-x-[18px]" : "translate-x-0.5"
39
+ "inline-block size-3.5 rounded-full shadow-(--shadow-subtle) transition-transform",
40
+ checked ? "translate-x-[18px] bg-(--el-switch-knob)" : "translate-x-0.5 bg-(--el-switch-knob-off)"
41
41
  )
42
42
  }
43
43
  )
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/components/ui/Switch.tsx"],"names":[],"mappings":";;;;;AAcO,SAAS,MAAM,MAAA,EAA8B;AAClD,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACgBO,SAAS,MAAA,CAAO;AAAA,EACrB,OAAA;AAAA,EACA,eAAA;AAAA,EACA,QAAA;AAAA,EACA,EAAA;AAAA,EACA,SAAA;AAAA,EACA,YAAA,EAAc,SAAA;AAAA,EACd,iBAAA,EAAmB;AACrB,CAAA,EAAgB;AACd,EAAA,uBACE,GAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAK,QAAA;AAAA,MACL,IAAA,EAAK,QAAA;AAAA,MACL,EAAA;AAAA,MACA,cAAA,EAAc,OAAA;AAAA,MACd,YAAA,EAAY,SAAA;AAAA,MACZ,iBAAA,EAAiB,cAAA;AAAA,MACjB,QAAA;AAAA,MACA,OAAA,EAAS,MAAM,eAAA,CAAgB,CAAC,OAAO,CAAA;AAAA,MACvC,SAAA,EAAW,EAAA;AAAA,QACT,kOAAA;AAAA,QACA,UACI,6CAAA,GACA,6CAAA;AAAA,QACJ;AAAA,OACF;AAAA,MAEA,QAAA,kBAAA,GAAA;AAAA,QAAC,MAAA;AAAA,QAAA;AAAA,UACC,aAAA,EAAW,IAAA;AAAA,UACX,SAAA,EAAW,EAAA;AAAA,YACT,wGAAA;AAAA,YACA,UAAU,oBAAA,GAAuB;AAAA;AACnC;AAAA;AACF;AAAA,GACF;AAEJ","file":"Switch.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Merge Tailwind class names without conflicts.\n *\n * `clsx` handles conditional inclusion (booleans, undefineds, arrays);\n * `twMerge` resolves conflicting Tailwind classes (later wins, e.g.\n * `px-2 px-4` → `px-4`). Together they are the de facto standard for\n * composing Tailwind classes in React components.\n *\n * @example\n * cn('px-2 py-1', isActive && 'bg-primary', className)\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","import { cn } from '../../utils/cn';\n\n/**\n * Switch — a sliding on/off toggle (`role=\"switch\"`), keyboard-operable.\n *\n * The shared primitive the design system names (notifications preferences\n * matrix, automation rule enable, …). Colour flows through `--el-*`; the knob +\n * track are genuinely-circular (`rounded-full`), the shape-rule carve-out for a\n * pill control. Always pass an `aria-label` (or wire an external `<label>` via\n * `id`) so the toggle is screen-reader-legible — a bare switch announces no\n * purpose.\n *\n * @example\n * <Switch checked={on} onCheckedChange={setOn} aria-label=\"Email for Mentioned\" />\n */\nexport interface SwitchProps {\n checked: boolean;\n onCheckedChange: (next: boolean) => void;\n disabled?: boolean;\n /** Accessible name — required unless an external `<label htmlFor=id>` exists. */\n 'aria-label'?: string;\n /**\n * Accessible name by reference — the id of a visible label element. Use this\n * instead of `aria-label` when the label already renders on screen (so the\n * name stays in sync with the visible text). One of `aria-label` /\n * `aria-labelledby` should be set, else the switch announces no purpose.\n */\n 'aria-labelledby'?: string;\n id?: string;\n className?: string;\n}\n\nexport function Switch({\n checked,\n onCheckedChange,\n disabled,\n id,\n className,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n}: SwitchProps) {\n return (\n <button\n type=\"button\"\n role=\"switch\"\n id={id}\n aria-checked={checked}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n disabled={disabled}\n onClick={() => onCheckedChange(!checked)}\n className={cn(\n 'relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border transition-colors focus-visible:ring-2 focus-visible:ring-(--focus-ring-color) focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',\n checked\n ? 'border-(--el-switch-on) bg-(--el-switch-on)'\n : 'border-(--el-border-strong) bg-(--el-muted)',\n className,\n )}\n >\n <span\n aria-hidden\n className={cn(\n 'inline-block size-3.5 rounded-full bg-(--el-switch-knob) shadow-(--shadow-subtle) transition-transform',\n checked ? 'translate-x-[18px]' : 'translate-x-0.5',\n )}\n />\n </button>\n );\n}\n"]}
1
+ {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/components/ui/Switch.tsx"],"names":[],"mappings":";;;;;AAcO,SAAS,MAAM,MAAA,EAA8B;AAClD,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACgBO,SAAS,MAAA,CAAO;AAAA,EACrB,OAAA;AAAA,EACA,eAAA;AAAA,EACA,QAAA;AAAA,EACA,EAAA;AAAA,EACA,SAAA;AAAA,EACA,YAAA,EAAc,SAAA;AAAA,EACd,iBAAA,EAAmB;AACrB,CAAA,EAAgB;AACd,EAAA,uBACE,GAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAK,QAAA;AAAA,MACL,IAAA,EAAK,QAAA;AAAA,MACL,EAAA;AAAA,MACA,cAAA,EAAc,OAAA;AAAA,MACd,YAAA,EAAY,SAAA;AAAA,MACZ,iBAAA,EAAiB,cAAA;AAAA,MACjB,QAAA;AAAA,MACA,OAAA,EAAS,MAAM,eAAA,CAAgB,CAAC,OAAO,CAAA;AAAA,MACvC,SAAA,EAAW,EAAA;AAAA,QACT,kOAAA;AAAA,QACA,UACI,oDAAA,GACA,6CAAA;AAAA,QACJ;AAAA,OACF;AAAA,MAEA,QAAA,kBAAA,GAAA;AAAA,QAAC,MAAA;AAAA,QAAA;AAAA,UACC,aAAA,EAAW,IAAA;AAAA,UACX,SAAA,EAAW,EAAA;AAAA,YACT,kFAAA;AAAA,YACA,UACI,0CAAA,GACA;AAAA;AACN;AAAA;AACF;AAAA,GACF;AAEJ","file":"Switch.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Merge Tailwind class names without conflicts.\n *\n * `clsx` handles conditional inclusion (booleans, undefineds, arrays);\n * `twMerge` resolves conflicting Tailwind classes (later wins, e.g.\n * `px-2 px-4` → `px-4`). Together they are the de facto standard for\n * composing Tailwind classes in React components.\n *\n * @example\n * cn('px-2 py-1', isActive && 'bg-primary', className)\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","import { cn } from '../../utils/cn';\n\n/**\n * Switch — a sliding on/off toggle (`role=\"switch\"`), keyboard-operable.\n *\n * The shared primitive the design system names (notifications preferences\n * matrix, automation rule enable, …). Colour flows through `--el-*`; the knob +\n * track are genuinely-circular (`rounded-full`), the shape-rule carve-out for a\n * pill control. Always pass an `aria-label` (or wire an external `<label>` via\n * `id`) so the toggle is screen-reader-legible — a bare switch announces no\n * purpose.\n *\n * @example\n * <Switch checked={on} onCheckedChange={setOn} aria-label=\"Email for Mentioned\" />\n */\nexport interface SwitchProps {\n checked: boolean;\n onCheckedChange: (next: boolean) => void;\n disabled?: boolean;\n /** Accessible name — required unless an external `<label htmlFor=id>` exists. */\n 'aria-label'?: string;\n /**\n * Accessible name by reference — the id of a visible label element. Use this\n * instead of `aria-label` when the label already renders on screen (so the\n * name stays in sync with the visible text). One of `aria-label` /\n * `aria-labelledby` should be set, else the switch announces no purpose.\n */\n 'aria-labelledby'?: string;\n id?: string;\n className?: string;\n}\n\nexport function Switch({\n checked,\n onCheckedChange,\n disabled,\n id,\n className,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n}: SwitchProps) {\n return (\n <button\n type=\"button\"\n role=\"switch\"\n id={id}\n aria-checked={checked}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n disabled={disabled}\n onClick={() => onCheckedChange(!checked)}\n className={cn(\n 'relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border transition-colors focus-visible:ring-2 focus-visible:ring-(--focus-ring-color) focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',\n checked\n ? 'border-(--el-switch-on-border) bg-(--el-switch-on)'\n : 'border-(--el-border-strong) bg-(--el-muted)',\n className,\n )}\n >\n <span\n aria-hidden\n className={cn(\n 'inline-block size-3.5 rounded-full shadow-(--shadow-subtle) transition-transform',\n checked\n ? 'translate-x-[18px] bg-(--el-switch-knob)'\n : 'translate-x-0.5 bg-(--el-switch-knob-off)',\n )}\n />\n </button>\n );\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motir/design-system",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Motir's 3-axis design system (colour · style · type): distributable token CSS, the axis registries, the theme-apply API, and framework-agnostic React primitives. Shared by motir-core and planner-scaffolded products.",
5
5
  "license": "GPL-3.0-only",
6
6
  "repository": {
package/theme.css CHANGED
@@ -3543,7 +3543,27 @@
3543
3543
  --el-tooltip-bg: var(--color-foreground); /* inverted tooltip fill (was --el-text) */
3544
3544
  --el-tooltip-text: var(--color-background); /* text on the tooltip (was --el-text-inverted) */
3545
3545
  --el-switch-on: var(--color-primary-fill); /* checked track (was --el-accent) */
3546
- --el-switch-knob: var(--color-surface); /* the thumb (was --el-surface) */
3546
+ /* The ON track's OUTER EDGE, against the page it sits on (WCAG 1.4.11, 3:1 —
3547
+ where the control is, and how far its knob has travelled). The fill alone
3548
+ fell to 1.80 / 1.47 / 1.57:1 on --el-page-bg in amber, citrine and candy
3549
+ light, whose primary fill is a pale yellow or pink. So the border takes the
3550
+ palette's own primary INK rather than its fill: the fill, and with it the
3551
+ palette's identity and the ON knob pairing above it, is untouched. Where the
3552
+ fill already passes the two are equal or near it; lowest 4.67:1 across all
3553
+ twenty palette x theme pairs. tests/theme/switchStateContrast.test.ts
3554
+ (MOTIR-5715). */
3555
+ --el-switch-on-border: var(--color-primary); /* the ON track's edge, on --el-page-bg */
3556
+ /* The two knob inks are a PAIR with the track under them, and are measured as
3557
+ one (WCAG 1.4.11, 3:1 — the knob's position IS the switch's state). The
3558
+ OFF knob used to share --el-switch-knob and sat at 1.01:1 on --el-muted in
3559
+ light and 1.00:1 (the same hex) in dark; the ON knob on --color-surface fell
3560
+ to 1.33–2.97:1 on the light primary fills of amber, citrine, candy and
3561
+ sienna, and garnet dark. So the ON knob is the palette's own ink FOR its
3562
+ primary fill (what the primary button writes on it), and the OFF knob is the
3563
+ muted-foreground grey: 4.99:1 / 4.12:1 at the lowest across all twenty
3564
+ palette x theme pairs. tests/theme/switchStateContrast.test.ts (MOTIR-5711). */
3565
+ --el-switch-knob: var(--color-primary-foreground); /* the ON thumb, on --el-switch-on */
3566
+ --el-switch-knob-off: var(--color-muted-foreground); /* the OFF thumb, on --el-muted */
3547
3567
  --el-option-active-bg: var(--color-muted); /* highlighted listbox option (was --el-surface) */
3548
3568
  --el-overlay-scrim: #00000066; /* modal/dialog scrim — concrete (light); dark companion below */
3549
3569
  --el-chip-bg: var(--color-surface); /* neutral chip fill (tinted chips keep their tint) */