cursedbelt 5.64.0 → 5.67.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 (145) hide show
  1. package/dist/react/Toast.d.ts.map +1 -1
  2. package/dist/react/Toast.js +4 -1
  3. package/dist/react/Toast.js.map +1 -1
  4. package/dist/react/components/Accordion.d.ts.map +1 -1
  5. package/dist/react/components/Accordion.js +6 -1
  6. package/dist/react/components/Accordion.js.map +1 -1
  7. package/dist/react/components/Button.d.ts.map +1 -1
  8. package/dist/react/components/Button.js +9 -2
  9. package/dist/react/components/Button.js.map +1 -1
  10. package/dist/react/components/CardTapTarget.d.ts.map +1 -1
  11. package/dist/react/components/CardTapTarget.js +7 -1
  12. package/dist/react/components/CardTapTarget.js.map +1 -1
  13. package/dist/react/components/ChipFilterStrip.d.ts.map +1 -1
  14. package/dist/react/components/ChipFilterStrip.js +2 -1
  15. package/dist/react/components/ChipFilterStrip.js.map +1 -1
  16. package/dist/react/components/ColorField.js +1 -1
  17. package/dist/react/components/ColorField.js.map +1 -1
  18. package/dist/react/components/ComboboxField.js +1 -1
  19. package/dist/react/components/ComboboxField.js.map +1 -1
  20. package/dist/react/components/ConfirmDialog.d.ts.map +1 -1
  21. package/dist/react/components/ConfirmDialog.js +2 -1
  22. package/dist/react/components/ConfirmDialog.js.map +1 -1
  23. package/dist/react/components/Drawer.d.ts.map +1 -1
  24. package/dist/react/components/Drawer.js +2 -1
  25. package/dist/react/components/Drawer.js.map +1 -1
  26. package/dist/react/components/Lightbox.d.ts.map +1 -1
  27. package/dist/react/components/Lightbox.js +2 -1
  28. package/dist/react/components/Lightbox.js.map +1 -1
  29. package/dist/react/components/Modal.d.ts.map +1 -1
  30. package/dist/react/components/Modal.js +5 -3
  31. package/dist/react/components/Modal.js.map +1 -1
  32. package/dist/react/components/PromptDialog.d.ts.map +1 -1
  33. package/dist/react/components/PromptDialog.js +2 -1
  34. package/dist/react/components/PromptDialog.js.map +1 -1
  35. package/dist/react/components/SecretInput.js +1 -1
  36. package/dist/react/components/SecretInput.js.map +1 -1
  37. package/dist/react/components/SegmentedControl.d.ts.map +1 -1
  38. package/dist/react/components/SegmentedControl.js +2 -1
  39. package/dist/react/components/SegmentedControl.js.map +1 -1
  40. package/dist/react/components/Tabs.d.ts.map +1 -1
  41. package/dist/react/components/Tabs.js +68 -15
  42. package/dist/react/components/Tabs.js.map +1 -1
  43. package/dist/react/components/TagsInput.js +1 -1
  44. package/dist/react/components/TagsInput.js.map +1 -1
  45. package/dist/react/components/TooltipImpl.d.ts.map +1 -1
  46. package/dist/react/components/TooltipImpl.js +2 -1
  47. package/dist/react/components/TooltipImpl.js.map +1 -1
  48. package/dist/react/components/dialog.d.ts.map +1 -1
  49. package/dist/react/components/dialog.js +2 -1
  50. package/dist/react/components/dialog.js.map +1 -1
  51. package/dist/react/components/searchInput.css +4 -2
  52. package/dist/react/layoutEngine/LayoutConfigDialog.d.ts.map +1 -1
  53. package/dist/react/layoutEngine/LayoutConfigDialog.js +3 -1
  54. package/dist/react/layoutEngine/LayoutConfigDialog.js.map +1 -1
  55. package/dist/react/lib/field.d.ts +2 -2
  56. package/dist/react/lib/field.d.ts.map +1 -1
  57. package/dist/react/lib/field.js +9 -3
  58. package/dist/react/lib/field.js.map +1 -1
  59. package/dist/react/lib/floatingStyles.d.ts +10 -2
  60. package/dist/react/lib/floatingStyles.d.ts.map +1 -1
  61. package/dist/react/lib/floatingStyles.js +12 -7
  62. package/dist/react/lib/floatingStyles.js.map +1 -1
  63. package/dist/react/lib/overlayMotion.d.ts +37 -0
  64. package/dist/react/lib/overlayMotion.d.ts.map +1 -0
  65. package/dist/react/lib/overlayMotion.js +50 -0
  66. package/dist/react/lib/overlayMotion.js.map +1 -0
  67. package/dist/react/lib/press.d.ts +37 -0
  68. package/dist/react/lib/press.d.ts.map +1 -0
  69. package/dist/react/lib/press.js +41 -0
  70. package/dist/react/lib/press.js.map +1 -0
  71. package/dist/react/media-gallery/MediaGallery.d.ts +22 -1
  72. package/dist/react/media-gallery/MediaGallery.d.ts.map +1 -1
  73. package/dist/react/media-gallery/MediaGallery.js +13 -3
  74. package/dist/react/media-gallery/MediaGallery.js.map +1 -1
  75. package/dist/react/nav/PlaceBar.d.ts.map +1 -1
  76. package/dist/react/nav/PlaceBar.js +4 -3
  77. package/dist/react/nav/PlaceBar.js.map +1 -1
  78. package/dist/react/nav/TopBar.d.ts.map +1 -1
  79. package/dist/react/nav/TopBar.js +13 -2
  80. package/dist/react/nav/TopBar.js.map +1 -1
  81. package/dist/react/palette/CommandPalette.d.ts.map +1 -1
  82. package/dist/react/palette/CommandPalette.js +3 -2
  83. package/dist/react/palette/CommandPalette.js.map +1 -1
  84. package/dist/styles-areas/analytics.css +2 -2
  85. package/dist/styles-areas/core.css +73 -64
  86. package/dist/styles-areas/data-table.css +3 -3
  87. package/dist/styles-areas/fields.css +2 -2
  88. package/dist/styles-areas/media.css +4 -4
  89. package/dist/styles-areas/nav.css +18 -18
  90. package/dist/styles-areas/overlays.css +2 -2
  91. package/dist/styles-areas/palette.css +4 -5
  92. package/dist/styles-areas/place-bar.css +5 -5
  93. package/dist/styles-areas/sharing.css +4 -4
  94. package/dist/styles-areas/test-report.css +4 -4
  95. package/dist/styles-areas/wizard.css +2 -2
  96. package/dist/styles-areas/workbench.css +2 -2
  97. package/dist/styles-utilities.css +103 -20
  98. package/dist/theme.css +47 -0
  99. package/package.json +1 -1
  100. package/src/react/Toast.tsx +4 -1
  101. package/src/react/components/Accordion.tsx +8 -1
  102. package/src/react/components/Button.spec.tsx +16 -0
  103. package/src/react/components/Button.tsx +9 -2
  104. package/src/react/components/CardTapTarget.tsx +6 -1
  105. package/src/react/components/ChipFilterStrip.tsx +3 -1
  106. package/src/react/components/ColorField.tsx +1 -1
  107. package/src/react/components/ComboboxField.tsx +1 -1
  108. package/src/react/components/ConfirmDialog.tsx +2 -2
  109. package/src/react/components/Drawer.tsx +2 -0
  110. package/src/react/components/Lightbox.tsx +2 -2
  111. package/src/react/components/Modal.tsx +5 -4
  112. package/src/react/components/PromptDialog.tsx +2 -2
  113. package/src/react/components/SecretInput.tsx +1 -1
  114. package/src/react/components/SegmentedControl.tsx +3 -1
  115. package/src/react/components/Tabs.tsx +78 -5
  116. package/src/react/components/TagsInput.tsx +1 -1
  117. package/src/react/components/TooltipImpl.tsx +2 -0
  118. package/src/react/components/dialog.tsx +2 -2
  119. package/src/react/components/searchInput.css +4 -2
  120. package/src/react/layoutEngine/LayoutConfigDialog.tsx +3 -1
  121. package/src/react/lib/field.ts +9 -3
  122. package/src/react/lib/floatingStyles.ts +12 -8
  123. package/src/react/lib/overlayMotion.ts +58 -0
  124. package/src/react/lib/press.ts +43 -0
  125. package/src/react/media-gallery/MediaGallery.spec.tsx +65 -1
  126. package/src/react/media-gallery/MediaGallery.tsx +37 -2
  127. package/src/react/nav/PlaceBar.tsx +7 -3
  128. package/src/react/nav/TopBar.spec.tsx +34 -0
  129. package/src/react/nav/TopBar.tsx +22 -0
  130. package/src/react/palette/CommandPalette.tsx +3 -2
  131. package/src/styles-areas/analytics.css +2 -2
  132. package/src/styles-areas/core.css +73 -64
  133. package/src/styles-areas/data-table.css +3 -3
  134. package/src/styles-areas/fields.css +2 -2
  135. package/src/styles-areas/media.css +4 -4
  136. package/src/styles-areas/nav.css +18 -18
  137. package/src/styles-areas/overlays.css +2 -2
  138. package/src/styles-areas/palette.css +4 -5
  139. package/src/styles-areas/place-bar.css +5 -5
  140. package/src/styles-areas/sharing.css +4 -4
  141. package/src/styles-areas/test-report.css +4 -4
  142. package/src/styles-areas/wizard.css +2 -2
  143. package/src/styles-areas/workbench.css +2 -2
  144. package/src/styles-utilities.css +103 -20
  145. package/src/theme.css +47 -0
@@ -18,7 +18,7 @@
18
18
  * after an app's responsive variant (task 2124). An app that imports every area gets exactly
19
19
  * what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
20
20
  * src/stylesAreas.spec.ts, which is also what fails when this file is stale.
21
- * 144 candidate(s).
21
+ * 142 candidate(s).
22
22
  *
23
23
  * Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
24
24
  * `bun run build` runs. The map of what is in which area, and why the areas are
@@ -37,20 +37,20 @@
37
37
  @source inline("data-[selected=true]:text-foreground data-[state=closed]:slide-out-to-bottom");
38
38
  @source inline("data-[state=closed]:slide-out-to-left data-[state=closed]:slide-out-to-right");
39
39
  @source inline("data-[state=open]:slide-in-from-bottom data-[state=open]:slide-in-from-left");
40
- @source inline("data-[state=open]:slide-in-from-right data-[state=open]:slide-in-from-top-2");
41
- @source inline("disabled:hover:scale-100 disabled:opacity-40 focus-within:opacity-100 g grid-cols-4");
42
- @source inline("group-hover/navitem:opacity-100 group-hover/tile:opacity-100 group/navitem group/tile h-12");
43
- @source inline("h-[var(--header-h)] h-[var(--header-h-dense)] h-[var(--header-h-roomy)] h-dvh");
44
- @source inline("hover:border-foreground/20 hover:decoration-border hover:scale-110 hover:underline-offset-4");
45
- @source inline("inset-x-2 inset-x-3 inset-y-1 inset-y-1.5 items-stretch justify-around leading-4 left-0 left-3");
46
- @source inline("lg:flex lg:flex-col lg:hidden lg:shrink-0 list-none max-h-72 max-h-[inherit]");
47
- @source inline("max-h-[var(--palette-max-h)] max-h-[var(--sheet-h-lg)] max-h-[var(--sheet-h-md)]");
48
- @source inline("max-h-[var(--sheet-h-sm)] max-md:-right-0.5 max-md:-top-0.5 max-md:absolute max-md:hidden");
49
- @source inline("max-sm:flex-none max-sm:justify-center max-sm:px-0 max-sm:w-10 max-w max-w-48 max-w-64 max-w-80");
50
- @source inline("max-w-sm md:flex md:flex-col md:gap-4 md:hidden md:pointer-coarse:w-auto md:px-3 md:px-6");
51
- @source inline("md:shrink-0 min-h-10 min-h-12 min-h-dvh min-w-3.5 min-w-8 min-w-9 mix-blend-difference mt-auto");
52
- @source inline("mx-1 no-underline opacity-55 opacity-80 pb-[env(safe-area-inset-bottom,0px)] pl-11 pl-9");
53
- @source inline("place-items-center placeholder:text-muted-foreground pr-3 pr-9 pt-0 pt-1 px-0.5 py-8 right-1");
54
- @source inline("rotate-180 rounded-l-xl rounded-r-xl rounded-t-xl shadow-lg size-7 size-9 sm:inline");
55
- @source inline("sm:inline-block sm:inline-flex space-y-0.5 space-y-2 top-[var(--palette-top)] w-52 w-56 w-60");
56
- @source inline("w-[calc(100%-2rem)] w-[var(--rail-w)] w-[var(--rail-w-collapsed)] w-max");
40
+ @source inline("data-[state=open]:slide-in-from-right disabled:hover:scale-100 disabled:opacity-40");
41
+ @source inline("focus-within:opacity-100 g grid-cols-4 group-hover/navitem:opacity-100");
42
+ @source inline("group-hover/tile:opacity-100 group/navitem group/tile h-12 h-[var(--header-h)]");
43
+ @source inline("h-[var(--header-h-dense)] h-[var(--header-h-roomy)] h-dvh hover:border-foreground/20");
44
+ @source inline("hover:decoration-border hover:scale-110 hover:underline-offset-4 inset-x-2 inset-x-3 inset-y-1");
45
+ @source inline("inset-y-1.5 items-stretch justify-around leading-4 left-3 lg:flex lg:flex-col lg:hidden");
46
+ @source inline("lg:shrink-0 list-none max-h-72 max-h-[inherit] max-h-[var(--palette-max-h)]");
47
+ @source inline("max-h-[var(--sheet-h-lg)] max-h-[var(--sheet-h-md)] max-h-[var(--sheet-h-sm)] max-md:-right-0.5");
48
+ @source inline("max-md:-top-0.5 max-md:absolute max-md:hidden max-sm:flex-none max-sm:justify-center max-sm:px-0");
49
+ @source inline("max-sm:w-10 max-w max-w-48 max-w-64 max-w-80 max-w-sm md:flex md:flex-col md:gap-4 md:hidden");
50
+ @source inline("md:pointer-coarse:w-auto md:px-3 md:px-6 md:shrink-0 min-h-10 min-h-12 min-h-dvh min-w-3.5");
51
+ @source inline("min-w-8 min-w-9 mix-blend-difference mt-auto mx-1 no-underline opacity-55 opacity-80");
52
+ @source inline("pb-[env(safe-area-inset-bottom,0px)] pl-11 pl-9 place-items-center");
53
+ @source inline("placeholder:text-muted-foreground pr-3 pr-9 pt-0 pt-1 px-0.5 py-8 right-1 rotate-180");
54
+ @source inline("rounded-l-xl rounded-r-xl rounded-t-xl shadow-lg size-7 size-9 sm:inline sm:inline-block");
55
+ @source inline("sm:inline-flex space-y-0.5 space-y-2 top-[var(--palette-top)] w-52 w-56 w-60 w-[calc(100%-2rem)]");
56
+ @source inline("w-[var(--rail-w)] w-[var(--rail-w-collapsed)] w-max");
@@ -18,7 +18,7 @@
18
18
  * after an app's responsive variant (task 2124). An app that imports every area gets exactly
19
19
  * what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
20
20
  * src/stylesAreas.spec.ts, which is also what fails when this file is stale.
21
- * 24 candidate(s).
21
+ * 23 candidate(s).
22
22
  *
23
23
  * Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
24
24
  * `bun run build` runs. The map of what is in which area, and why the areas are
@@ -28,6 +28,6 @@
28
28
  @source inline("data-[state=closed]:slide-out-to-bottom data-[state=closed]:slide-out-to-left");
29
29
  @source inline("data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-bottom");
30
30
  @source inline("data-[state=open]:slide-in-from-left data-[state=open]:slide-in-from-right hover:border-border");
31
- @source inline("left-0 max-h-[var(--radix-popover-content-available-height)] max-h-[var(--sheet-h-lg)]");
31
+ @source inline("max-h-[var(--radix-popover-content-available-height)] max-h-[var(--sheet-h-lg)]");
32
32
  @source inline("max-h-[var(--sheet-h-md)] max-h-[var(--sheet-h-sm)] max-w-[var(--overlay-max-w)] max-w-sm");
33
33
  @source inline("rounded-l-xl rounded-r-xl rounded-t-xl w-64");
@@ -18,7 +18,7 @@
18
18
  * after an app's responsive variant (task 2124). An app that imports every area gets exactly
19
19
  * what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
20
20
  * src/stylesAreas.spec.ts, which is also what fails when this file is stale.
21
- * 24 candidate(s).
21
+ * 23 candidate(s).
22
22
  *
23
23
  * Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
24
24
  * `bun run build` runs. The map of what is in which area, and why the areas are
@@ -29,7 +29,6 @@
29
29
  @source inline("[&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:tracking-wide");
30
30
  @source inline("[&_[cmdk-group-heading]]:uppercase [&_svg]:size-3.5 bg-accent/15 border-dashed");
31
31
  @source inline("data-[disabled=true]:opacity-50 data-[disabled=true]:pointer-events-none");
32
- @source inline("data-[selected=true]:bg-muted data-[selected=true]:text-foreground");
33
- @source inline("data-[state=open]:slide-in-from-top-2 h-12 max-h-[var(--palette-max-h)] mx-1 place-items-center");
34
- @source inline("placeholder:text-muted-foreground py-8 sm:inline-block top-[var(--palette-top)]");
35
- @source inline("w-[calc(100%-2rem)]");
32
+ @source inline("data-[selected=true]:bg-muted data-[selected=true]:text-foreground h-12");
33
+ @source inline("max-h-[var(--palette-max-h)] mx-1 place-items-center placeholder:text-muted-foreground py-8");
34
+ @source inline("sm:inline-block top-[var(--palette-top)] w-[calc(100%-2rem)]");
@@ -18,7 +18,7 @@
18
18
  * after an app's responsive variant (task 2124). An app that imports every area gets exactly
19
19
  * what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
20
20
  * src/stylesAreas.spec.ts, which is also what fails when this file is stale.
21
- * 31 candidate(s).
21
+ * 30 candidate(s).
22
22
  *
23
23
  * Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
24
24
  * `bun run build` runs. The map of what is in which area, and why the areas are
@@ -27,7 +27,7 @@
27
27
  @source inline("-mt-5 -top-1 bg-accent/15 border-l border-r data-[state=closed]:slide-out-to-bottom");
28
28
  @source inline("data-[state=closed]:slide-out-to-left data-[state=closed]:slide-out-to-right");
29
29
  @source inline("data-[state=open]:slide-in-from-bottom data-[state=open]:slide-in-from-left");
30
- @source inline("data-[state=open]:slide-in-from-right inset-x-3 items-stretch justify-around leading-4 left-0");
31
- @source inline("list-none max-h-[var(--sheet-h-lg)] max-h-[var(--sheet-h-md)] max-h-[var(--sheet-h-sm)] max-w-sm");
32
- @source inline("min-h-12 min-w-3.5 no-underline place-items-center pt-0 px-0.5 rounded-l-xl rounded-r-xl");
33
- @source inline("rounded-t-xl shadow-lg");
30
+ @source inline("data-[state=open]:slide-in-from-right inset-x-3 items-stretch justify-around leading-4 list-none");
31
+ @source inline("max-h-[var(--sheet-h-lg)] max-h-[var(--sheet-h-md)] max-h-[var(--sheet-h-sm)] max-w-sm min-h-12");
32
+ @source inline("min-w-3.5 no-underline place-items-center pt-0 px-0.5 rounded-l-xl rounded-r-xl rounded-t-xl");
33
+ @source inline("shadow-lg");
@@ -18,7 +18,7 @@
18
18
  * after an app's responsive variant (task 2124). An app that imports every area gets exactly
19
19
  * what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
20
20
  * src/stylesAreas.spec.ts, which is also what fails when this file is stale.
21
- * 30 candidate(s).
21
+ * 29 candidate(s).
22
22
  *
23
23
  * Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
24
24
  * `bun run build` runs. The map of what is in which area, and why the areas are
@@ -27,6 +27,6 @@
27
27
  @source inline("bg-accent/15 border-[var(--border)] border-l border-r data-[state=closed]:slide-out-to-bottom");
28
28
  @source inline("data-[state=closed]:slide-out-to-left data-[state=closed]:slide-out-to-right");
29
29
  @source inline("data-[state=open]:slide-in-from-bottom data-[state=open]:slide-in-from-left");
30
- @source inline("data-[state=open]:slide-in-from-right disabled:opacity-100 hover:opacity-90 left-0 left-3");
31
- @source inline("max-h-56 max-h-[var(--sheet-h-lg)] max-h-[var(--sheet-h-md)] max-h-[var(--sheet-h-sm)] max-w-sm");
32
- @source inline("min-w-48 ml-2 mt-4 pl-9 right-3 rounded-l-xl rounded-r-xl rounded-t-xl size-9 space-y-4 w-32");
30
+ @source inline("data-[state=open]:slide-in-from-right disabled:opacity-100 hover:opacity-90 left-3 max-h-56");
31
+ @source inline("max-h-[var(--sheet-h-lg)] max-h-[var(--sheet-h-md)] max-h-[var(--sheet-h-sm)] max-w-sm min-w-48");
32
+ @source inline("ml-2 mt-4 pl-9 right-3 rounded-l-xl rounded-r-xl rounded-t-xl size-9 space-y-4 w-32");
@@ -18,7 +18,7 @@
18
18
  * after an app's responsive variant (task 2124). An app that imports every area gets exactly
19
19
  * what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
20
20
  * src/stylesAreas.spec.ts, which is also what fails when this file is stale.
21
- * 24 candidate(s).
21
+ * 23 candidate(s).
22
22
  *
23
23
  * Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
24
24
  * `bun run build` runs. The map of what is in which area, and why the areas are
@@ -27,6 +27,6 @@
27
27
  @source inline("border-l border-r data-[state=closed]:slide-out-to-bottom data-[state=closed]:slide-out-to-left");
28
28
  @source inline("data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-bottom");
29
29
  @source inline("data-[state=open]:slide-in-from-left data-[state=open]:slide-in-from-right h-80");
30
- @source inline("hover:bg-muted/60 hover:no-underline last:border-0 left-0 max-h-72 max-h-96");
31
- @source inline("max-h-[var(--sheet-h-lg)] max-h-[var(--sheet-h-md)] max-h-[var(--sheet-h-sm)] max-w-sm");
32
- @source inline("no-underline rounded-l-xl rounded-r-xl rounded-t-xl w-2");
30
+ @source inline("hover:bg-muted/60 hover:no-underline last:border-0 max-h-72 max-h-96 max-h-[var(--sheet-h-lg)]");
31
+ @source inline("max-h-[var(--sheet-h-md)] max-h-[var(--sheet-h-sm)] max-w-sm no-underline rounded-l-xl");
32
+ @source inline("rounded-r-xl rounded-t-xl w-2");
@@ -18,7 +18,7 @@
18
18
  * after an app's responsive variant (task 2124). An app that imports every area gets exactly
19
19
  * what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
20
20
  * src/stylesAreas.spec.ts, which is also what fails when this file is stale.
21
- * 58 candidate(s).
21
+ * 57 candidate(s).
22
22
  *
23
23
  * Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
24
24
  * `bun run build` runs. The map of what is in which area, and why the areas are
@@ -33,7 +33,7 @@
33
33
  @source inline("data-[state=open]:slide-in-from-right disabled:bg-transparent disabled:opacity-100 duration-100");
34
34
  @source inline("focus-visible:ring-field-ring/40 focus-within:border-field-ring focus-within:ring-2");
35
35
  @source inline("focus-within:ring-field-ring/30 h-105 has-[[aria-invalid=true]]:border-field-error");
36
- @source inline("hover:border-field-ring hover:text-warning left-0 max-h-64 max-h-[var(--sheet-h-lg)]");
36
+ @source inline("hover:border-field-ring hover:text-warning max-h-64 max-h-[var(--sheet-h-lg)]");
37
37
  @source inline("max-h-[var(--sheet-h-md)] max-h-[var(--sheet-h-sm)] max-w-sm min-h-20 min-w-24");
38
38
  @source inline("min-w-[var(--anchor-width)] mr-1.5 mr-2 pl-1.5 pl-2 pr-1 pr-16 pr-3 resize-none resize-y");
39
39
  @source inline("ring-field-error/30 rounded-l-xl rounded-r-xl rounded-t-xl self-stretch size-7 sm:inline");
@@ -18,7 +18,7 @@
18
18
  * after an app's responsive variant (task 2124). An app that imports every area gets exactly
19
19
  * what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
20
20
  * src/stylesAreas.spec.ts, which is also what fails when this file is stale.
21
- * 108 candidate(s).
21
+ * 107 candidate(s).
22
22
  *
23
23
  * Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
24
24
  * `bun run build` runs. The map of what is in which area, and why the areas are
@@ -32,7 +32,7 @@
32
32
  @source inline("data-[state=open]:slide-in-from-bottom data-[state=open]:slide-in-from-left");
33
33
  @source inline("data-[state=open]:slide-in-from-right disabled:opacity-0 duration-150 first:pt-1 flex-row");
34
34
  @source inline("group-hover:disabled:opacity-50 h-105 h-40 hover:bg-card-overlay hover:bg-muted-foreground/10");
35
- @source inline("hover:bg-muted/80 hover:border-field-ring hover:shadow-popover italic left-0 left-2.5");
35
+ @source inline("hover:bg-muted/80 hover:border-field-ring hover:shadow-popover italic left-2.5");
36
36
  @source inline("max-h-[var(--sheet-h-lg)] max-h-[var(--sheet-h-md)] max-h-[var(--sheet-h-sm)] max-h-full");
37
37
  @source inline("max-w-sm min-h min-h-20 min-h-8 min-h-9 min-h-dvh min-h-full min-w-20 min-w-40 opacity-100");
38
38
  @source inline("pb-0.5 pb-[max(env(safe-area-inset-bottom,0px),0.5rem)] peer-focus peer-focus:outline-none");
@@ -3344,6 +3344,11 @@
3344
3344
  transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
3345
3345
  transition-duration: var(--tw-duration, var(--default-transition-duration));
3346
3346
  }
3347
+ .transition-\[color\,background-color\,border-color\,box-shadow\,scale\] {
3348
+ transition-property: color,background-color,border-color,box-shadow,scale;
3349
+ transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
3350
+ transition-duration: var(--tw-duration, var(--default-transition-duration));
3351
+ }
3347
3352
  .transition-\[opacity\,transform\] {
3348
3353
  transition-property: opacity,transform;
3349
3354
  transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
@@ -3359,6 +3364,11 @@
3359
3364
  transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
3360
3365
  transition-duration: var(--tw-duration, var(--default-transition-duration));
3361
3366
  }
3367
+ .transition-\[transform\,width\] {
3368
+ transition-property: transform,width;
3369
+ transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
3370
+ transition-duration: var(--tw-duration, var(--default-transition-duration));
3371
+ }
3362
3372
  .transition-\[width\] {
3363
3373
  transition-property: width;
3364
3374
  transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
@@ -3400,14 +3410,30 @@
3400
3410
  --tw-duration: 300ms;
3401
3411
  transition-duration: 300ms;
3402
3412
  }
3413
+ .duration-\[var\(--duration-base\)\] {
3414
+ --tw-duration: var(--duration-base);
3415
+ transition-duration: var(--duration-base);
3416
+ }
3403
3417
  .duration-\[var\(--duration-fast\)\] {
3404
3418
  --tw-duration: var(--duration-fast);
3405
3419
  transition-duration: var(--duration-fast);
3406
3420
  }
3421
+ .duration-\[var\(--duration-slow\)\] {
3422
+ --tw-duration: var(--duration-slow);
3423
+ transition-duration: var(--duration-slow);
3424
+ }
3425
+ .duration-\[var\(--duration-slow\)\]\! {
3426
+ --tw-duration: var(--duration-slow) !important;
3427
+ transition-duration: var(--duration-slow) !important;
3428
+ }
3407
3429
  .ease-\[var\(--ease-out\)\] {
3408
3430
  --tw-ease: var(--ease-out);
3409
3431
  transition-timing-function: var(--ease-out);
3410
3432
  }
3433
+ .ease-\[var\(--ease-out\)\]\! {
3434
+ --tw-ease: var(--ease-out) !important;
3435
+ transition-timing-function: var(--ease-out) !important;
3436
+ }
3411
3437
  .ease-in-out {
3412
3438
  --tw-ease: var(--ease-in-out);
3413
3439
  transition-timing-function: var(--ease-in-out);
@@ -4026,6 +4052,18 @@
4026
4052
  --tw-outline-style: none;
4027
4053
  outline-style: none;
4028
4054
  }
4055
+ .active\:scale-100:active {
4056
+ --tw-scale-x: 100%;
4057
+ --tw-scale-y: 100%;
4058
+ --tw-scale-z: 100%;
4059
+ scale: var(--tw-scale-x) var(--tw-scale-y);
4060
+ }
4061
+ .active\:scale-\[var\(--press-scale\)\]:active {
4062
+ scale: var(--press-scale);
4063
+ }
4064
+ .active\:scale-\[var\(--press-scale-surface\)\]:active {
4065
+ scale: var(--press-scale-surface);
4066
+ }
4029
4067
  .active\:cursor-grabbing:active {
4030
4068
  cursor: grabbing;
4031
4069
  }
@@ -4082,6 +4120,12 @@
4082
4120
  color: var(--muted-foreground);
4083
4121
  }
4084
4122
  }
4123
+ .disabled\:active\:scale-100:disabled:active {
4124
+ --tw-scale-x: 100%;
4125
+ --tw-scale-y: 100%;
4126
+ --tw-scale-z: 100%;
4127
+ scale: var(--tw-scale-x) var(--tw-scale-y);
4128
+ }
4085
4129
  .has-\[\[aria-invalid\=true\]\]\:border-field-error:has(:is([aria-invalid=true])) {
4086
4130
  border-color: var(--field-error);
4087
4131
  }
@@ -4113,6 +4157,12 @@
4113
4157
  background-color: transparent;
4114
4158
  }
4115
4159
  }
4160
+ .aria-disabled\:active\:scale-100[aria-disabled="true"]:active {
4161
+ --tw-scale-x: 100%;
4162
+ --tw-scale-y: 100%;
4163
+ --tw-scale-z: 100%;
4164
+ scale: var(--tw-scale-x) var(--tw-scale-y);
4165
+ }
4116
4166
  .aria-pressed\:border[aria-pressed="true"] {
4117
4167
  border-style: var(--tw-border-style);
4118
4168
  border-width: 1px;
@@ -4215,15 +4265,27 @@
4215
4265
  .data-\[selected\=true\]\:text-foreground[data-selected="true"] {
4216
4266
  color: var(--foreground);
4217
4267
  }
4268
+ .data-\[side\=bottom\]\:slide-in-from-top-1[data-side="bottom"] {
4269
+ --tw-enter-translate-y: calc(1*var(--spacing)*-1);
4270
+ }
4218
4271
  .data-\[side\=bottom\]\:slide-in-from-top-2[data-side="bottom"] {
4219
4272
  --tw-enter-translate-y: calc(2*var(--spacing)*-1);
4220
4273
  }
4274
+ .data-\[side\=left\]\:slide-in-from-right-1[data-side="left"] {
4275
+ --tw-enter-translate-x: calc(1*var(--spacing));
4276
+ }
4221
4277
  .data-\[side\=left\]\:slide-in-from-right-2[data-side="left"] {
4222
4278
  --tw-enter-translate-x: calc(2*var(--spacing));
4223
4279
  }
4280
+ .data-\[side\=right\]\:slide-in-from-left-1[data-side="right"] {
4281
+ --tw-enter-translate-x: calc(1*var(--spacing)*-1);
4282
+ }
4224
4283
  .data-\[side\=right\]\:slide-in-from-left-2[data-side="right"] {
4225
4284
  --tw-enter-translate-x: calc(2*var(--spacing)*-1);
4226
4285
  }
4286
+ .data-\[side\=top\]\:slide-in-from-bottom-1[data-side="top"] {
4287
+ --tw-enter-translate-y: calc(1*var(--spacing));
4288
+ }
4227
4289
  .data-\[side\=top\]\:slide-in-from-bottom-2[data-side="top"] {
4228
4290
  --tw-enter-translate-y: calc(2*var(--spacing));
4229
4291
  }
@@ -4310,10 +4372,6 @@
4310
4372
  --tw-exit-opacity: calc(0/100);
4311
4373
  --tw-exit-opacity: 0;
4312
4374
  }
4313
- .data-\[state\=closed\]\:zoom-out-95[data-state="closed"] {
4314
- --tw-exit-scale: calc(95*1%);
4315
- --tw-exit-scale: .95;
4316
- }
4317
4375
  .data-\[state\=closed\]\:slide-out-to-bottom[data-state="closed"] {
4318
4376
  --tw-exit-translate-y: 100%;
4319
4377
  }
@@ -4323,6 +4381,19 @@
4323
4381
  .data-\[state\=closed\]\:slide-out-to-right[data-state="closed"] {
4324
4382
  --tw-exit-translate-x: 100%;
4325
4383
  }
4384
+ .data-\[state\=closed\]\:zoom-out-\[0\.985\][data-state="closed"] {
4385
+ --tw-exit-scale: 0.985;
4386
+ }
4387
+ .data-\[state\=delayed-open\]\:animate-in[data-state="delayed-open"] {
4388
+ animation: enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none);
4389
+ }
4390
+ .data-\[state\=delayed-open\]\:fade-in-0[data-state="delayed-open"] {
4391
+ --tw-enter-opacity: calc(0/100);
4392
+ --tw-enter-opacity: 0;
4393
+ }
4394
+ .data-\[state\=delayed-open\]\:zoom-in-\[0\.985\][data-state="delayed-open"] {
4395
+ --tw-enter-scale: 0.985;
4396
+ }
4326
4397
  .data-\[state\=indeterminate\]\:border-accent-solid[data-state="indeterminate"] {
4327
4398
  border-color: var(--accent-solid);
4328
4399
  }
@@ -4342,21 +4413,20 @@
4342
4413
  --tw-enter-opacity: calc(0/100);
4343
4414
  --tw-enter-opacity: 0;
4344
4415
  }
4345
- .data-\[state\=open\]\:zoom-in-95[data-state="open"] {
4346
- --tw-enter-scale: calc(95*1%);
4347
- --tw-enter-scale: .95;
4348
- }
4349
4416
  .data-\[state\=open\]\:slide-in-from-bottom[data-state="open"] {
4350
4417
  --tw-enter-translate-y: 100%;
4351
4418
  }
4419
+ .data-\[state\=open\]\:slide-in-from-bottom-2[data-state="open"] {
4420
+ --tw-enter-translate-y: calc(2*var(--spacing));
4421
+ }
4352
4422
  .data-\[state\=open\]\:slide-in-from-left[data-state="open"] {
4353
4423
  --tw-enter-translate-x: -100%;
4354
4424
  }
4355
4425
  .data-\[state\=open\]\:slide-in-from-right[data-state="open"] {
4356
4426
  --tw-enter-translate-x: 100%;
4357
4427
  }
4358
- .data-\[state\=open\]\:slide-in-from-top-2[data-state="open"] {
4359
- --tw-enter-translate-y: calc(2*var(--spacing)*-1);
4428
+ .data-\[state\=open\]\:zoom-in-\[0\.985\][data-state="open"] {
4429
+ --tw-enter-scale: 0.985;
4360
4430
  }
4361
4431
  .data-\[state\=unchecked\]\:translate-x-0[data-state="unchecked"] {
4362
4432
  --tw-translate-x: 0px;
@@ -4369,6 +4439,19 @@
4369
4439
  .motion-reduce\:animate-none {
4370
4440
  animation: none;
4371
4441
  }
4442
+ .motion-reduce\:transition-none {
4443
+ transition-property: none;
4444
+ }
4445
+ .motion-reduce\:duration-0 {
4446
+ --tw-duration: 0ms;
4447
+ transition-duration: 0ms;
4448
+ }
4449
+ .motion-reduce\:active\:scale-100:active {
4450
+ --tw-scale-x: 100%;
4451
+ --tw-scale-y: 100%;
4452
+ --tw-scale-z: 100%;
4453
+ scale: var(--tw-scale-x) var(--tw-scale-y);
4454
+ }
4372
4455
  }
4373
4456
  @media (width < 48rem) {
4374
4457
  .max-md\:absolute {
@@ -4621,14 +4704,6 @@
4621
4704
  font-size: var(--text-xs);
4622
4705
  line-height: var(--tw-leading, var(--text-xs--line-height));
4623
4706
  }
4624
- .sm\:data-\[state\=closed\]\:zoom-out-95[data-state="closed"] {
4625
- --tw-exit-scale: calc(95*1%);
4626
- --tw-exit-scale: .95;
4627
- }
4628
- .sm\:data-\[state\=open\]\:zoom-in-95[data-state="open"] {
4629
- --tw-enter-scale: calc(95*1%);
4630
- --tw-enter-scale: .95;
4631
- }
4632
4707
  }
4633
4708
  @media (width >= 48rem) {
4634
4709
  .md\:flex {
@@ -5071,6 +5146,14 @@
5071
5146
  }
5072
5147
  }
5073
5148
  }
5149
+ @media (width >= 40rem) {
5150
+ @media (pointer: fine) {
5151
+ .sm\:pointer-fine\:text-sm {
5152
+ font-size: var(--text-sm);
5153
+ line-height: var(--tw-leading, var(--text-sm--line-height));
5154
+ }
5155
+ }
5156
+ }
5074
5157
  .\[\&_\.alert-icon\]\:text-accent .alert-icon {
5075
5158
  color: var(--accent);
5076
5159
  }
@@ -5768,11 +5851,11 @@
5768
5851
  /*
5769
5852
  * Theme variables cursedbelt's components read BY NAME (`var(--color-card)` in a style prop).
5770
5853
  * Tailwind emits a theme variable only when the app's own pass sees it used, and a `var()` here is
5771
- * that use — `@source inline` cannot mark one. 10 variable(s), derived by
5854
+ * that use — `@source inline` cannot mark one. 13 variable(s), derived by
5772
5855
  * scripts/generateUtilityStyles.ts → themeVariablesReadByName(). Nothing reads this property.
5773
5856
  */
5774
5857
  @layer theme {
5775
5858
  :root, :host {
5776
- --cb-theme-read-by-name: var(--color-accent) var(--color-border) var(--color-card) var(--color-muted) var(--color-popover) var(--duration-base) var(--ease-out) var(--spacing) var(--text-caption) var(--text-sm);
5859
+ --cb-theme-read-by-name: var(--color-accent) var(--color-border) var(--color-card) var(--color-muted) var(--color-popover) var(--duration-base) var(--duration-slow) var(--ease-out) var(--press-scale) var(--press-scale-surface) var(--spacing) var(--text-caption) var(--text-sm);
5777
5860
  }
5778
5861
  }
package/dist/theme.css CHANGED
@@ -896,6 +896,47 @@ body {
896
896
  color: var(--foreground);
897
897
  }
898
898
 
899
+ /*
900
+ * ───────────── Scrollbars: a thin capsule tinted to the pane (forge task 2912) ─────
901
+ *
902
+ * The browser's stock scrollbar is a grey trough with a grey slab in it — on Windows and
903
+ * Linux 15-17px of chrome down the side of every scrolling list, in the OS's colours, not
904
+ * the app's. Linear and Obsidian draw a thin capsule that tints toward the pane it sits on;
905
+ * the owner's brief (`owner-drafting-area/linear-art.md/app.css`) does the same, and so does
906
+ * every app that takes this stylesheet.
907
+ *
908
+ * `--scrollbar-thumb` is the token: unset, the thumb is the pane's own `--foreground` at 22%,
909
+ * resolved per element — so it follows `.dark`, the aurora and every look with no per-scope
910
+ * literal. A scope that wants another tint sets the token. `scrollbar-width`/`scrollbar-color`
911
+ * are the standard (Firefox, Chrome 121+, Safari 18.2+); the `::-webkit-scrollbar` rules are
912
+ * for the Safari that predates them, and Chrome ignores them once the standard pair is set.
913
+ * A Mac's overlay scrollbars keep their own behaviour — only their colour is ours.
914
+ *
915
+ * 🔴 `@layer base`, not bare: this file is unlayered, and an unlayered rule beats every
916
+ * layered one whatever its specificity — so a bare `*` here would have put a thumb back on
917
+ * every strip that hides its bar with the `[scrollbar-width:none]` utility (ChipFilterStrip,
918
+ * TagStrip, TopBar, SectionNav).
919
+ */
920
+ @layer base {
921
+ * {
922
+ scrollbar-width: thin;
923
+ scrollbar-color: var(--scrollbar-thumb, color-mix(in oklab, var(--foreground) 22%, transparent)) transparent;
924
+ }
925
+ ::-webkit-scrollbar {
926
+ width: 8px;
927
+ height: 8px;
928
+ }
929
+ ::-webkit-scrollbar-track {
930
+ background: transparent;
931
+ }
932
+ ::-webkit-scrollbar-thumb {
933
+ background: var(--scrollbar-thumb, color-mix(in oklab, var(--foreground) 22%, transparent));
934
+ border: 2px solid transparent;
935
+ border-radius: 999px;
936
+ background-clip: content-box;
937
+ }
938
+ }
939
+
899
940
  /*
900
941
  * ───────────── The native <select>'s own list, which the PAGE cannot reach ────
901
942
  *
@@ -1099,6 +1140,12 @@ option:disabled {
1099
1140
  --duration-fast: 120ms; /* hover / press */
1100
1141
  --duration-base: 200ms; /* popover */
1101
1142
  --duration-slow: 300ms; /* modal */
1143
+ /* How far a control dips while it is held (Button: `active:scale-[var(--press-scale)]`). One
1144
+ token so every pressable surface answers a finger the same amount; 1 turns it off. */
1145
+ --press-scale: 0.97;
1146
+ /* The same answer for a whole card or row (`CardTapTarget`): a dip is proportional to size, so
1147
+ 0.97 of a 400px card moves 12px and reads as a lurch. lib/press.ts has the contract. */
1148
+ --press-scale-surface: 0.99;
1102
1149
 
1103
1150
  /* Fluid typography — headings only (fluid body text causes line-length drift).
1104
1151
  Exposed as text-h1 / text-h2 / text-h3 font-size utilities. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursedbelt",
3
- "version": "5.64.0",
3
+ "version": "5.67.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.",
@@ -82,7 +82,10 @@ export function Toaster({ style, ...props }: ToasterProps) {
82
82
  closeButton
83
83
  toastOptions={{
84
84
  classNames: {
85
- toast: 'rounded-lg border border-border bg-popover text-foreground shadow-overlay',
85
+ // The overlay curve and the slow speed (`lib/overlayMotion.ts`, forge task 2912) in place
86
+ // of sonner's 400ms `ease`. `!` because sonner's own `[data-sonner-toast]` rule sets the
87
+ // transition shorthand after this stylesheet; its reduced-motion rule still turns it off.
88
+ toast: 'rounded-lg border border-border bg-popover text-foreground shadow-overlay duration-[var(--duration-slow)]! ease-[var(--ease-out)]!',
86
89
  title: 'text-sm font-medium',
87
90
  // `!` because sonner colours these two with element rules of its own (`#3f3f3f` for a
88
91
  // description, its grey scale for the close button) that no variable reaches.
@@ -2,6 +2,7 @@ import * as RadixAccordion from '@radix-ui/react-accordion';
2
2
  import { ChevronRight } from 'lucide-react';
3
3
  import type { ReactNode } from 'react';
4
4
  import { cn } from '../lib/cn.js';
5
+ import { TAP_TARGET_HEIGHT } from '../lib/tapTarget.js';
5
6
 
6
7
  export interface AccordionItem {
7
8
  value: string;
@@ -67,7 +68,13 @@ export function Accordion({
67
68
  <RadixAccordion.Header>
68
69
  {/* Disabled = muted ink, as SelectableRow: half-opacity faded the chevron and the
69
70
  divider-adjacent text into the surface (forge task 2594). */}
70
- <RadixAccordion.Trigger className='group flex w-full items-center justify-start gap-2 py-3 text-left text-sm font-medium text-foreground transition-colors hover:text-accent disabled:cursor-not-allowed disabled:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring'>
71
+ <RadixAccordion.Trigger
72
+ // `TAP_TARGET_HEIGHT`: a 41px header on a phone (family's Children/Siblings, forge task 2913) is under the 44pt floor — DESIGN.md §5.
73
+ className={cn(
74
+ 'group flex w-full items-center justify-start gap-2 py-3 text-left text-sm font-medium text-foreground transition-colors hover:text-accent disabled:cursor-not-allowed disabled:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
75
+ TAP_TARGET_HEIGHT,
76
+ )}
77
+ >
71
78
  {/* The arrow leads (FRM-18), and the trigger takes the rest of the row. */}
72
79
  <ChevronRight aria-hidden className='size-4 shrink-0 text-muted-foreground transition-transform duration-200 group-data-[state=open]:rotate-90' />
73
80
  <span className='min-w-0 flex-1'>{item.trigger}</span>
@@ -172,6 +172,22 @@ describe('Button', () => {
172
172
  expect(getByRole('button').getAttribute('aria-disabled')).toBeNull();
173
173
  });
174
174
 
175
+ test('🔴 a press is answered: the control dips to --press-scale while held, eased, and a link never does', () => {
176
+ const cls = render(<Button tooltip='B'>B</Button>).getByRole('button').className;
177
+ expect(cls).toContain('active:scale-[var(--press-scale)]');
178
+ // The dip eases in and out like the colours do — `scale` (the property Tailwind v4's
179
+ // `scale-*` writes, not `transform`) is in the transition list.
180
+ expect(cls).toMatch(/transition-\[[^\]]*\bscale\b[^\]]*\]/);
181
+ // Nothing happened on a refused press, so nothing dips; reduced motion turns it off.
182
+ expect(cls).toContain('aria-disabled:active:scale-100');
183
+ expect(cls).toContain('motion-reduce:active:scale-100');
184
+ cleanup();
185
+ const link = render(<Button tooltip='L' variant='link'>L</Button>).getByRole('button').className.split(/\s+/);
186
+ // tailwind-merge keeps the later `active:scale-100` and drops the base dip for a link.
187
+ expect(link).toContain('active:scale-100');
188
+ expect(link).not.toContain('active:scale-[var(--press-scale)]');
189
+ });
190
+
175
191
  test('a pressed toggle and a refused control are drawn by the variant classes', () => {
176
192
  const cls = render(<Button tooltip='B'>B</Button>).getByRole('button').className;
177
193
  expect(cls).toContain('aria-pressed:border-accent');
@@ -14,6 +14,7 @@ import { cn } from '../lib/cn.js';
14
14
  import { Tooltip, type TooltipMobile, type TooltipPlacement } from './Tooltip.js';
15
15
  import { TAP_TARGET_HEIGHT } from '../lib/tapTarget.js';
16
16
  import { RAW_BUTTON } from '../lib/cn.js';
17
+ import { PRESS } from '../lib/press.js';
17
18
 
18
19
  /**
19
20
  * The single source of truth for every interactive element that triggers an action.
@@ -32,7 +33,9 @@ import { RAW_BUTTON } from '../lib/cn.js';
32
33
  export const buttonVariants = cva(
33
34
  [
34
35
  'relative inline-flex items-center justify-center gap-2 rounded-md font-medium',
35
- 'transition-colors duration-[var(--duration-fast)] ease-[var(--ease-out)]',
36
+ // 🔴 TACTILE (forge task family-app-ux-overhaul, 2026-10-06): a press is answered — eased
37
+ // colours and a `--press-scale` dip while held. The contract and who is exempt: `lib/press.ts`.
38
+ PRESS,
36
39
  TAP_TARGET_HEIGHT, // ≥44px touch target on coarse pointers
37
40
  'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-offset-background',
38
41
  // Disabled = muted surface + faded + inert (never color-alone): clearly off, not a low-contrast haze.
@@ -129,7 +132,11 @@ export const buttonVariants = cva(
129
132
  // before the size's `px-3`/`px-4` and fixed height, so the size won the merge and every link
130
133
  // button — sized or on the `md` default — drew a padded box: TotpEntry's "Use a recovery code
131
134
  // instead" sat 12px in from the column every other row starts on.
132
- compoundVariants: [{ variant: 'link', size: ['sm', 'md', 'lg'], class: 'h-auto px-0 py-0' }],
135
+ compoundVariants: [
136
+ { variant: 'link', size: ['sm', 'md', 'lg'], class: 'h-auto px-0 py-0' },
137
+ // A link is text inside a sentence: it underlines on hover and does not dip on press.
138
+ { variant: 'link', class: 'active:scale-100' },
139
+ ],
133
140
  defaultVariants: { variant: 'secondary', size: 'md' },
134
141
  },
135
142
  );
@@ -1,5 +1,7 @@
1
1
  import { type ReactNode, useRef } from 'react';
2
2
  import { cn } from '../lib/cn.js';
3
+ import { PRESS_SURFACE } from '../lib/press.js';
4
+ import { TAP_TARGET_HEIGHT } from '../lib/tapTarget.js';
3
5
  import { hasSelectionWithin } from '../lib/textSelection.js';
4
6
 
5
7
  export interface CardTapTargetProps {
@@ -67,7 +69,10 @@ export const CardTapTarget = ({
67
69
  onClick();
68
70
  }
69
71
  }}
70
- className={cn('cursor-pointer touch-manipulation text-left', className)}
72
+ // `TAP_TARGET_HEIGHT`: a tappable row on a phone answers to a thumb — family's 35px value
73
+ // rows (`.ft-row`, forge task 2913) were under the 44pt floor. DESIGN.md §5.
74
+ // `PRESS_SURFACE`: the card answers a press with a dip sized for a surface (forge task 2912).
75
+ className={cn('cursor-pointer touch-manipulation text-left', TAP_TARGET_HEIGHT, PRESS_SURFACE, className)}
71
76
  {...rest}
72
77
  >
73
78
  {children}
@@ -1,5 +1,6 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import { cn, RAW_BUTTON } from '../lib/cn.js';
3
+ import { PRESS } from '../lib/press.js';
3
4
  import { ScrollArea } from './ScrollArea.js';
4
5
 
5
6
  export interface ChipOption {
@@ -122,7 +123,8 @@ function Chip({ active, onClick, children }: ChipProps) {
122
123
  RAW_BUTTON,
123
124
  // 44 px min tap target height via py + min-h (STANDARDS §4a). px-4 keeps every
124
125
  // pill's internal padding identical regardless of label length.
125
- 'inline-flex min-h-11 shrink-0 cursor-pointer items-center rounded-full px-4 text-sm font-medium transition-colors',
126
+ 'inline-flex min-h-11 shrink-0 cursor-pointer items-center rounded-full px-4 text-sm font-medium',
127
+ PRESS,
126
128
  'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
127
129
  // Soft active state — an accent tint + accent text (the standardized active
128
130
  // treatment shared with nav items and the `accent` Badge tone), not a stark