@proteos/ui 0.55.0 → 0.57.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.
Files changed (50) hide show
  1. package/dist/{chunk-IKAX76MB.js → chunk-NSE4VW3Z.js} +45 -21
  2. package/dist/chunk-NSE4VW3Z.js.map +1 -0
  3. package/dist/{chunk-MWAXK66H.cjs → chunk-NWGG5WFL.cjs} +45 -21
  4. package/dist/chunk-NWGG5WFL.cjs.map +1 -0
  5. package/dist/index.cjs +377 -361
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.cts +46 -14
  8. package/dist/index.d.ts +46 -14
  9. package/dist/index.js +124 -108
  10. package/dist/index.js.map +1 -1
  11. package/dist/{pdf-view-MH75RVQP.js → pdf-view-RQ2OZYHJ.js} +3 -3
  12. package/dist/{pdf-view-MH75RVQP.js.map → pdf-view-RQ2OZYHJ.js.map} +1 -1
  13. package/dist/{pdf-view-K4MMVHVY.cjs → pdf-view-RRMAUK3L.cjs} +7 -7
  14. package/dist/{pdf-view-K4MMVHVY.cjs.map → pdf-view-RRMAUK3L.cjs.map} +1 -1
  15. package/package.json +2 -2
  16. package/src/components/avatar/avatar.stories.tsx +13 -0
  17. package/src/components/avatar/avatar.tsx +16 -9
  18. package/src/components/badge-multi-select/badge-multi-select.tsx +2 -2
  19. package/src/components/button/button.stories.tsx +202 -97
  20. package/src/components/button/button.test.tsx +40 -7
  21. package/src/components/button/button.tsx +71 -18
  22. package/src/components/combobox/combobox.tsx +2 -2
  23. package/src/components/currency-input/currency-input.tsx +2 -2
  24. package/src/components/date-picker/calendar-internals.tsx +1 -1
  25. package/src/components/date-picker/date-picker.tsx +3 -3
  26. package/src/components/date-time-picker/date-time-picker.tsx +3 -3
  27. package/src/components/icon-picker/icon-picker.tsx +2 -2
  28. package/src/components/image-lightbox/image-lightbox.tsx +2 -2
  29. package/src/components/menu/menu.tsx +2 -2
  30. package/src/components/modal/modal.tsx +4 -4
  31. package/src/components/multi-select/multi-select.tsx +2 -2
  32. package/src/components/popover/popover.tsx +2 -2
  33. package/src/components/record-filter/chip-value.tsx +6 -5
  34. package/src/components/record-filter/filter-value-input.tsx +11 -9
  35. package/src/components/record-filter/nested-group.tsx +2 -2
  36. package/src/components/record-filter/operators.test.ts +67 -12
  37. package/src/components/record-filter/operators.ts +43 -2
  38. package/src/components/record-filter/record-filter-builder-panel.tsx +11 -23
  39. package/src/components/record-filter/record-filter-chips.tsx +3 -10
  40. package/src/components/record-filter/types.ts +6 -1
  41. package/src/components/segmented-control/segmented-control.stories.tsx +134 -8
  42. package/src/components/segmented-control/segmented-control.test.tsx +81 -8
  43. package/src/components/segmented-control/segmented-control.tsx +38 -19
  44. package/src/components/select/select.tsx +2 -2
  45. package/src/components/time-field/time-field.tsx +1 -1
  46. package/src/components/toast/toast.tsx +2 -2
  47. package/src/components/tooltip/tooltip.tsx +2 -2
  48. package/src/styles.css +58 -0
  49. package/dist/chunk-IKAX76MB.js.map +0 -1
  50. package/dist/chunk-MWAXK66H.cjs.map +0 -1
@@ -8,10 +8,27 @@ import { jsx } from 'react/jsx-runtime';
8
8
  function cn(...inputs) {
9
9
  return twMerge(clsx(inputs));
10
10
  }
11
+ var ctaClasses = [
12
+ "bg-ink text-surface border-ink",
13
+ "hovered:bg-accent-ink hovered:border-accent-ink"
14
+ ];
15
+ var raisedClasses = [
16
+ "bg-surface text-ink border-rule shadow-[var(--shadow-rest)]",
17
+ "hovered:bg-surface-2 hovered:border-rule-strong",
18
+ "pressed:bg-surface-3 pressed:shadow-none",
19
+ "focus-visible:shadow-[var(--shadow-focus),var(--shadow-rest)]"
20
+ ];
21
+ var mutedClasses = [
22
+ "bg-surface-3 text-ink-2 border-transparent",
23
+ "hovered:bg-surface-4 hovered:text-ink",
24
+ "pressed:bg-[color-mix(in_oklab,var(--color-ink)_8%,var(--color-surface-4))]"
25
+ ];
11
26
  var buttonStyles = tv({
12
27
  base: [
13
28
  "inline-flex items-center justify-center gap-2 whitespace-nowrap cursor-pointer",
14
- "font-mono tracking-[-0.005em] border border-transparent",
29
+ // Button labels are human copy, not machine fact — Inter 500 (DESIGN.md
30
+ // typography.body), not the mono reserved for ids, timestamps and badges.
31
+ "font-sans font-medium tracking-[-0.005em] border border-transparent",
15
32
  "transition-[background-color,color,border-color,box-shadow,transform] duration-150",
16
33
  "outline-none pressed:translate-y-px",
17
34
  "focus-visible:shadow-[var(--shadow-focus)]",
@@ -19,26 +36,31 @@ var buttonStyles = tv({
19
36
  ],
20
37
  variants: {
21
38
  variant: {
22
- primary: ["bg-ink text-surface border-ink", "hover:bg-accent-ink hover:border-accent-ink"],
23
- // Hover uses accent-2 — the token DESIGN.md reserves for "hover on
24
- // accent". accent-ink is a text-on-tinted-surface color and flips
25
- // light in dark mode, so it can't double as a hover fill here.
26
- accent: [
27
- "bg-accent text-[#f0efe8] border-accent",
28
- "hover:bg-accent-2 hover:border-accent-2"
29
- ],
30
- secondary: ["bg-transparent text-ink border-ink", "hover:bg-ink hover:text-surface"],
39
+ cta: ctaClasses,
40
+ accent: ctaClasses,
41
+ primary: raisedClasses,
42
+ secondary: mutedClasses,
31
43
  ghost: [
32
- "bg-bg text-ink-2 border-rule-strong",
33
- "hover:bg-surface-2 hover:text-ink hover:border-ink-4"
44
+ "bg-transparent text-ink-2 border-transparent",
45
+ "hovered:bg-surface-3 hovered:text-ink",
46
+ "pressed:bg-surface-4"
47
+ ],
48
+ subtle: [
49
+ "bg-transparent text-ink-3 border-transparent",
50
+ "hovered:bg-surface-2 hovered:text-ink",
51
+ "pressed:bg-surface-3"
34
52
  ],
35
- // Transparent at rest, paper-fill on hover. The toolbar / sidebar
36
- // register — pairs with `size="icon-md"` for top-bar icon buttons.
37
- subtle: ["bg-transparent text-ink-3 border-transparent", "hover:bg-surface-2 hover:text-ink"],
38
53
  link: [
39
54
  "bg-transparent text-ink-2 border-transparent",
40
55
  "px-0.5 border-b border-b-ink-4",
41
- "hover:text-ink hover:border-b-ink"
56
+ "hovered:text-ink hovered:border-b-ink"
57
+ ],
58
+ // `text-surface`, not white: dark's --color-danger is a light salmon, and
59
+ // the surface token flips dark with it while white would fail contrast.
60
+ danger: [
61
+ "bg-danger text-surface border-danger",
62
+ "hovered:bg-[color-mix(in_oklab,var(--color-danger)_88%,var(--color-ink))]",
63
+ "hovered:border-[color-mix(in_oklab,var(--color-danger)_88%,var(--color-ink))]"
42
64
  ]
43
65
  },
44
66
  size: {
@@ -51,8 +73,8 @@ var buttonStyles = tv({
51
73
  "icon-lg": "size-[42px] p-0 [&_svg]:size-[18px]"
52
74
  },
53
75
  // Corner shape. Default is rounded (soft 10px radius — the standard
54
- // Proteos button), `pill` is the fully-rounded legacy treatment.
55
- // The `link` variant overrides both to a flat underline.
76
+ // Proteos button), `pill` is the fully-rounded treatment for a small set
77
+ // of prominent CTAs. The `link` variant overrides both to a flat underline.
56
78
  shape: {
57
79
  rounded: "rounded-md",
58
80
  pill: "rounded-pill"
@@ -64,7 +86,9 @@ var buttonStyles = tv({
64
86
  { variant: "link", size: "lg", class: "px-0.5 py-0" },
65
87
  // Link never takes a corner radius — it's an underline, not a box.
66
88
  { variant: "link", shape: "rounded", class: "rounded-none" },
67
- { variant: "link", shape: "pill", class: "rounded-none" }
89
+ { variant: "link", shape: "pill", class: "rounded-none" },
90
+ // Destructive confirms get the terracotta halo instead of the indigo one.
91
+ { variant: "danger", class: "focus-visible:shadow-[var(--shadow-focus-danger)]" }
68
92
  ],
69
93
  defaultVariants: {
70
94
  variant: "primary",
@@ -109,5 +133,5 @@ function Skeleton({ shape, className, ...props }) {
109
133
  }
110
134
 
111
135
  export { Button, Skeleton, buttonStyles, cn, skeletonStyles };
112
- //# sourceMappingURL=chunk-IKAX76MB.js.map
113
- //# sourceMappingURL=chunk-IKAX76MB.js.map
136
+ //# sourceMappingURL=chunk-NSE4VW3Z.js.map
137
+ //# sourceMappingURL=chunk-NSE4VW3Z.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/cn.ts","../src/components/button/button.tsx","../src/components/skeleton/skeleton.tsx"],"names":["AriaButton","tv","jsx"],"mappings":";;;;;;;AAGO,SAAS,MAAM,MAAA,EAA8B;AAClD,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACuBA,IAAM,UAAA,GAAa;AAAA,EACjB,gCAAA;AAAA,EACA;AACF,CAAA;AAIA,IAAM,aAAA,GAAgB;AAAA,EACpB,6DAAA;AAAA,EACA,iDAAA;AAAA,EACA,0CAAA;AAAA,EACA;AACF,CAAA;AAGA,IAAM,YAAA,GAAe;AAAA,EACnB,4CAAA;AAAA,EACA,uCAAA;AAAA,EACA;AACF,CAAA;AAEO,IAAM,eAAe,EAAA,CAAG;AAAA,EAC7B,IAAA,EAAM;AAAA,IACJ,gFAAA;AAAA;AAAA;AAAA,IAGA,qEAAA;AAAA,IACA,oFAAA;AAAA,IACA,qCAAA;AAAA,IACA,4CAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,QAAA,EAAU;AAAA,IACR,OAAA,EAAS;AAAA,MACP,GAAA,EAAK,UAAA;AAAA,MACL,MAAA,EAAQ,UAAA;AAAA,MACR,OAAA,EAAS,aAAA;AAAA,MACT,SAAA,EAAW,YAAA;AAAA,MACX,KAAA,EAAO;AAAA,QACL,8CAAA;AAAA,QACA,uCAAA;AAAA,QACA;AAAA,OACF;AAAA,MACA,MAAA,EAAQ;AAAA,QACN,8CAAA;AAAA,QACA,uCAAA;AAAA,QACA;AAAA,OACF;AAAA,MACA,IAAA,EAAM;AAAA,QACJ,8CAAA;AAAA,QACA,gCAAA;AAAA,QACA;AAAA,OACF;AAAA;AAAA;AAAA,MAGA,MAAA,EAAQ;AAAA,QACN,sCAAA;AAAA,QACA,2EAAA;AAAA,QACA;AAAA;AACF,KACF;AAAA,IACA,IAAA,EAAM;AAAA,MACJ,EAAA,EAAI,oCAAA;AAAA,MACJ,EAAA,EAAI,0CAAA;AAAA,MACJ,EAAA,EAAI,yCAAA;AAAA;AAAA,MAEJ,SAAA,EAAW,6BAAA;AAAA,MACX,SAAA,EAAW,gCAAA;AAAA,MACX,SAAA,EAAW;AAAA,KACb;AAAA;AAAA;AAAA;AAAA,IAIA,KAAA,EAAO;AAAA,MACL,OAAA,EAAS,YAAA;AAAA,MACT,IAAA,EAAM;AAAA;AACR,GACF;AAAA,EACA,gBAAA,EAAkB;AAAA,IAChB,EAAE,OAAA,EAAS,MAAA,EAAQ,IAAA,EAAM,IAAA,EAAM,OAAO,aAAA,EAAc;AAAA,IACpD,EAAE,OAAA,EAAS,MAAA,EAAQ,IAAA,EAAM,IAAA,EAAM,OAAO,aAAA,EAAc;AAAA,IACpD,EAAE,OAAA,EAAS,MAAA,EAAQ,IAAA,EAAM,IAAA,EAAM,OAAO,aAAA,EAAc;AAAA;AAAA,IAEpD,EAAE,OAAA,EAAS,MAAA,EAAQ,KAAA,EAAO,SAAA,EAAW,OAAO,cAAA,EAAe;AAAA,IAC3D,EAAE,OAAA,EAAS,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAQ,OAAO,cAAA,EAAe;AAAA;AAAA,IAExD,EAAE,OAAA,EAAS,QAAA,EAAU,KAAA,EAAO,mDAAA;AAAoD,GAClF;AAAA,EACA,eAAA,EAAiB;AAAA,IACf,OAAA,EAAS,SAAA;AAAA,IACT,IAAA,EAAM,IAAA;AAAA,IACN,KAAA,EAAO;AAAA;AAEX,CAAC;AAKM,SAAS,MAAA,CAAO,EAAE,OAAA,EAAS,IAAA,EAAM,OAAO,SAAA,EAAW,GAAG,OAAM,EAAgB;AACjF,EAAA,uBACE,GAAA;AAAA,IAACA,QAAA;AAAA,IAAA;AAAA,MACE,GAAG,KAAA;AAAA,MACJ,SAAA,EAAW,CAAC,WAAA,KACV,EAAA;AAAA,QACE,YAAA,CAAa,EAAE,OAAA,EAAS,IAAA,EAAM,OAAO,CAAA;AAAA,QACrC,OAAO,SAAA,KAAc,UAAA,GAAa,SAAA,CAAU,WAAW,CAAA,GAAI;AAAA;AAC7D;AAAA,GAEJ;AAEJ;AC5HO,IAAM,iBAAiBC,EAAAA,CAAG;AAAA,EAC/B,IAAA,EAAM,CAAC,eAAA,EAAiB,cAAc,CAAA;AAAA,EACtC,QAAA,EAAU;AAAA,IACR,KAAA,EAAO;AAAA,MACL,IAAA,EAAM,YAAA;AAAA,MACN,MAAA,EAAQ,cAAA;AAAA;AAAA;AAAA,MAGR,IAAA,EAAM;AAAA;AACR,GACF;AAAA,EACA,eAAA,EAAiB,EAAE,KAAA,EAAO,MAAA;AAC5B,CAAC;AAKM,SAAS,SAAS,EAAE,KAAA,EAAO,SAAA,EAAW,GAAG,OAAM,EAAkB;AACtE,EAAA,uBACEC,GAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MAIC,aAAA,EAAY,MAAA;AAAA,MACX,GAAG,KAAA;AAAA,MACJ,WAAW,EAAA,CAAG,cAAA,CAAe,EAAE,KAAA,EAAO,GAAG,SAAS;AAAA;AAAA,GACpD;AAEJ","file":"chunk-NSE4VW3Z.js","sourcesContent":["import { type ClassValue, clsx } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs))\n}\n","import { Button as AriaButton, type ButtonProps as AriaButtonProps } from 'react-aria-components'\nimport { tv, type VariantProps } from 'tailwind-variants'\n\nimport { cn } from '../../lib/cn'\n\n/* =========================================================================\n Button — emphasis ladder, top to bottom:\n\n cta ink fill. The one strong action per view (never two visible).\n primary raised white: `surface` fill, `rule-strong` hairline, rest\n shadow. The everyday button; the default variant.\n secondary muted: `surface-3` fill, no border. Cancel / supporting actions.\n ghost secondary's chrome, only on hover. Invisible at rest.\n subtle borderless text button (toolbars, icon buttons).\n link inline underline.\n danger solid terracotta for destructive confirms.\n\n `accent` is a frozen alias of `cta` — variant names are part of the runtime\n contract for module component bundles, so nothing is ever removed.\n\n Interaction state comes from React Aria's data attributes (`hovered:`,\n `pressed:` — see the @custom-variant block in styles.css); `focus-visible:`\n and `disabled:` stay on the native pseudo-classes because the RAC root IS\n the focusable <button>. Hover is always a colour step on the surface ramp,\n never opacity; press is translateY(1px) plus one more ramp step.\n ========================================================================= */\n\n// Ink fill with the indigo-tinted hover. Kept verbatim from the old primary.\nconst ctaClasses = [\n 'bg-ink text-surface border-ink',\n 'hovered:bg-accent-ink hovered:border-accent-ink',\n]\n\n// Raised white: a lighter inner on the darker page ground. On a white card the\n// hairline + rest shadow carry it. Press sinks one step and drops the shadow.\nconst raisedClasses = [\n 'bg-surface text-ink border-rule shadow-[var(--shadow-rest)]',\n 'hovered:bg-surface-2 hovered:border-rule-strong',\n 'pressed:bg-surface-3 pressed:shadow-none',\n 'focus-visible:shadow-[var(--shadow-focus),var(--shadow-rest)]',\n]\n\n// Muted gray fill, no border. One step quieter than primary.\nconst mutedClasses = [\n 'bg-surface-3 text-ink-2 border-transparent',\n 'hovered:bg-surface-4 hovered:text-ink',\n 'pressed:bg-[color-mix(in_oklab,var(--color-ink)_8%,var(--color-surface-4))]',\n]\n\nexport const buttonStyles = tv({\n base: [\n 'inline-flex items-center justify-center gap-2 whitespace-nowrap cursor-pointer',\n // Button labels are human copy, not machine fact — Inter 500 (DESIGN.md\n // typography.body), not the mono reserved for ids, timestamps and badges.\n 'font-sans font-medium tracking-[-0.005em] border border-transparent',\n 'transition-[background-color,color,border-color,box-shadow,transform] duration-150',\n 'outline-none pressed:translate-y-px',\n 'focus-visible:shadow-[var(--shadow-focus)]',\n 'disabled:opacity-45 disabled:cursor-not-allowed disabled:pointer-events-none',\n ],\n variants: {\n variant: {\n cta: ctaClasses,\n accent: ctaClasses,\n primary: raisedClasses,\n secondary: mutedClasses,\n ghost: [\n 'bg-transparent text-ink-2 border-transparent',\n 'hovered:bg-surface-3 hovered:text-ink',\n 'pressed:bg-surface-4',\n ],\n subtle: [\n 'bg-transparent text-ink-3 border-transparent',\n 'hovered:bg-surface-2 hovered:text-ink',\n 'pressed:bg-surface-3',\n ],\n link: [\n 'bg-transparent text-ink-2 border-transparent',\n 'px-0.5 border-b border-b-ink-4',\n 'hovered:text-ink hovered:border-b-ink',\n ],\n // `text-surface`, not white: dark's --color-danger is a light salmon, and\n // the surface token flips dark with it while white would fail contrast.\n danger: [\n 'bg-danger text-surface border-danger',\n 'hovered:bg-[color-mix(in_oklab,var(--color-danger)_88%,var(--color-ink))]',\n 'hovered:border-[color-mix(in_oklab,var(--color-danger)_88%,var(--color-ink))]',\n ],\n },\n size: {\n sm: 'text-xs px-3 py-1.5 [&_svg]:size-3',\n md: 'text-[13px] px-4 py-[9px] [&_svg]:size-3',\n lg: 'text-sm px-[22px] py-3 [&_svg]:size-3.5',\n // Square dimensions for icon-only buttons. Requires an `aria-label`.\n 'icon-sm': 'size-7 p-0 [&_svg]:size-3.5',\n 'icon-md': 'size-[34px] p-0 [&_svg]:size-4',\n 'icon-lg': 'size-[42px] p-0 [&_svg]:size-[18px]',\n },\n // Corner shape. Default is rounded (soft 10px radius — the standard\n // Proteos button), `pill` is the fully-rounded treatment for a small set\n // of prominent CTAs. The `link` variant overrides both to a flat underline.\n shape: {\n rounded: 'rounded-md',\n pill: 'rounded-pill',\n },\n },\n compoundVariants: [\n { variant: 'link', size: 'sm', class: 'px-0.5 py-0' },\n { variant: 'link', size: 'md', class: 'px-0.5 py-0' },\n { variant: 'link', size: 'lg', class: 'px-0.5 py-0' },\n // Link never takes a corner radius — it's an underline, not a box.\n { variant: 'link', shape: 'rounded', class: 'rounded-none' },\n { variant: 'link', shape: 'pill', class: 'rounded-none' },\n // Destructive confirms get the terracotta halo instead of the indigo one.\n { variant: 'danger', class: 'focus-visible:shadow-[var(--shadow-focus-danger)]' },\n ],\n defaultVariants: {\n variant: 'primary',\n size: 'md',\n shape: 'rounded',\n },\n})\n\nexport type ButtonVariants = VariantProps<typeof buttonStyles>\nexport type ButtonProps = AriaButtonProps & ButtonVariants\n\nexport function Button({ variant, size, shape, className, ...props }: ButtonProps) {\n return (\n <AriaButton\n {...props}\n className={(renderProps) =>\n cn(\n buttonStyles({ variant, size, shape }),\n typeof className === 'function' ? className(renderProps) : className,\n )\n }\n />\n )\n}\n","import type { HTMLAttributes } from 'react'\nimport { tv, type VariantProps } from 'tailwind-variants'\n\nimport { cn } from '../../lib/cn'\n\n/* =========================================================================\n Skeleton — content placeholder for loading states.\n Purely presentational, so it doesn't wrap a React Aria primitive (RAC has\n no Skeleton — interactive primitives only). Accessibility lives in props:\n - decorative skeletons get `aria-hidden` so screen readers ignore them\n - skeletons that *announce* the loading state should be wrapped in a\n `role=\"status\"` region by the consumer (e.g. AppLayoutSkeleton).\n ========================================================================= */\n\nexport const skeletonStyles = tv({\n base: ['animate-pulse', 'bg-surface-3'],\n variants: {\n shape: {\n rect: 'rounded-md',\n circle: 'rounded-full',\n // `text` matches a typical line of body text — width is up to the\n // consumer; the rounded-sm + height keep proportions sensible.\n text: 'h-4 w-full rounded-sm',\n },\n },\n defaultVariants: { shape: 'rect' },\n})\n\nexport type SkeletonVariants = VariantProps<typeof skeletonStyles>\nexport type SkeletonProps = HTMLAttributes<HTMLDivElement> & SkeletonVariants\n\nexport function Skeleton({ shape, className, ...props }: SkeletonProps) {\n return (\n <div\n // Decorative by default. Consumers that want screen readers to announce\n // the loading state should wrap a Skeleton group in `role=\"status\"`\n // and pass `aria-hidden={false}` here, or just rely on the wrapper.\n aria-hidden=\"true\"\n {...props}\n className={cn(skeletonStyles({ shape }), className)}\n />\n )\n}\n"]}
@@ -10,10 +10,27 @@ var jsxRuntime = require('react/jsx-runtime');
10
10
  function cn(...inputs) {
11
11
  return tailwindMerge.twMerge(clsx.clsx(inputs));
12
12
  }
13
+ var ctaClasses = [
14
+ "bg-ink text-surface border-ink",
15
+ "hovered:bg-accent-ink hovered:border-accent-ink"
16
+ ];
17
+ var raisedClasses = [
18
+ "bg-surface text-ink border-rule shadow-[var(--shadow-rest)]",
19
+ "hovered:bg-surface-2 hovered:border-rule-strong",
20
+ "pressed:bg-surface-3 pressed:shadow-none",
21
+ "focus-visible:shadow-[var(--shadow-focus),var(--shadow-rest)]"
22
+ ];
23
+ var mutedClasses = [
24
+ "bg-surface-3 text-ink-2 border-transparent",
25
+ "hovered:bg-surface-4 hovered:text-ink",
26
+ "pressed:bg-[color-mix(in_oklab,var(--color-ink)_8%,var(--color-surface-4))]"
27
+ ];
13
28
  var buttonStyles = tailwindVariants.tv({
14
29
  base: [
15
30
  "inline-flex items-center justify-center gap-2 whitespace-nowrap cursor-pointer",
16
- "font-mono tracking-[-0.005em] border border-transparent",
31
+ // Button labels are human copy, not machine fact — Inter 500 (DESIGN.md
32
+ // typography.body), not the mono reserved for ids, timestamps and badges.
33
+ "font-sans font-medium tracking-[-0.005em] border border-transparent",
17
34
  "transition-[background-color,color,border-color,box-shadow,transform] duration-150",
18
35
  "outline-none pressed:translate-y-px",
19
36
  "focus-visible:shadow-[var(--shadow-focus)]",
@@ -21,26 +38,31 @@ var buttonStyles = tailwindVariants.tv({
21
38
  ],
22
39
  variants: {
23
40
  variant: {
24
- primary: ["bg-ink text-surface border-ink", "hover:bg-accent-ink hover:border-accent-ink"],
25
- // Hover uses accent-2 — the token DESIGN.md reserves for "hover on
26
- // accent". accent-ink is a text-on-tinted-surface color and flips
27
- // light in dark mode, so it can't double as a hover fill here.
28
- accent: [
29
- "bg-accent text-[#f0efe8] border-accent",
30
- "hover:bg-accent-2 hover:border-accent-2"
31
- ],
32
- secondary: ["bg-transparent text-ink border-ink", "hover:bg-ink hover:text-surface"],
41
+ cta: ctaClasses,
42
+ accent: ctaClasses,
43
+ primary: raisedClasses,
44
+ secondary: mutedClasses,
33
45
  ghost: [
34
- "bg-bg text-ink-2 border-rule-strong",
35
- "hover:bg-surface-2 hover:text-ink hover:border-ink-4"
46
+ "bg-transparent text-ink-2 border-transparent",
47
+ "hovered:bg-surface-3 hovered:text-ink",
48
+ "pressed:bg-surface-4"
49
+ ],
50
+ subtle: [
51
+ "bg-transparent text-ink-3 border-transparent",
52
+ "hovered:bg-surface-2 hovered:text-ink",
53
+ "pressed:bg-surface-3"
36
54
  ],
37
- // Transparent at rest, paper-fill on hover. The toolbar / sidebar
38
- // register — pairs with `size="icon-md"` for top-bar icon buttons.
39
- subtle: ["bg-transparent text-ink-3 border-transparent", "hover:bg-surface-2 hover:text-ink"],
40
55
  link: [
41
56
  "bg-transparent text-ink-2 border-transparent",
42
57
  "px-0.5 border-b border-b-ink-4",
43
- "hover:text-ink hover:border-b-ink"
58
+ "hovered:text-ink hovered:border-b-ink"
59
+ ],
60
+ // `text-surface`, not white: dark's --color-danger is a light salmon, and
61
+ // the surface token flips dark with it while white would fail contrast.
62
+ danger: [
63
+ "bg-danger text-surface border-danger",
64
+ "hovered:bg-[color-mix(in_oklab,var(--color-danger)_88%,var(--color-ink))]",
65
+ "hovered:border-[color-mix(in_oklab,var(--color-danger)_88%,var(--color-ink))]"
44
66
  ]
45
67
  },
46
68
  size: {
@@ -53,8 +75,8 @@ var buttonStyles = tailwindVariants.tv({
53
75
  "icon-lg": "size-[42px] p-0 [&_svg]:size-[18px]"
54
76
  },
55
77
  // Corner shape. Default is rounded (soft 10px radius — the standard
56
- // Proteos button), `pill` is the fully-rounded legacy treatment.
57
- // The `link` variant overrides both to a flat underline.
78
+ // Proteos button), `pill` is the fully-rounded treatment for a small set
79
+ // of prominent CTAs. The `link` variant overrides both to a flat underline.
58
80
  shape: {
59
81
  rounded: "rounded-md",
60
82
  pill: "rounded-pill"
@@ -66,7 +88,9 @@ var buttonStyles = tailwindVariants.tv({
66
88
  { variant: "link", size: "lg", class: "px-0.5 py-0" },
67
89
  // Link never takes a corner radius — it's an underline, not a box.
68
90
  { variant: "link", shape: "rounded", class: "rounded-none" },
69
- { variant: "link", shape: "pill", class: "rounded-none" }
91
+ { variant: "link", shape: "pill", class: "rounded-none" },
92
+ // Destructive confirms get the terracotta halo instead of the indigo one.
93
+ { variant: "danger", class: "focus-visible:shadow-[var(--shadow-focus-danger)]" }
70
94
  ],
71
95
  defaultVariants: {
72
96
  variant: "primary",
@@ -115,5 +139,5 @@ exports.Skeleton = Skeleton;
115
139
  exports.buttonStyles = buttonStyles;
116
140
  exports.cn = cn;
117
141
  exports.skeletonStyles = skeletonStyles;
118
- //# sourceMappingURL=chunk-MWAXK66H.cjs.map
119
- //# sourceMappingURL=chunk-MWAXK66H.cjs.map
142
+ //# sourceMappingURL=chunk-NWGG5WFL.cjs.map
143
+ //# sourceMappingURL=chunk-NWGG5WFL.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/cn.ts","../src/components/button/button.tsx","../src/components/skeleton/skeleton.tsx"],"names":["twMerge","clsx","tv","jsx","AriaButton"],"mappings":"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","file":"chunk-NWGG5WFL.cjs","sourcesContent":["import { type ClassValue, clsx } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs))\n}\n","import { Button as AriaButton, type ButtonProps as AriaButtonProps } from 'react-aria-components'\nimport { tv, type VariantProps } from 'tailwind-variants'\n\nimport { cn } from '../../lib/cn'\n\n/* =========================================================================\n Button — emphasis ladder, top to bottom:\n\n cta ink fill. The one strong action per view (never two visible).\n primary raised white: `surface` fill, `rule-strong` hairline, rest\n shadow. The everyday button; the default variant.\n secondary muted: `surface-3` fill, no border. Cancel / supporting actions.\n ghost secondary's chrome, only on hover. Invisible at rest.\n subtle borderless text button (toolbars, icon buttons).\n link inline underline.\n danger solid terracotta for destructive confirms.\n\n `accent` is a frozen alias of `cta` — variant names are part of the runtime\n contract for module component bundles, so nothing is ever removed.\n\n Interaction state comes from React Aria's data attributes (`hovered:`,\n `pressed:` — see the @custom-variant block in styles.css); `focus-visible:`\n and `disabled:` stay on the native pseudo-classes because the RAC root IS\n the focusable <button>. Hover is always a colour step on the surface ramp,\n never opacity; press is translateY(1px) plus one more ramp step.\n ========================================================================= */\n\n// Ink fill with the indigo-tinted hover. Kept verbatim from the old primary.\nconst ctaClasses = [\n 'bg-ink text-surface border-ink',\n 'hovered:bg-accent-ink hovered:border-accent-ink',\n]\n\n// Raised white: a lighter inner on the darker page ground. On a white card the\n// hairline + rest shadow carry it. Press sinks one step and drops the shadow.\nconst raisedClasses = [\n 'bg-surface text-ink border-rule shadow-[var(--shadow-rest)]',\n 'hovered:bg-surface-2 hovered:border-rule-strong',\n 'pressed:bg-surface-3 pressed:shadow-none',\n 'focus-visible:shadow-[var(--shadow-focus),var(--shadow-rest)]',\n]\n\n// Muted gray fill, no border. One step quieter than primary.\nconst mutedClasses = [\n 'bg-surface-3 text-ink-2 border-transparent',\n 'hovered:bg-surface-4 hovered:text-ink',\n 'pressed:bg-[color-mix(in_oklab,var(--color-ink)_8%,var(--color-surface-4))]',\n]\n\nexport const buttonStyles = tv({\n base: [\n 'inline-flex items-center justify-center gap-2 whitespace-nowrap cursor-pointer',\n // Button labels are human copy, not machine fact — Inter 500 (DESIGN.md\n // typography.body), not the mono reserved for ids, timestamps and badges.\n 'font-sans font-medium tracking-[-0.005em] border border-transparent',\n 'transition-[background-color,color,border-color,box-shadow,transform] duration-150',\n 'outline-none pressed:translate-y-px',\n 'focus-visible:shadow-[var(--shadow-focus)]',\n 'disabled:opacity-45 disabled:cursor-not-allowed disabled:pointer-events-none',\n ],\n variants: {\n variant: {\n cta: ctaClasses,\n accent: ctaClasses,\n primary: raisedClasses,\n secondary: mutedClasses,\n ghost: [\n 'bg-transparent text-ink-2 border-transparent',\n 'hovered:bg-surface-3 hovered:text-ink',\n 'pressed:bg-surface-4',\n ],\n subtle: [\n 'bg-transparent text-ink-3 border-transparent',\n 'hovered:bg-surface-2 hovered:text-ink',\n 'pressed:bg-surface-3',\n ],\n link: [\n 'bg-transparent text-ink-2 border-transparent',\n 'px-0.5 border-b border-b-ink-4',\n 'hovered:text-ink hovered:border-b-ink',\n ],\n // `text-surface`, not white: dark's --color-danger is a light salmon, and\n // the surface token flips dark with it while white would fail contrast.\n danger: [\n 'bg-danger text-surface border-danger',\n 'hovered:bg-[color-mix(in_oklab,var(--color-danger)_88%,var(--color-ink))]',\n 'hovered:border-[color-mix(in_oklab,var(--color-danger)_88%,var(--color-ink))]',\n ],\n },\n size: {\n sm: 'text-xs px-3 py-1.5 [&_svg]:size-3',\n md: 'text-[13px] px-4 py-[9px] [&_svg]:size-3',\n lg: 'text-sm px-[22px] py-3 [&_svg]:size-3.5',\n // Square dimensions for icon-only buttons. Requires an `aria-label`.\n 'icon-sm': 'size-7 p-0 [&_svg]:size-3.5',\n 'icon-md': 'size-[34px] p-0 [&_svg]:size-4',\n 'icon-lg': 'size-[42px] p-0 [&_svg]:size-[18px]',\n },\n // Corner shape. Default is rounded (soft 10px radius — the standard\n // Proteos button), `pill` is the fully-rounded treatment for a small set\n // of prominent CTAs. The `link` variant overrides both to a flat underline.\n shape: {\n rounded: 'rounded-md',\n pill: 'rounded-pill',\n },\n },\n compoundVariants: [\n { variant: 'link', size: 'sm', class: 'px-0.5 py-0' },\n { variant: 'link', size: 'md', class: 'px-0.5 py-0' },\n { variant: 'link', size: 'lg', class: 'px-0.5 py-0' },\n // Link never takes a corner radius — it's an underline, not a box.\n { variant: 'link', shape: 'rounded', class: 'rounded-none' },\n { variant: 'link', shape: 'pill', class: 'rounded-none' },\n // Destructive confirms get the terracotta halo instead of the indigo one.\n { variant: 'danger', class: 'focus-visible:shadow-[var(--shadow-focus-danger)]' },\n ],\n defaultVariants: {\n variant: 'primary',\n size: 'md',\n shape: 'rounded',\n },\n})\n\nexport type ButtonVariants = VariantProps<typeof buttonStyles>\nexport type ButtonProps = AriaButtonProps & ButtonVariants\n\nexport function Button({ variant, size, shape, className, ...props }: ButtonProps) {\n return (\n <AriaButton\n {...props}\n className={(renderProps) =>\n cn(\n buttonStyles({ variant, size, shape }),\n typeof className === 'function' ? className(renderProps) : className,\n )\n }\n />\n )\n}\n","import type { HTMLAttributes } from 'react'\nimport { tv, type VariantProps } from 'tailwind-variants'\n\nimport { cn } from '../../lib/cn'\n\n/* =========================================================================\n Skeleton — content placeholder for loading states.\n Purely presentational, so it doesn't wrap a React Aria primitive (RAC has\n no Skeleton — interactive primitives only). Accessibility lives in props:\n - decorative skeletons get `aria-hidden` so screen readers ignore them\n - skeletons that *announce* the loading state should be wrapped in a\n `role=\"status\"` region by the consumer (e.g. AppLayoutSkeleton).\n ========================================================================= */\n\nexport const skeletonStyles = tv({\n base: ['animate-pulse', 'bg-surface-3'],\n variants: {\n shape: {\n rect: 'rounded-md',\n circle: 'rounded-full',\n // `text` matches a typical line of body text — width is up to the\n // consumer; the rounded-sm + height keep proportions sensible.\n text: 'h-4 w-full rounded-sm',\n },\n },\n defaultVariants: { shape: 'rect' },\n})\n\nexport type SkeletonVariants = VariantProps<typeof skeletonStyles>\nexport type SkeletonProps = HTMLAttributes<HTMLDivElement> & SkeletonVariants\n\nexport function Skeleton({ shape, className, ...props }: SkeletonProps) {\n return (\n <div\n // Decorative by default. Consumers that want screen readers to announce\n // the loading state should wrap a Skeleton group in `role=\"status\"`\n // and pass `aria-hidden={false}` here, or just rely on the wrapper.\n aria-hidden=\"true\"\n {...props}\n className={cn(skeletonStyles({ shape }), className)}\n />\n )\n}\n"]}