cursedbelt 5.26.0 → 5.28.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 (98) hide show
  1. package/dist/react/charts/IntervalTimeline.d.ts.map +1 -1
  2. package/dist/react/charts/IntervalTimeline.js +6 -2
  3. package/dist/react/charts/IntervalTimeline.js.map +1 -1
  4. package/dist/react/components/Button.d.ts +1 -1
  5. package/dist/react/components/Button.d.ts.map +1 -1
  6. package/dist/react/components/Button.js +4 -0
  7. package/dist/react/components/Button.js.map +1 -1
  8. package/dist/react/components/CodeArea.d.ts.map +1 -1
  9. package/dist/react/components/CodeArea.js +3 -1
  10. package/dist/react/components/CodeArea.js.map +1 -1
  11. package/dist/react/components/DataTable/DataTable.js +2 -2
  12. package/dist/react/components/DataTable/DataTable.js.map +1 -1
  13. package/dist/react/components/DataTable/TableFilterBuilder.d.ts.map +1 -1
  14. package/dist/react/components/DataTable/TableFilterBuilder.js +2 -1
  15. package/dist/react/components/DataTable/TableFilterBuilder.js.map +1 -1
  16. package/dist/react/components/FindingsList.d.ts.map +1 -1
  17. package/dist/react/components/FindingsList.js +2 -1
  18. package/dist/react/components/FindingsList.js.map +1 -1
  19. package/dist/react/components/InfoHint.d.ts.map +1 -1
  20. package/dist/react/components/InfoHint.js +3 -1
  21. package/dist/react/components/InfoHint.js.map +1 -1
  22. package/dist/react/components/Section.d.ts.map +1 -1
  23. package/dist/react/components/Section.js +3 -1
  24. package/dist/react/components/Section.js.map +1 -1
  25. package/dist/react/components/calendar/MonthGrid.d.ts.map +1 -1
  26. package/dist/react/components/calendar/MonthGrid.js +33 -32
  27. package/dist/react/components/calendar/MonthGrid.js.map +1 -1
  28. package/dist/react/components/calendar/WeekView.d.ts.map +1 -1
  29. package/dist/react/components/calendar/WeekView.js +2 -1
  30. package/dist/react/components/calendar/WeekView.js.map +1 -1
  31. package/dist/react/components/sharing/RecipientPicker.d.ts.map +1 -1
  32. package/dist/react/components/sharing/RecipientPicker.js +3 -1
  33. package/dist/react/components/sharing/RecipientPicker.js.map +1 -1
  34. package/dist/react/dashboard-grid/WidgetFrame.d.ts.map +1 -1
  35. package/dist/react/dashboard-grid/WidgetFrame.js +2 -1
  36. package/dist/react/dashboard-grid/WidgetFrame.js.map +1 -1
  37. package/dist/react/diff-viewer/TextComparePane.d.ts.map +1 -1
  38. package/dist/react/diff-viewer/TextComparePane.js +2 -1
  39. package/dist/react/diff-viewer/TextComparePane.js.map +1 -1
  40. package/dist/react/emoji/EmojiAutocomplete.d.ts.map +1 -1
  41. package/dist/react/emoji/EmojiAutocomplete.js +3 -1
  42. package/dist/react/emoji/EmojiAutocomplete.js.map +1 -1
  43. package/dist/react/folder-tree/FolderPicker.d.ts.map +1 -1
  44. package/dist/react/folder-tree/FolderPicker.js +3 -2
  45. package/dist/react/folder-tree/FolderPicker.js.map +1 -1
  46. package/dist/react/layoutEngine/LayoutCanvas.d.ts.map +1 -1
  47. package/dist/react/layoutEngine/LayoutCanvas.js.map +1 -1
  48. package/dist/react/layoutEngine/NodeInspector.d.ts.map +1 -1
  49. package/dist/react/layoutEngine/NodeInspector.js +2 -1
  50. package/dist/react/layoutEngine/NodeInspector.js.map +1 -1
  51. package/dist/react/media/TrimRangeSlider.d.ts.map +1 -1
  52. package/dist/react/media/TrimRangeSlider.js +3 -1
  53. package/dist/react/media/TrimRangeSlider.js.map +1 -1
  54. package/dist/react/media-gallery/MediaGallery.d.ts +14 -1
  55. package/dist/react/media-gallery/MediaGallery.d.ts.map +1 -1
  56. package/dist/react/media-gallery/MediaGallery.js +11 -7
  57. package/dist/react/media-gallery/MediaGallery.js.map +1 -1
  58. package/dist/react/media-gallery/index.d.ts +1 -1
  59. package/dist/react/media-gallery/index.d.ts.map +1 -1
  60. package/dist/react/media-gallery/index.js +1 -1
  61. package/dist/react/media-gallery/index.js.map +1 -1
  62. package/dist/react/problem-detail/ProblemDetailView.d.ts.map +1 -1
  63. package/dist/react/problem-detail/ProblemDetailView.js +2 -1
  64. package/dist/react/problem-detail/ProblemDetailView.js.map +1 -1
  65. package/dist/scripts/guardrailsEnforce.d.ts.map +1 -1
  66. package/dist/styles-areas/core.css +29 -28
  67. package/dist/styles-utilities.css +9 -0
  68. package/dist/theme.css +11 -0
  69. package/package.json +8 -2
  70. package/scripts/guardrails-baseline.json +2 -1
  71. package/scripts/guardrailsEnforce.spec.ts +36 -0
  72. package/scripts/guardrailsEnforce.ts +66 -0
  73. package/src/react/charts/IntervalTimeline.tsx +2 -0
  74. package/src/react/components/Button.spec.tsx +8 -0
  75. package/src/react/components/Button.tsx +4 -0
  76. package/src/react/components/CodeArea.tsx +1 -0
  77. package/src/react/components/DataTable/DataTable.tsx +4 -4
  78. package/src/react/components/DataTable/TableFilterBuilder.tsx +3 -2
  79. package/src/react/components/FindingsList.tsx +9 -5
  80. package/src/react/components/InfoHint.tsx +1 -0
  81. package/src/react/components/Section.tsx +1 -0
  82. package/src/react/components/calendar/MonthGrid.tsx +3 -1
  83. package/src/react/components/calendar/WeekView.tsx +3 -2
  84. package/src/react/components/sharing/RecipientPicker.tsx +4 -1
  85. package/src/react/dashboard-grid/WidgetFrame.tsx +4 -6
  86. package/src/react/diff-viewer/TextComparePane.tsx +4 -6
  87. package/src/react/emoji/EmojiAutocomplete.tsx +1 -0
  88. package/src/react/folder-tree/FolderPicker.tsx +5 -4
  89. package/src/react/layoutEngine/LayoutCanvas.tsx +1 -0
  90. package/src/react/layoutEngine/NodeInspector.tsx +3 -2
  91. package/src/react/media/TrimRangeSlider.tsx +1 -0
  92. package/src/react/media-gallery/MediaGallery.spec.tsx +22 -0
  93. package/src/react/media-gallery/MediaGallery.tsx +27 -6
  94. package/src/react/media-gallery/index.ts +2 -0
  95. package/src/react/problem-detail/ProblemDetailView.tsx +14 -5
  96. package/src/styles-areas/core.css +29 -28
  97. package/src/styles-utilities.css +9 -0
  98. package/src/theme.css +11 -0
@@ -5,6 +5,7 @@ import { Badge } from '../components/Badge.js';
5
5
  import { CopyButton } from '../components/CopyButton.js';
6
6
  import { PathText } from '../components/PathText.js';
7
7
  import { cn } from '../lib/cn.js';
8
+ import { Surface } from '../components/Surface.js';
8
9
  const EXPLAIN_ROWS = [
9
10
  { key: 'what', label: 'What’s happening', icon: _jsx(AlertTriangle, { className: 'size-4' }) },
10
11
  { key: 'why', label: 'Why', icon: _jsx(HelpCircle, { className: 'size-4' }) },
@@ -12,6 +13,6 @@ const EXPLAIN_ROWS = [
12
13
  { key: 'whatToDo', label: 'What to do', icon: _jsx(Wrench, { className: 'size-4' }) },
13
14
  ];
14
15
  export function ProblemDetailView({ kind, summary, detail, eli5, eli5By, explain, triage, blocking, ready, session, createdAt, actions, className, }) {
15
- return (_jsxs("div", { className: cn('flex select-text flex-col gap-4 text-sm', className), children: [_jsxs("header", { className: 'flex flex-col gap-1', children: [_jsxs("div", { className: 'flex items-start justify-between gap-2', children: [_jsxs("div", { className: 'flex min-w-0 items-center gap-2', children: [_jsx(Badge, { tone: 'outline', className: 'shrink-0 font-mono text-caption', children: kind }), createdAt !== undefined && (_jsx("span", { className: 'text-xs text-muted-foreground', children: formatTimeAgo(createdAt) }))] }), _jsx(CopyButton, { value: summary, label: 'Copy summary', className: 'shrink-0' })] }), _jsx("p", { className: 'font-medium leading-snug text-foreground', children: summary })] }), eli5 && (_jsxs("section", { className: 'rounded-lg border border-accent/30 bg-accent/5 p-3', children: [_jsxs("div", { className: 'mb-1 flex items-center justify-between gap-2', children: [_jsx("span", { className: 'text-caption font-semibold uppercase tracking-wide text-accent-strong', children: "In plain words" }), _jsx(CopyButton, { value: eli5, label: 'Copy explanation', iconSize: 14, className: 'shrink-0' })] }), _jsx("p", { className: 'leading-relaxed text-foreground', children: eli5 }), eli5By && eli5By !== 'deterministic' && (_jsxs("p", { className: 'mt-1 text-caption text-muted-foreground', children: ["explained by ", eli5By] }))] })), explain && (_jsx("section", { className: 'grid gap-2 sm:grid-cols-2', children: EXPLAIN_ROWS.map(({ key, label, icon }) => explain[key] ? (_jsxs("div", { className: 'rounded-lg border border-border bg-card p-3', children: [_jsxs("div", { className: 'mb-1 flex items-center gap-1.5 text-muted-foreground', children: [_jsx("span", { className: 'shrink-0', children: icon }), _jsx("span", { className: 'text-caption font-semibold uppercase tracking-wide', children: label })] }), _jsx("p", { className: 'leading-snug text-foreground', children: explain[key] })] }, key)) : null) })), blocking && (_jsxs("section", { className: 'rounded-lg border border-border p-3', children: [_jsxs("div", { className: 'mb-2 flex flex-wrap items-center gap-2', children: [_jsx("span", { className: 'text-caption font-semibold uppercase tracking-wide text-muted-foreground', children: "Blocking gate" }), blocking.ownerGated && (_jsxs(Badge, { tone: 'warning', className: 'gap-1', children: [_jsx(Lock, { className: 'size-3' }), " needs you"] })), blocking.gateFlags.map((f) => (_jsx(Badge, { tone: 'outline', className: 'font-mono text-caption', children: f }, f)))] }), _jsxs("div", { className: 'flex items-center gap-2', children: [_jsx(PathText, { path: `blocked/${blocking.head.file}`, className: 'min-w-0 flex-1 text-xs' }), _jsx(CopyButton, { value: blocking.head.file, label: 'Copy filename', iconSize: 14, className: 'shrink-0' })] }), blocking.head.brief && (_jsx("p", { className: 'mt-1 text-xs text-muted-foreground', children: blocking.head.brief })), blocking.chain.length > 1 && (_jsxs("p", { className: 'mt-2 text-caption text-muted-foreground', children: ["+", blocking.chain.length - 1, " more gate", blocking.chain.length - 1 === 1 ? '' : 's', " in the blocked chain."] }))] })), ready && ready.length > 0 && (_jsxs("section", { children: [_jsxs("div", { className: 'mb-1.5 flex items-center gap-1.5 text-muted-foreground', children: [_jsx(ListChecks, { className: 'size-4 shrink-0' }), _jsxs("span", { className: 'text-caption font-semibold uppercase tracking-wide', children: [ready.length, " ready, waiting"] })] }), _jsx("ul", { className: 'flex flex-col gap-1', children: ready.slice(0, 12).map((t) => (_jsxs("li", { className: 'flex items-center gap-2 rounded border border-border/60 bg-card/50 px-2 py-1', children: [_jsx("span", { className: 'min-w-0 flex-1 truncate font-mono text-caption text-foreground', children: t.file }), t.unmetNeeds.length > 0 && (_jsxs(Badge, { tone: 'warning', className: 'shrink-0 text-caption', children: ["waits on ", t.unmetNeeds.join(', ')] }))] }, t.file))) }), ready.length > 12 && (_jsxs("p", { className: 'mt-1 text-caption text-muted-foreground', children: ["+", ready.length - 12, " more."] }))] })), session && session.events.length > 0 && (_jsxs("section", { children: [_jsxs("div", { className: 'mb-1.5 flex items-center justify-between gap-2', children: [_jsxs("span", { className: 'text-caption font-semibold uppercase tracking-wide text-muted-foreground', children: ["Session activity \u00B7 ", session.events.length, " event", session.events.length === 1 ? '' : 's'] }), _jsx(CopyButton, { value: session.events.map((e) => `[${e.kind}] ${e.message}`).join('\n'), label: 'Copy event log', iconSize: 14, className: 'shrink-0' })] }), _jsx("ol", { className: 'flex flex-col gap-2 border-l border-border pl-3', children: session.events.map((e) => (_jsxs("li", { className: 'relative', children: [_jsx("span", { "aria-hidden": true, className: 'absolute -left-[calc(0.75rem+3px)] top-1.5 size-1.5 rounded-full bg-muted-foreground' }), _jsxs("div", { className: 'flex items-baseline justify-between gap-2', children: [_jsx("span", { className: 'font-mono text-caption uppercase tracking-wide text-muted-foreground', children: e.kind }), _jsx("span", { className: 'shrink-0 text-caption text-muted-foreground', children: formatTimeAgo(e.at) })] }), _jsx("p", { className: 'whitespace-pre-wrap break-words leading-snug text-foreground', children: e.message })] }, e.seq))) })] })), triage && (_jsxs("section", { className: 'rounded-lg border border-border bg-muted/30 p-3 text-xs', children: [_jsxs("span", { className: 'text-caption font-semibold uppercase tracking-wide text-muted-foreground', children: ["System verdict \u00B7 ", triage.status] }), _jsx("p", { className: 'mt-1 text-foreground', children: triage.cause }), _jsx("p", { className: 'mt-0.5 text-muted-foreground', children: triage.action }), triage.decidedBy && (_jsxs("p", { className: 'mt-1 text-caption text-muted-foreground', children: ["decided by ", triage.decidedBy] }))] })), detail?.trim() && (_jsxs("details", { className: 'rounded-lg border border-border', children: [_jsx("summary", { className: 'cursor-pointer select-none px-3 py-2 text-caption font-semibold uppercase tracking-wide text-muted-foreground', children: "Raw detail" }), _jsxs("div", { className: 'flex items-start gap-2 border-t border-border p-3', children: [_jsx("pre", { className: 'min-w-0 flex-1 whitespace-pre-wrap break-words font-mono text-caption text-muted-foreground', children: detail }), _jsx(CopyButton, { value: detail, label: 'Copy detail', iconSize: 14, className: 'shrink-0' })] })] })), actions && (_jsx("footer", { className: 'flex flex-wrap items-center gap-2 border-t border-border pt-3', children: actions }))] }));
16
+ return (_jsxs("div", { className: cn('flex select-text flex-col gap-4 text-sm', className), children: [_jsxs("header", { className: 'flex flex-col gap-1', children: [_jsxs("div", { className: 'flex items-start justify-between gap-2', children: [_jsxs("div", { className: 'flex min-w-0 items-center gap-2', children: [_jsx(Badge, { tone: 'outline', className: 'shrink-0 font-mono text-caption', children: kind }), createdAt !== undefined && (_jsx("span", { className: 'text-xs text-muted-foreground', children: formatTimeAgo(createdAt) }))] }), _jsx(CopyButton, { value: summary, label: 'Copy summary', className: 'shrink-0' })] }), _jsx("p", { className: 'font-medium leading-snug text-foreground', children: summary })] }), eli5 && (_jsxs("section", { className: 'rounded-lg border border-accent/30 bg-accent/5 p-3', children: [_jsxs("div", { className: 'mb-1 flex items-center justify-between gap-2', children: [_jsx("span", { className: 'text-caption font-semibold uppercase tracking-wide text-accent-strong', children: "In plain words" }), _jsx(CopyButton, { value: eli5, label: 'Copy explanation', iconSize: 14, className: 'shrink-0' })] }), _jsx("p", { className: 'leading-relaxed text-foreground', children: eli5 }), eli5By && eli5By !== 'deterministic' && (_jsxs("p", { className: 'mt-1 text-caption text-muted-foreground', children: ["explained by ", eli5By] }))] })), explain && (_jsx("section", { className: 'grid gap-2 sm:grid-cols-2', children: EXPLAIN_ROWS.map(({ key, label, icon }) => explain[key] ? (_jsxs(Surface, { level: "flat", pad: "sm", radius: "lg", className: 'bg-card', children: [_jsxs("div", { className: 'mb-1 flex items-center gap-1.5 text-muted-foreground', children: [_jsx("span", { className: 'shrink-0', children: icon }), _jsx("span", { className: 'text-caption font-semibold uppercase tracking-wide', children: label })] }), _jsx("p", { className: 'leading-snug text-foreground', children: explain[key] })] }, key)) : null) })), blocking && (_jsxs("section", { className: 'rounded-lg border border-border p-3', children: [_jsxs("div", { className: 'mb-2 flex flex-wrap items-center gap-2', children: [_jsx("span", { className: 'text-caption font-semibold uppercase tracking-wide text-muted-foreground', children: "Blocking gate" }), blocking.ownerGated && (_jsxs(Badge, { tone: 'warning', className: 'gap-1', children: [_jsx(Lock, { className: 'size-3' }), " needs you"] })), blocking.gateFlags.map((f) => (_jsx(Badge, { tone: 'outline', className: 'font-mono text-caption', children: f }, f)))] }), _jsxs("div", { className: 'flex items-center gap-2', children: [_jsx(PathText, { path: `blocked/${blocking.head.file}`, className: 'min-w-0 flex-1 text-xs' }), _jsx(CopyButton, { value: blocking.head.file, label: 'Copy filename', iconSize: 14, className: 'shrink-0' })] }), blocking.head.brief && (_jsx("p", { className: 'mt-1 text-xs text-muted-foreground', children: blocking.head.brief })), blocking.chain.length > 1 && (_jsxs("p", { className: 'mt-2 text-caption text-muted-foreground', children: ["+", blocking.chain.length - 1, " more gate", blocking.chain.length - 1 === 1 ? '' : 's', " in the blocked chain."] }))] })), ready && ready.length > 0 && (_jsxs("section", { children: [_jsxs("div", { className: 'mb-1.5 flex items-center gap-1.5 text-muted-foreground', children: [_jsx(ListChecks, { className: 'size-4 shrink-0' }), _jsxs("span", { className: 'text-caption font-semibold uppercase tracking-wide', children: [ready.length, " ready, waiting"] })] }), _jsx("ul", { className: 'flex flex-col gap-1', children: ready.slice(0, 12).map((t) => (_jsx(Surface, { asChild: true, level: 'flat', pad: 'none', radius: 'md', className: 'flex items-center gap-2 rounded border-border/60 bg-card/50 px-2 py-1', children: _jsxs("li", { children: [_jsx("span", { className: 'min-w-0 flex-1 truncate font-mono text-caption text-foreground', children: t.file }), t.unmetNeeds.length > 0 && (_jsxs(Badge, { tone: 'warning', className: 'shrink-0 text-caption', children: ["waits on ", t.unmetNeeds.join(', ')] }))] }) }, t.file))) }), ready.length > 12 && (_jsxs("p", { className: 'mt-1 text-caption text-muted-foreground', children: ["+", ready.length - 12, " more."] }))] })), session && session.events.length > 0 && (_jsxs("section", { children: [_jsxs("div", { className: 'mb-1.5 flex items-center justify-between gap-2', children: [_jsxs("span", { className: 'text-caption font-semibold uppercase tracking-wide text-muted-foreground', children: ["Session activity \u00B7 ", session.events.length, " event", session.events.length === 1 ? '' : 's'] }), _jsx(CopyButton, { value: session.events.map((e) => `[${e.kind}] ${e.message}`).join('\n'), label: 'Copy event log', iconSize: 14, className: 'shrink-0' })] }), _jsx("ol", { className: 'flex flex-col gap-2 border-l border-border pl-3', children: session.events.map((e) => (_jsxs("li", { className: 'relative', children: [_jsx("span", { "aria-hidden": true, className: 'absolute -left-[calc(0.75rem+3px)] top-1.5 size-1.5 rounded-full bg-muted-foreground' }), _jsxs("div", { className: 'flex items-baseline justify-between gap-2', children: [_jsx("span", { className: 'font-mono text-caption uppercase tracking-wide text-muted-foreground', children: e.kind }), _jsx("span", { className: 'shrink-0 text-caption text-muted-foreground', children: formatTimeAgo(e.at) })] }), _jsx("p", { className: 'whitespace-pre-wrap break-words leading-snug text-foreground', children: e.message })] }, e.seq))) })] })), triage && (_jsx(Surface, { asChild: true, level: 'flat', pad: 'sm', radius: 'lg', className: 'bg-muted/30 text-xs', children: _jsxs("section", { children: [_jsxs("span", { className: 'text-caption font-semibold uppercase tracking-wide text-muted-foreground', children: ["System verdict \u00B7 ", triage.status] }), _jsx("p", { className: 'mt-1 text-foreground', children: triage.cause }), _jsx("p", { className: 'mt-0.5 text-muted-foreground', children: triage.action }), triage.decidedBy && (_jsxs("p", { className: 'mt-1 text-caption text-muted-foreground', children: ["decided by ", triage.decidedBy] }))] }) })), detail?.trim() && (_jsxs("details", { className: 'rounded-lg border border-border', children: [_jsx("summary", { className: 'cursor-pointer select-none px-3 py-2 text-caption font-semibold uppercase tracking-wide text-muted-foreground', children: "Raw detail" }), _jsxs("div", { className: 'flex items-start gap-2 border-t border-border p-3', children: [_jsx("pre", { className: 'min-w-0 flex-1 whitespace-pre-wrap break-words font-mono text-caption text-muted-foreground', children: detail }), _jsx(CopyButton, { value: detail, label: 'Copy detail', iconSize: 14, className: 'shrink-0' })] })] })), actions && (_jsx("footer", { className: 'flex flex-wrap items-center gap-2 border-t border-border pt-3', children: actions }))] }));
16
17
  }
17
18
  //# sourceMappingURL=ProblemDetailView.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProblemDetailView.js","sourceRoot":"","sources":["../../../src/react/problem-detail/ProblemDetailView.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ProblemDetailView.js","sourceRoot":"","sources":["../../../src/react/problem-detail/ProblemDetailView.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"guardrailsEnforce.d.ts","sourceRoot":"","sources":["../../scripts/guardrailsEnforce.ts"],"names":[],"mappings":";AA2NA,UAAU,SAAS;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB;;;sEAGkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,IAAI;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;CACzD;AAED,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE,CA6B9D;AAu5BD,iGAAiG;AACjG,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAE3D;AAmHD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAEpE;AA+vBD,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,GAAG,IAAI,CAEpF;AAID,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CAoBhE;AA8CD,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAwBtC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,WAAW,CAAC,MAAM,CAAuC,CAAC;AAE5F,0EAA0E;AAC1E,eAAO,MAAM,SAAS,EAAE,SAAS,MAAM,EAAwE,CAAC;AAShH,eAAO,MAAM,uBAAuB,aAAkD,CAAC;AAGvF,MAAM,WAAW,SAAS;IACzB,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACb;AAED;2FAC2F;AAC3F,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CA0C3F;AAED,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAmGhC,wBAAgB,0BAA0B,CAAC,SAAS,GAAE,MAAwB,GAAG,MAAM,EAAE,CAyBxF"}
1
+ {"version":3,"file":"guardrailsEnforce.d.ts","sourceRoot":"","sources":["../../scripts/guardrailsEnforce.ts"],"names":[],"mappings":";AA2NA,UAAU,SAAS;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB;;;sEAGkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,IAAI;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;CACzD;AAED,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE,CA6B9D;AAu5BD,iGAAiG;AACjG,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAE3D;AAmHD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAEpE;AA+vBD,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,GAAG,IAAI,CAEpF;AAID,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CAoBhE;AA+GD,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAyBtC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,WAAW,CAAC,MAAM,CAAuC,CAAC;AAE5F,0EAA0E;AAC1E,eAAO,MAAM,SAAS,EAAE,SAAS,MAAM,EAAwE,CAAC;AAShH,eAAO,MAAM,uBAAuB,aAAkD,CAAC;AAGvF,MAAM,WAAW,SAAS;IACzB,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACb;AAED;2FAC2F;AAC3F,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CA0C3F;AAED,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAmGhC,wBAAgB,0BAA0B,CAAC,SAAS,GAAE,MAAwB,GAAG,MAAM,EAAE,CAyBxF"}
@@ -15,7 +15,7 @@
15
15
  * after an app's responsive variant (task 2124). An app that imports every area gets exactly
16
16
  * what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
17
17
  * src/stylesAreas.spec.ts, which is also what fails when this file is stale.
18
- * 715 candidate(s).
18
+ * 718 candidate(s).
19
19
  *
20
20
  * Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
21
21
  * `bun run build` runs. The map of what is in which area, and why the areas are
@@ -43,21 +43,21 @@
43
43
  @source inline("bg-background bg-background/80 bg-background/90 bg-background/95 bg-black/50 bg-border bg-card");
44
44
  @source inline("bg-current bg-destructive bg-destructive-solid bg-destructive/15 bg-destructive/5");
45
45
  @source inline("bg-destructive/8 bg-emerald-100 bg-field-bg bg-muted bg-muted-foreground/40");
46
- @source inline("bg-muted-foreground/50 bg-muted/20 bg-popover bg-success bg-success/15 bg-success/5 bg-surface");
47
- @source inline("bg-surface-sunken bg-tone-amber-bg bg-tone-cyan-bg bg-tone-emerald-bg bg-tone-fuchsia-bg");
48
- @source inline("bg-tone-lime-bg bg-tone-orange-bg bg-tone-purple-bg bg-tone-rose-bg bg-tone-sky-bg");
49
- @source inline("bg-tone-slate-bg bg-tone-teal-bg bg-tone-violet-bg bg-transparent bg-warning bg-warning/15");
50
- @source inline("bg-warning/5 bg-warning/8 bg-white block blur border border-accent/40 border-accent/50 border-b");
51
- @source inline("border-b-2 border-b-[7px] border-b-current border-border border-border-strong border-border/60");
52
- @source inline("border-collapse border-current border-destructive/30 border-destructive/40 border-destructive/50");
53
- @source inline("border-field-border border-success/30 border-success/40 border-success/50 border-t");
54
- @source inline("border-tone-sky-fg/30 border-tone-slate-fg/30 border-transparent border-warning/30");
55
- @source inline("border-warning/40 border-warning/50 border-x-4 border-x-transparent bot bottom bottom-0 break");
56
- @source inline("break-all break-words c ca cap capitalize co com contain container content contents cursor");
57
- @source inline("cursor-col-resize cursor-default cursor-ew-resize cursor-pointer cursor-row-resize d dark");
58
- @source inline("dark:bg-card-overlay dark:bg-card-raised dark:bg-gray-800 data data-[disabled]:hover:bg-border");
59
- @source inline("data-[disabled]:opacity-50 data-[disabled]:pointer-events-none");
60
- @source inline("data-[interactive=true]:duration-[var(--duration-base)]");
46
+ @source inline("bg-muted-foreground/50 bg-muted/20 bg-popover bg-success bg-success-solid bg-success/15");
47
+ @source inline("bg-success/5 bg-surface bg-surface-sunken bg-tone-amber-bg bg-tone-cyan-bg bg-tone-emerald-bg");
48
+ @source inline("bg-tone-fuchsia-bg bg-tone-lime-bg bg-tone-orange-bg bg-tone-purple-bg bg-tone-rose-bg");
49
+ @source inline("bg-tone-sky-bg bg-tone-slate-bg bg-tone-teal-bg bg-tone-violet-bg bg-transparent bg-warning");
50
+ @source inline("bg-warning/15 bg-warning/5 bg-warning/8 bg-white block blur border border-accent/40");
51
+ @source inline("border-accent/50 border-b border-b-2 border-b-[7px] border-b-current border-border");
52
+ @source inline("border-border-strong border-border/60 border-collapse border-current border-destructive/30");
53
+ @source inline("border-destructive/40 border-destructive/50 border-field-border border-success/30");
54
+ @source inline("border-success/40 border-success/50 border-t border-tone-sky-fg/30 border-tone-slate-fg/30");
55
+ @source inline("border-transparent border-warning/30 border-warning/40 border-warning/50 border-x-4");
56
+ @source inline("border-x-transparent bot bottom bottom-0 break break-all break-words c ca cap capitalize co com");
57
+ @source inline("contain container content contents cursor cursor-col-resize cursor-default cursor-ew-resize");
58
+ @source inline("cursor-pointer cursor-row-resize d dark dark:bg-card-overlay dark:bg-card-raised");
59
+ @source inline("dark:bg-gray-800 data data-[disabled]:hover:bg-border data-[disabled]:opacity-50");
60
+ @source inline("data-[disabled]:pointer-events-none data-[interactive=true]:duration-[var(--duration-base)]");
61
61
  @source inline("data-[interactive=true]:ease-[var(--ease-out)] data-[interactive=true]:hover:-translate-y-0.5");
62
62
  @source inline("data-[interactive=true]:hover:border-border-strong data-[interactive=true]:hover:shadow-overlay");
63
63
  @source inline("data-[interactive=true]:transition-all data-[placeholder]:text-muted-foreground");
@@ -86,11 +86,11 @@
86
86
  @source inline("focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent");
87
87
  @source inline("focus-visible:ring-accent-solid focus-visible:ring-destructive focus-visible:ring-field-ring/30");
88
88
  @source inline("focus-visible:ring-offset-1 focus-visible:ring-offset-2 focus-visible:ring-offset-background");
89
- @source inline("focus-visible:ring-ring focus-visible:text-destructive-foreground focus:bg-accent/10");
90
- @source inline("focus:bg-destructive/10 focus:outline-none focus:ring-2 focus:ring-accent focus:text-accent");
91
- @source inline("focus:text-destructive font-bold font-medium font-mono font-normal font-semibold gap gap-0");
92
- @source inline("gap-0.5 gap-1 gap-1.5 gap-12 gap-16 gap-2 gap-3 gap-4 gap-6 gap-8 gap-x-2.5 gap-x-3 gap-y-1");
93
- @source inline("gap-y-2 gap-y-4 grid grid-cols-2");
89
+ @source inline("focus-visible:ring-ring focus-visible:ring-success focus-visible:text-destructive-foreground");
90
+ @source inline("focus:bg-accent/10 focus:bg-destructive/10 focus:outline-none focus:ring-2 focus:ring-accent");
91
+ @source inline("focus:text-accent focus:text-destructive font-bold font-medium font-mono font-normal");
92
+ @source inline("font-semibold gap gap-0 gap-0.5 gap-1 gap-1.5 gap-12 gap-16 gap-2 gap-3 gap-4 gap-6 gap-8");
93
+ @source inline("gap-x-2.5 gap-x-3 gap-y-1 gap-y-2 gap-y-4 grid grid-cols-2");
94
94
  @source inline("grid-cols-[repeat(auto-fit,minmax(min(var(--cb-grid-min),100%),1fr))] group");
95
95
  @source inline("group-active/sep:opacity-100 group-data-[state=open]:rotate-180");
96
96
  @source inline("group-focus-visible/sep:opacity-100 group-hover group-hover/path:opacity-100");
@@ -99,13 +99,14 @@
99
99
  @source inline("h-[var(--control-h-lg)] h-[var(--control-h-md)] h-[var(--control-h-sm)] h-auto h-full h-px hi");
100
100
  @source inline("hid hidden hover hover:bg-accent hover:bg-accent-solid-hover hover:bg-accent/10");
101
101
  @source inline("hover:bg-accent/20 hover:bg-destructive-solid hover:bg-destructive-solid-hover hover:bg-muted");
102
- @source inline("hover:bg-transparent hover:border-accent hover:border-destructive-solid hover:opacity-70");
103
- @source inline("hover:text-accent hover:text-destructive hover:text-destructive-foreground hover:text-foreground");
104
- @source inline("hover:underline i in inline inline-block inline-flex inset inset-0 inset-x-0 inset-y-0 invisible");
105
- @source inline("is iso isolate it item items items-baseline items-center items-end items-start just");
106
- @source inline("justify-between justify-center justify-end l leading leading-none leading-relaxed leading-snug");
107
- @source inline("left left-1/2 left-2 lg lg:px-8 lower lowercase m m-0 max max-h max-h-[80vh]");
108
- @source inline("max-h-[calc(100dvh-2rem)] max-h-[calc(var(--radix-popper-available-height,100dvh)-1rem)]");
102
+ @source inline("hover:bg-success-solid-hover hover:bg-transparent hover:border-accent");
103
+ @source inline("hover:border-destructive-solid hover:opacity-70 hover:text-accent hover:text-destructive");
104
+ @source inline("hover:text-destructive-foreground hover:text-foreground hover:underline i in inline inline-block");
105
+ @source inline("inline-flex inset inset-0 inset-x-0 inset-y-0 invisible is iso isolate it item items");
106
+ @source inline("items-baseline items-center items-end items-start just justify-between justify-center");
107
+ @source inline("justify-end l leading leading-none leading-relaxed leading-snug left left-1/2 left-2 lg lg:px-8");
108
+ @source inline("lower lowercase m m-0 max max-h max-h-[80vh] max-h-[calc(100dvh-2rem)]");
109
+ @source inline("max-h-[calc(var(--radix-popper-available-height,100dvh)-1rem)]");
109
110
  @source inline("max-h-[min(90dvh,calc(100dvh-2rem))] max-h-[var(--radix-select-content-available-height)]");
110
111
  @source inline("max-w-2xl max-w-4xl max-w-5xl max-w-[10rem] max-w-[calc(100vw-2rem)]");
111
112
  @source inline("max-w-[var(--reading-width)] max-w-full max-w-lg max-w-sm max-w-xl max-w-xs mb-0.5 mb-1 md");
@@ -2098,6 +2098,9 @@
2098
2098
  .bg-success {
2099
2099
  background-color: var(--success);
2100
2100
  }
2101
+ .bg-success-solid {
2102
+ background-color: var(--success-solid);
2103
+ }
2101
2104
  .bg-success\/5 {
2102
2105
  background-color: var(--success);
2103
2106
  @supports (color: color-mix(in lab, red, red)) {
@@ -3520,6 +3523,9 @@
3520
3523
  background-color: color-mix(in oklab, var(--muted) 80%, transparent);
3521
3524
  }
3522
3525
  }
3526
+ .hover\:bg-success-solid-hover:hover {
3527
+ background-color: var(--success-solid-hover);
3528
+ }
3523
3529
  .hover\:bg-transparent:hover {
3524
3530
  background-color: transparent;
3525
3531
  }
@@ -3667,6 +3673,9 @@
3667
3673
  .focus-visible\:ring-ring:focus-visible {
3668
3674
  --tw-ring-color: var(--ring);
3669
3675
  }
3676
+ .focus-visible\:ring-success:focus-visible {
3677
+ --tw-ring-color: var(--success);
3678
+ }
3670
3679
  .focus-visible\:ring-offset-1:focus-visible {
3671
3680
  --tw-ring-offset-width: 1px;
3672
3681
  --tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
package/dist/theme.css CHANGED
@@ -169,6 +169,13 @@
169
169
  --destructive-solid: var(--destructive); /* guardrails-ignore no-hardcoded-colors: this file IS the theme token source of truth — every value here is necessarily a literal color definition */
170
170
  --destructive-solid-hover: #991b1b; /* guardrails-ignore no-hardcoded-colors: this file IS the theme token source of truth — every value here is necessarily a literal color definition */
171
171
  --success: #16a34a; /* guardrails-ignore no-hardcoded-colors: this file IS the theme token source of truth — every value here is necessarily a literal color definition */
172
+ /* Solid-fill success, for a green button carrying WHITE text — the `destructive-solid`
173
+ split, for the same reason: --success is tuned as a status colour, and white on it
174
+ is 3.3:1. green-700 is 5.02:1 with white and its hover 7.1:1, in both modes, so it
175
+ is mode-stable like --accent-solid. First consumer: station's Resume, the green half
176
+ of a run/pause toggle whose Pause is `destructive-solid` (owner, 2026-09-29). */
177
+ --success-solid: #15803d; /* guardrails-ignore no-hardcoded-colors: this file IS the theme token source of truth — every value here is necessarily a literal color definition */
178
+ --success-solid-hover: #166534; /* guardrails-ignore no-hardcoded-colors: this file IS the theme token source of truth — every value here is necessarily a literal color definition */
172
179
  --warning: #d97706; /* guardrails-ignore no-hardcoded-colors: this file IS the theme token source of truth — every value here is necessarily a literal color definition */
173
180
  /* The gold a filled star is painted in (StarRating). Light needs a DARKER gold
174
181
  than dark mode does, and this is measured, not taste: the neon below scores
@@ -470,6 +477,8 @@
470
477
  --destructive-solid: #dc2626; /* guardrails-ignore no-hardcoded-colors: this file IS the theme token source of truth — every value here is necessarily a literal color definition */
471
478
  --destructive-solid-hover: #b91c1c; /* guardrails-ignore no-hardcoded-colors: this file IS the theme token source of truth — every value here is necessarily a literal color definition */
472
479
  --success: #22c55e; /* guardrails-ignore no-hardcoded-colors: this file IS the theme token source of truth — every value here is necessarily a literal color definition */
480
+ --success-solid: #15803d; /* guardrails-ignore no-hardcoded-colors: this file IS the theme token source of truth — every value here is necessarily a literal color definition */
481
+ --success-solid-hover: #166534; /* guardrails-ignore no-hardcoded-colors: this file IS the theme token source of truth — every value here is necessarily a literal color definition */
473
482
  --warning: #f59e0b; /* guardrails-ignore no-hardcoded-colors: this file IS the theme token source of truth — every value here is necessarily a literal color definition */
474
483
  /* The neon the star always had — correct against a near-black canvas at 10.76:1,
475
484
  and the half of the pair that never needed changing. See the note at :root. */
@@ -845,6 +854,8 @@ option:disabled {
845
854
  --color-destructive-solid: var(--destructive-solid);
846
855
  --color-destructive-solid-hover: var(--destructive-solid-hover);
847
856
  --color-success: var(--success);
857
+ --color-success-solid: var(--success-solid);
858
+ --color-success-solid-hover: var(--success-solid-hover);
848
859
  --color-warning: var(--warning);
849
860
  --color-star: var(--star);
850
861
  /* AA-safe status TEXT over the 15% tinted badge fills */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursedbelt",
3
- "version": "5.26.0",
3
+ "version": "5.28.0",
4
4
  "license": "ISC",
5
5
  "type": "module",
6
6
  "description": "The React design system of the cursedbelt split — components, styles, theme. cursedbelt-core below it; server tier in cursedbelt-server.",
@@ -68,7 +68,7 @@
68
68
  "typecheck:scripts": "tsc --noEmit --project tsconfig.scripts.json",
69
69
  "demo:check": "tsc --noEmit --project demo/tsconfig.json",
70
70
  "demo:paths": "bun run demo/generatePaths.ts --write",
71
- "guardrails:report": "bun run scripts/guardrailsEnforce.ts no-raw-action-button,no-raw-overflow,no-raw-inputs,no-hardcoded-colors,no-raw-modal-footer,no-unguarded-editing-modal,control-height-tokens,no-raw-zindex,no-raw-data-table,no-tailwind-palette,no-raw-title-attr,micro-typography-floor,choice-needs-empty-state,no-select-as-action-menu,no-native-dialog,no-fast-tooltip,no-raw-password,no-raw-lazy,no-raw-popover,no-hand-rolled-scroll-memory --scope cursedbelt src/react src/styles.css",
71
+ "guardrails:report": "bun run scripts/guardrailsEnforce.ts no-raw-action-button,no-raw-overflow,no-raw-inputs,no-hardcoded-colors,no-raw-modal-footer,no-unguarded-editing-modal,control-height-tokens,no-raw-zindex,no-raw-data-table,no-tailwind-palette,no-raw-title-attr,micro-typography-floor,choice-needs-empty-state,no-select-as-action-menu,no-native-dialog,no-fast-tooltip,no-raw-password,no-raw-lazy,no-raw-popover,no-hand-rolled-scroll-memory,no-hand-rolled-surface --scope cursedbelt src/react src/styles.css",
72
72
  "lint": "biome lint --error-on-warnings src e2e demo scripts",
73
73
  "styles": "bun run styles:static && bun run styles:utilities && bun run styles:areas",
74
74
  "styles:static": "bun run scripts/generateStaticStyles.ts",
@@ -391,6 +391,12 @@
391
391
  "source": "./src/react/components/MultiSelectField.tsx",
392
392
  "import": "./dist/react/components/MultiSelectField.js"
393
393
  },
394
+ "./react/segmented-control": {
395
+ "types": "./dist/react/components/SegmentedControl.d.ts",
396
+ "bun": "./src/react/components/SegmentedControl.tsx",
397
+ "source": "./src/react/components/SegmentedControl.tsx",
398
+ "import": "./dist/react/components/SegmentedControl.js"
399
+ },
394
400
  "./react/sharing": {
395
401
  "types": "./dist/react/sharing/index.d.ts",
396
402
  "bun": "./src/react/sharing/index.ts",
@@ -16,7 +16,8 @@
16
16
  "no-fast-tooltip": 0,
17
17
  "no-raw-lazy": 0,
18
18
  "no-raw-popover": 0,
19
- "no-hand-rolled-scroll-memory": 0
19
+ "no-hand-rolled-scroll-memory": 0,
20
+ "no-hand-rolled-surface": 0
20
21
  },
21
22
  "cursedshell": {
22
23
  "no-raw-action-button": 0,
@@ -1628,3 +1628,39 @@ describe('no-raw-popover — a popup its stylesheet positions (the 2026-09-27 su
1628
1628
  ).toHaveLength(0);
1629
1629
  });
1630
1630
  });
1631
+
1632
+ describe('no-hand-rolled-surface', () => {
1633
+ test('🔴 flags a hand-classed card — the shape family carried 23 of (2026-09-29)', () => {
1634
+ expect(
1635
+ check('no-hand-rolled-surface', `<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-3">{body}</div>`),
1636
+ ).toHaveLength(1);
1637
+ expect(check('no-hand-rolled-surface', `<li className="rounded-lg border border-border bg-card p-4">`)).toHaveLength(1);
1638
+ expect(
1639
+ check('no-hand-rolled-surface', `<section className={cn("rounded-md border bg-[var(--muted)]/10 p-3", open && "ring")}>`),
1640
+ ).toHaveLength(1);
1641
+ });
1642
+
1643
+ test('passes Surface, a control, a hover-only fill, a drop target and a stated reason', () => {
1644
+ expect(check('no-hand-rolled-surface', `<Surface level="raised" pad="sm">{body}</Surface>`)).toHaveLength(0);
1645
+ expect(
1646
+ check('no-hand-rolled-surface', `<button className="rounded-lg border border-[var(--border)] bg-[var(--card)] px-3">Go</button>`),
1647
+ ).toHaveLength(0);
1648
+ expect(
1649
+ check('no-hand-rolled-surface', `<div className="rounded-lg border border-transparent px-2 hover:border-[var(--border)] hover:bg-[var(--card)]">`),
1650
+ ).toHaveLength(0);
1651
+ expect(
1652
+ check('no-hand-rolled-surface', `<div className="rounded-xl border-2 border-dashed border-[var(--border)] bg-[var(--card)]">`),
1653
+ ).toHaveLength(0);
1654
+ expect(
1655
+ check(
1656
+ 'no-hand-rolled-surface',
1657
+ `{/* guardrails-ignore no-hand-rolled-surface: the map's HUD is positioned over a canvas */}
1658
+ <div className="absolute rounded-lg border bg-[var(--card)]">`,
1659
+ ),
1660
+ ).toHaveLength(0);
1661
+ });
1662
+
1663
+ test('a comment that merely quotes the classes is not a violation', () => {
1664
+ expect(check('no-hand-rolled-surface', `// was <div className="rounded-xl border bg-card">`)).toHaveLength(0);
1665
+ });
1666
+ });
@@ -2141,6 +2141,71 @@ function localComponentRootIsPositioned(source: string, scannable: string, name:
2141
2141
  return FLOATING_POSITION_RE.test(tag.code) || completesPositioned(classTokens(tag.code));
2142
2142
  }
2143
2143
 
2144
+ // ── no-hand-rolled-surface ──────────────────────────────────────────────────
2145
+ // The owner, 2026-09-29 (`full-move-to-cb-review`): *"apps keep turning up with hand rolled
2146
+ // components that are buggy or bad Ux … containers, sidebars, sections … Most layout related
2147
+ // components are better in cb."* `no-local-reimplementation` catches a component declared under a
2148
+ // cursedbelt NAME; a card is usually not a component at all but a class string —
2149
+ // `<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-3">` — which has no
2150
+ // name to match, compiles, and looks nearly right. Music carried a dozen of them past a zero
2151
+ // baseline until the owner found them by eye (task 2276); family carried 24.
2152
+ //
2153
+ // The signal: a CONTAINER element (not a control — a bordered button or input is the control's
2154
+ // own look) whose static className rounds its corners, draws a real border AND fills with a
2155
+ // surface colour. That triple is the whole definition of `Surface` (`level`, `radius`, `pad`,
2156
+ // `interactive`, `asChild` for a `<section>`/`<li>`). A dashed border is a drop target
2157
+ // (`FileDropZone`), and is left to that component's review.
2158
+ const SURFACE_TAG_RE = /<(div|section|li|ul|ol|article|aside|header|footer|form|fieldset)\b/g;
2159
+ const SURFACE_IGNORE_RE = /guardrails-ignore\s+no-hand-rolled-surface\s*:/;
2160
+ const SURFACE_ROUNDED_RE = /^rounded(?:-(?:sm|md|lg|xl|2xl|3xl))?$/;
2161
+ const SURFACE_BORDER_RE = /^border(?:-[a-z0-9-]+|-\[[^\]]+\])?$/;
2162
+ const SURFACE_NO_BORDER_RE = /^border-(?:0|transparent|none|dashed|dotted)$/;
2163
+ const SURFACE_FILL_RE =
2164
+ /^bg-(?:\[var\(--(?:card|surface|muted|popover)[\w-]*\)\](?:\/\d+)?|(?:card|surface|muted|popover)(?:-[\w-]+)?(?:\/\d+)?)$/;
2165
+
2166
+ /** Every whitespace-separated token inside the string literals of a tag's `className` value. */
2167
+ function classLiteralTokens(tagCode: string): string[] {
2168
+ const at = tagCode.search(/\bclassName=/);
2169
+ if (at === -1) return [];
2170
+ let value = tagCode.slice(at + "className=".length);
2171
+ if (value.startsWith("{")) value = balancedBraces(value, 0);
2172
+ else value = value.match(/^(["'])([^"']*)\1/)?.[0] ?? "";
2173
+ const tokens: string[] = [];
2174
+ for (const lit of value.matchAll(/(["'`])((?:(?!\1)[^\\]|\\.)*)\1/g))
2175
+ for (const token of (lit[2] ?? "").replace(/\$\{[^}]*\}/g, " ").split(/\s+/)) if (token) tokens.push(token);
2176
+ return tokens;
2177
+ }
2178
+
2179
+ const noHandRolledSurface: Rule = {
2180
+ name: "no-hand-rolled-surface",
2181
+ description:
2182
+ "Owner, 2026-09-29: containers come from cursedbelt. A container element whose className rounds, borders AND fills with a surface colour is a hand-rolled card — use `Surface` (`level=\"raised\"` for a card, `flat` for a panel, `asChild` to keep a `<section>`/`<li>`), `Section` for a titled group, or `Alert` for a tinted notice. A frame that is genuinely none of those takes the opt-out with its reason.",
2183
+ check(filePath, source) {
2184
+ const normalized = filePath.replace(/\\/g, "/");
2185
+ if (isTestSource(normalized)) return [];
2186
+ const scannable = blankComments(source);
2187
+ const lines = source.split("\n");
2188
+ const out: Violation[] = [];
2189
+ SURFACE_TAG_RE.lastIndex = 0;
2190
+ for (const match of scannable.matchAll(SURFACE_TAG_RE)) {
2191
+ const tag = readOpeningTag(source, match.index ?? 0);
2192
+ const tokens = classLiteralTokens(tag.code);
2193
+ if (tokens.length === 0) continue;
2194
+ const bare = tokens.map((t) => t.replace(/^(?:[a-z0-9-]+:)+/, ""));
2195
+ // Only the UNPREFIXED classes define the resting look: `hover:bg-card` is a row's hover.
2196
+ const resting = tokens.filter((t) => !t.includes(":"));
2197
+ if (bare.some((t) => SURFACE_NO_BORDER_RE.test(t))) continue;
2198
+ if (!resting.some((t) => SURFACE_ROUNDED_RE.test(t))) continue;
2199
+ if (!resting.some((t) => SURFACE_BORDER_RE.test(t) && !SURFACE_NO_BORDER_RE.test(t))) continue;
2200
+ if (!resting.some((t) => SURFACE_FILL_RE.test(t))) continue;
2201
+ const line = lineOf(source, match.index ?? 0);
2202
+ if (SURFACE_IGNORE_RE.test(tag.text) || SURFACE_IGNORE_RE.test(blockAbove(lines, line))) continue;
2203
+ out.push({ file: filePath, line, snippet: lines[line - 1]?.trim().slice(0, 160) ?? "" });
2204
+ }
2205
+ return out;
2206
+ },
2207
+ };
2208
+
2144
2209
  export const RULES: Record<string, Rule> = {
2145
2210
  [noRawActionButton.name]: noRawActionButton,
2146
2211
  [noRawOverflow.name]: noRawOverflow,
@@ -2165,6 +2230,7 @@ export const RULES: Record<string, Rule> = {
2165
2230
  [noRawLazy.name]: noRawLazy,
2166
2231
  [noRawPopover.name]: noRawPopover,
2167
2232
  [noHandRolledScrollMemory.name]: noHandRolledScrollMemory,
2233
+ [noHandRolledSurface.name]: noHandRolledSurface,
2168
2234
  };
2169
2235
 
2170
2236
  /**
@@ -137,6 +137,7 @@ export function IntervalTimeline({
137
137
  return (
138
138
  <div ref={rootRef} className={cn('flex min-w-0 flex-col gap-1.5', className)}>
139
139
  <div
140
+ // guardrails-ignore no-hand-rolled-surface: a chart's plot well (sunken, fixed height, drawn into) — an instrument surface, not a card
140
141
  role='img'
141
142
  aria-label={ariaLabel}
142
143
  className='relative w-full overflow-hidden rounded-md border border-border bg-surface-sunken'
@@ -211,6 +212,7 @@ export function IntervalTimeline({
211
212
 
212
213
  {showConcurrency && peak > 0 && (
213
214
  <div
215
+ // guardrails-ignore no-hand-rolled-surface: the concurrency strip's plot well — an instrument surface, not a card
214
216
  className='relative h-8 w-full overflow-hidden rounded-md border border-border bg-surface-sunken'
215
217
  role='img'
216
218
  aria-label={`How many ran at once, peaking at ${peak}`}
@@ -49,6 +49,14 @@ describe('Button', () => {
49
49
  expect(cls).toMatch(/(^|\s)bg-destructive-solid(\s|$)/);
50
50
  });
51
51
 
52
+ test('success-solid is the green twin of destructive-solid — a solid fill carrying white text', () => {
53
+ const { getByRole } = render(<Button tooltip='R' variant='success-solid'>Resume</Button>);
54
+ const cls = getByRole('button').className;
55
+ expect(cls).toMatch(/(^|\s)bg-success-solid(\s|$)/);
56
+ expect(cls).toContain('text-white');
57
+ expect(cls).toContain('hover:bg-success-solid-hover');
58
+ });
59
+
52
60
  test('tertiary is a first-class variant rendering the ghost weight', () => {
53
61
  const { getByRole, rerender } = render(<Button tooltip='T' variant='tertiary'>T</Button>);
54
62
  const tertiaryCls = getByRole('button').className;
@@ -47,6 +47,9 @@ export const buttonVariants = cva(
47
47
  // destructive-solid — always-loud solid red for the rare surface whose
48
48
  // hero action IS destruction (a confirm dialog's confirm,
49
49
  // an emergency stop). ≤1 per view, like primary.
50
+ // success-solid — its green twin, for the "go" half of a state toggle whose
51
+ // "stop" half is destructive-solid (a runner's Resume beside its
52
+ // Pause): the colour says what the press DOES. Not a second primary.
50
53
  variant: {
51
54
  primary:
52
55
  'bg-accent-solid text-white hover:bg-accent-solid-hover focus-visible:ring-accent-solid',
@@ -71,6 +74,7 @@ export const buttonVariants = cva(
71
74
  'border border-destructive/50 bg-card text-destructive shadow-raised hover:border-destructive-solid hover:bg-destructive-solid hover:text-destructive-foreground focus-visible:ring-destructive',
72
75
  'destructive-solid':
73
76
  'bg-destructive-solid text-destructive-foreground hover:bg-destructive-solid-hover focus-visible:ring-destructive',
77
+ 'success-solid': 'bg-success-solid text-white hover:bg-success-solid-hover focus-visible:ring-success',
74
78
  },
75
79
  // Height is LOCKED to the control-height token scale (theme.css `--control-h-*`,
76
80
  // task cbux-41) — padding is horizontal-only, never vertical, so Button/Input/
@@ -88,6 +88,7 @@ export function CodeArea({
88
88
 
89
89
  return (
90
90
  <div
91
+ // guardrails-ignore no-hand-rolled-surface: a code INPUT's well — it carries the field focus ring (focus-within), which makes it a control, not a surface
91
92
  className={cn(
92
93
  'group relative flex min-h-0 flex-col overflow-hidden rounded-md border border-border bg-surface-sunken',
93
94
  // The textarea's own outline is suppressed (it would clip inside the scroll
@@ -1702,9 +1702,9 @@ export function DataTable<TRow extends Record<string, unknown>>({
1702
1702
  </div>
1703
1703
  {isExpanded ? (
1704
1704
  <div className='bg-muted/20 px-4 pt-1 pb-3'>
1705
- <div className='rounded-lg border border-border bg-card p-3 shadow-raised'>
1705
+ <Surface level="raised" pad="sm" radius="lg">
1706
1706
  {renderExpanded(row.original)}
1707
- </div>
1707
+ </Surface>
1708
1708
  </div>
1709
1709
  ) : null}
1710
1710
  </Fragment>
@@ -2006,9 +2006,9 @@ export function DataTable<TRow extends Record<string, unknown>>({
2006
2006
  instead of a full-bleed band that fractures the pane
2007
2007
  (owner final judgments part 2, detail §1). */}
2008
2008
  <div className='animate-in fade-in slide-in-from-top-1 px-4 pt-1 pb-3 duration-200'>
2009
- <div className='rounded-lg border border-border bg-card p-3 shadow-raised'>
2009
+ <Surface level="raised" pad="sm" radius="lg">
2010
2010
  {renderExpanded(row.original)}
2011
- </div>
2011
+ </Surface>
2012
2012
  </div>
2013
2013
  </td>
2014
2014
  </tr>
@@ -17,6 +17,7 @@ import {
17
17
  newGroup,
18
18
  VALUELESS_OPS,
19
19
  } from './filterQuery.js';
20
+ import { Surface } from '../Surface.js';
20
21
 
21
22
  export interface TableFilterBuilderColumn {
22
23
  key: string;
@@ -163,7 +164,7 @@ export function TableFilterBuilder({
163
164
  <span className='h-px flex-1 bg-border' />
164
165
  </div>
165
166
  ) : null}
166
- <div className='rounded-lg border border-border bg-muted/30 p-2'>
167
+ <Surface level="flat" pad="none" radius="lg" className='bg-muted/30 p-2'>
167
168
  <div className='flex flex-col gap-1.5'>
168
169
  {group.conditions.map((condition, conditionIndex) => (
169
170
  <div key={condition.id} className='flex items-center gap-1.5'>
@@ -254,7 +255,7 @@ export function TableFilterBuilder({
254
255
  >
255
256
  <Plus className='size-3' /> And
256
257
  </button>
257
- </div>
258
+ </Surface>
258
259
  </div>
259
260
  ))}
260
261
  </div>
@@ -2,6 +2,7 @@ import type { ReactNode } from 'react';
2
2
  import { cn } from '../lib/cn.js';
3
3
  import { Badge, type BadgeTone } from './Badge.js';
4
4
  import { EmptyState } from './EmptyState.js';
5
+ import { Surface } from './Surface.js';
5
6
 
6
7
  /** Ordered worst-first. The order IS the sort key. */
7
8
  export const FINDING_SEVERITIES = ['critical', 'warning', 'info'] as const;
@@ -91,13 +92,15 @@ export function FindingsList({
91
92
  return (
92
93
  <ul className={cn('flex flex-col gap-2', className)}>
93
94
  {shown.map((f) => (
94
- <li
95
+ <Surface
95
96
  key={f.id}
96
- className={cn(
97
- 'flex min-w-0 flex-col gap-1 rounded-md border border-border border-l-2 bg-card px-3 py-2.5',
98
- ACCENT[f.severity],
99
- )}
97
+ asChild
98
+ level='flat'
99
+ pad='none'
100
+ radius='md'
101
+ className={cn('flex min-w-0 flex-col gap-1 border-l-2 bg-card px-3 py-2.5', ACCENT[f.severity])}
100
102
  >
103
+ <li>
101
104
  <div className='flex min-w-0 flex-wrap items-center gap-2'>
102
105
  <Badge tone={TONE[f.severity]}>{f.severity}</Badge>
103
106
  {f.scope && <span className='cb-meta-label shrink-0'>{f.scope}</span>}
@@ -113,6 +116,7 @@ export function FindingsList({
113
116
  )}
114
117
  {f.action && <div className='pt-1'>{f.action}</div>}
115
118
  </li>
119
+ </Surface>
116
120
  ))}
117
121
  {hidden > 0 && (
118
122
  <li className='px-3 py-1 text-xs text-muted-foreground'>
@@ -104,6 +104,7 @@ export function InfoHint({
104
104
  {open ? (
105
105
  <FloatingPortal>
106
106
  <div
107
+ // guardrails-ignore no-hand-rolled-surface: the hint's floating-ui bubble — a tooltip-class overlay with its own z-index and shadow, not a card
107
108
  ref={refs.setFloating}
108
109
  style={floatingStyles}
109
110
  {...getFloatingProps()}
@@ -48,6 +48,7 @@ export function Section({
48
48
  const card = variant === 'card';
49
49
  return (
50
50
  <section
51
+ // guardrails-ignore no-hand-rolled-surface: this IS the section primitive; its `card` variant is the tinted frame apps are told to use instead of a hand-rolled one
51
52
  className={cn(
52
53
  'flex flex-col',
53
54
  card ? 'gap-3 rounded-lg border border-border bg-muted/20 p-3 sm:p-4' : 'gap-4',
@@ -3,6 +3,7 @@ import { dayStartMs, monthMatrix, todayKey, WEEKDAY_LABELS } from 'cwip/calendar
3
3
  import type { ReactNode } from 'react';
4
4
  import { cn } from '../../lib/cn.js';
5
5
  import { TAP_TARGET_ALWAYS } from '../../lib/tapTarget.js';
6
+ import { Surface } from '../Surface.js';
6
7
 
7
8
  export interface MonthGridDay {
8
9
  /** Local day key ("YYYY-MM-DD") this cell represents. */
@@ -71,8 +72,8 @@ export function MonthGrid({
71
72
  const clickable = Boolean(onSelectDay);
72
73
 
73
74
  return (
75
+ <Surface asChild level='flat' pad='none' radius='xl' className={cn('overflow-hidden bg-card', className)}>
74
76
  <section
75
- className={cn('overflow-hidden rounded-xl border border-border bg-card', className)}
76
77
  aria-label='Month calendar'
77
78
  >
78
79
  {/* Weekday header — presentational */}
@@ -164,5 +165,6 @@ export function MonthGrid({
164
165
  })}
165
166
  </div>
166
167
  </section>
168
+ </Surface>
167
169
  );
168
170
  }