tyrell-components 1.0.0-RC13 → 1.0.0-RC15

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 (358) hide show
  1. package/README.md +10 -2
  2. package/css/tyrell-theme.css +192 -180
  3. package/css/tyrell.css +23 -0
  4. package/dist/tyrell-colors-static.css +1 -316
  5. package/dist/tyrell-theme.css +1 -1454
  6. package/dist/tyrell.css +1 -1371
  7. package/dist/tyrell.js +1 -1
  8. package/lib/base/ty-component.d.ts +20 -10
  9. package/lib/base/ty-component.js +196 -319
  10. package/lib/components/button.d.ts +3 -17
  11. package/lib/components/button.js +261 -295
  12. package/lib/components/calendar-month.d.ts +4 -50
  13. package/lib/components/calendar-month.js +443 -582
  14. package/lib/components/calendar-navigation.d.ts +3 -48
  15. package/lib/components/calendar-navigation.js +239 -362
  16. package/lib/components/calendar.d.ts +8 -83
  17. package/lib/components/calendar.js +616 -829
  18. package/lib/components/checkbox.d.ts +5 -72
  19. package/lib/components/checkbox.js +273 -416
  20. package/lib/components/copy.d.ts +4 -57
  21. package/lib/components/copy.js +280 -397
  22. package/lib/components/date-picker.d.ts +21 -163
  23. package/lib/components/date-picker.js +1143 -1674
  24. package/lib/components/file-upload.d.ts +9 -15
  25. package/lib/components/file-upload.js +404 -426
  26. package/lib/components/icon.d.ts +3 -24
  27. package/lib/components/icon.js +141 -230
  28. package/lib/components/input.d.ts +8 -108
  29. package/lib/components/input.js +593 -784
  30. package/lib/components/modal.d.ts +17 -26
  31. package/lib/components/modal.js +340 -484
  32. package/lib/components/option.d.ts +2 -9
  33. package/lib/components/option.js +168 -216
  34. package/lib/components/popup.d.ts +2 -17
  35. package/lib/components/popup.js +249 -380
  36. package/lib/components/radio.d.ts +5 -25
  37. package/lib/components/radio.js +407 -461
  38. package/lib/components/resize-observer.d.ts +3 -30
  39. package/lib/components/resize-observer.js +69 -103
  40. package/lib/components/scroll-container.d.ts +2 -12
  41. package/lib/components/scroll-container.js +293 -338
  42. package/lib/components/select.d.ts +87 -159
  43. package/lib/components/select.js +1609 -2090
  44. package/lib/components/selected-tags.d.ts +5 -48
  45. package/lib/components/selected-tags.js +188 -276
  46. package/lib/components/step.d.ts +1 -6
  47. package/lib/components/step.js +62 -68
  48. package/lib/components/switch.d.ts +1 -12
  49. package/lib/components/switch.js +214 -247
  50. package/lib/components/tab.d.ts +1 -6
  51. package/lib/components/tab.js +62 -69
  52. package/lib/components/tabs.d.ts +6 -63
  53. package/lib/components/tabs.js +545 -777
  54. package/lib/components/tag.d.ts +3 -37
  55. package/lib/components/tag.js +236 -335
  56. package/lib/components/textarea.d.ts +7 -87
  57. package/lib/components/textarea.js +457 -627
  58. package/lib/components/tooltip.d.ts +3 -11
  59. package/lib/components/tooltip.js +307 -443
  60. package/lib/components/wizard.d.ts +6 -54
  61. package/lib/components/wizard.js +405 -618
  62. package/lib/icons/fontawesome/brands.d.ts +0 -1
  63. package/lib/icons/fontawesome/brands.js +0 -8
  64. package/lib/icons/fontawesome/regular.d.ts +0 -1
  65. package/lib/icons/fontawesome/regular.js +0 -8
  66. package/lib/icons/fontawesome/solid.d.ts +0 -1
  67. package/lib/icons/fontawesome/solid.js +0 -8
  68. package/lib/icons/heroicons/micro.d.ts +0 -1
  69. package/lib/icons/heroicons/micro.js +392 -400
  70. package/lib/icons/heroicons/mini.d.ts +0 -1
  71. package/lib/icons/heroicons/mini.js +382 -390
  72. package/lib/icons/heroicons/outline.d.ts +0 -1
  73. package/lib/icons/heroicons/outline.js +337 -345
  74. package/lib/icons/heroicons/solid.d.ts +0 -1
  75. package/lib/icons/heroicons/solid.js +407 -415
  76. package/lib/icons/lucide.d.ts +0 -1
  77. package/lib/icons/lucide.js +24479 -26351
  78. package/lib/icons/material/filled.d.ts +0 -1
  79. package/lib/icons/material/filled.js +9653 -9663
  80. package/lib/icons/material/outlined.d.ts +0 -1
  81. package/lib/icons/material/outlined.js +10271 -10281
  82. package/lib/icons/material/round.d.ts +0 -1
  83. package/lib/icons/material/round.js +10495 -10505
  84. package/lib/icons/material/sharp.d.ts +0 -1
  85. package/lib/icons/material/sharp.js +9905 -9915
  86. package/lib/icons/material/two-tone.d.ts +0 -1
  87. package/lib/icons/material/two-tone.js +12792 -12802
  88. package/lib/index.d.ts +0 -1
  89. package/lib/index.js +21 -41
  90. package/lib/styles/button.d.ts +2 -20
  91. package/lib/styles/button.js +202 -404
  92. package/lib/styles/calendar-month.d.ts +1 -5
  93. package/lib/styles/calendar-month.js +127 -244
  94. package/lib/styles/calendar-navigation.d.ts +2 -6
  95. package/lib/styles/calendar-navigation.js +64 -104
  96. package/lib/styles/calendar.d.ts +1 -2
  97. package/lib/styles/calendar.js +9 -22
  98. package/lib/styles/checkbox.d.ts +0 -1
  99. package/lib/styles/checkbox.js +45 -94
  100. package/lib/styles/copy.d.ts +0 -1
  101. package/lib/styles/copy.js +67 -106
  102. package/lib/styles/custom-scrollbar.d.ts +1 -2
  103. package/lib/styles/custom-scrollbar.js +80 -115
  104. package/lib/styles/date-picker.d.ts +1 -5
  105. package/lib/styles/date-picker.js +261 -335
  106. package/lib/styles/field-size.d.ts +6 -0
  107. package/lib/styles/field-size.js +91 -0
  108. package/lib/styles/file-upload.d.ts +1 -2
  109. package/lib/styles/file-upload.js +143 -188
  110. package/lib/styles/icon.d.ts +2 -6
  111. package/lib/styles/icon.js +92 -170
  112. package/lib/styles/input.d.ts +1 -6
  113. package/lib/styles/input.js +125 -290
  114. package/lib/styles/modal.d.ts +1 -2
  115. package/lib/styles/modal.js +71 -108
  116. package/lib/styles/option.d.ts +2 -6
  117. package/lib/styles/option.js +49 -64
  118. package/lib/styles/popup.d.ts +1 -2
  119. package/lib/styles/popup.js +42 -77
  120. package/lib/styles/radio.d.ts +0 -1
  121. package/lib/styles/radio.js +69 -110
  122. package/lib/styles/resize-observer.d.ts +1 -2
  123. package/lib/styles/resize-observer.js +2 -12
  124. package/lib/styles/scroll-container.d.ts +1 -2
  125. package/lib/styles/scroll-container.js +100 -149
  126. package/lib/styles/select-base.d.ts +0 -1
  127. package/lib/styles/select-base.js +597 -785
  128. package/lib/styles/select.d.ts +1 -2
  129. package/lib/styles/select.js +140 -236
  130. package/lib/styles/step.d.ts +1 -2
  131. package/lib/styles/step.js +22 -33
  132. package/lib/styles/switch.d.ts +0 -1
  133. package/lib/styles/switch.js +34 -73
  134. package/lib/styles/tab.d.ts +1 -2
  135. package/lib/styles/tab.js +22 -34
  136. package/lib/styles/tabs.d.ts +3 -11
  137. package/lib/styles/tabs.js +190 -222
  138. package/lib/styles/tag.d.ts +1 -5
  139. package/lib/styles/tag.js +136 -218
  140. package/lib/styles/textarea.d.ts +1 -2
  141. package/lib/styles/textarea.js +210 -319
  142. package/lib/styles/tooltip.d.ts +0 -1
  143. package/lib/styles/tooltip.js +3 -13
  144. package/lib/styles/wizard.d.ts +5 -21
  145. package/lib/styles/wizard.js +170 -306
  146. package/lib/types/common.d.ts +2 -2
  147. package/lib/types/common.js +6 -17
  148. package/lib/utils/calendar-utils.d.ts +4 -21
  149. package/lib/utils/calendar-utils.js +100 -273
  150. package/lib/utils/custom-scrollbar.d.ts +6 -20
  151. package/lib/utils/custom-scrollbar.js +273 -317
  152. package/lib/utils/flavor-sheet.d.ts +0 -1
  153. package/lib/utils/flavor-sheet.js +14 -37
  154. package/lib/utils/icon-registry.d.ts +0 -2
  155. package/lib/utils/icon-registry.js +137 -243
  156. package/lib/utils/icons.d.ts +0 -1
  157. package/lib/utils/icons.js +0 -9
  158. package/lib/utils/loader-registry.d.ts +0 -1
  159. package/lib/utils/loader-registry.js +3 -35
  160. package/lib/utils/locale.d.ts +2 -23
  161. package/lib/utils/locale.js +34 -120
  162. package/lib/utils/mobile.d.ts +0 -1
  163. package/lib/utils/mobile.js +1 -18
  164. package/lib/utils/number-format.d.ts +6 -3
  165. package/lib/utils/number-format.js +54 -110
  166. package/lib/utils/positioning.d.ts +50 -7
  167. package/lib/utils/positioning.js +224 -234
  168. package/lib/utils/property-capture.d.ts +5 -27
  169. package/lib/utils/property-capture.js +36 -144
  170. package/lib/utils/property-manager.d.ts +0 -1
  171. package/lib/utils/property-manager.js +126 -196
  172. package/lib/utils/resize-observer.d.ts +0 -1
  173. package/lib/utils/resize-observer.js +23 -59
  174. package/lib/utils/scroll-lock.d.ts +4 -11
  175. package/lib/utils/scroll-lock.js +65 -172
  176. package/lib/utils/styles.d.ts +0 -1
  177. package/lib/utils/styles.js +17 -41
  178. package/lib/version.d.ts +1 -2
  179. package/lib/version.js +1 -11
  180. package/package.json +4 -4
  181. package/lib/base/ty-component.d.ts.map +0 -1
  182. package/lib/base/ty-component.js.map +0 -1
  183. package/lib/components/button.d.ts.map +0 -1
  184. package/lib/components/button.js.map +0 -1
  185. package/lib/components/calendar-month.d.ts.map +0 -1
  186. package/lib/components/calendar-month.js.map +0 -1
  187. package/lib/components/calendar-navigation.d.ts.map +0 -1
  188. package/lib/components/calendar-navigation.js.map +0 -1
  189. package/lib/components/calendar.d.ts.map +0 -1
  190. package/lib/components/calendar.js.map +0 -1
  191. package/lib/components/checkbox.d.ts.map +0 -1
  192. package/lib/components/checkbox.js.map +0 -1
  193. package/lib/components/copy.d.ts.map +0 -1
  194. package/lib/components/copy.js.map +0 -1
  195. package/lib/components/date-picker.d.ts.map +0 -1
  196. package/lib/components/date-picker.js.map +0 -1
  197. package/lib/components/file-upload.d.ts.map +0 -1
  198. package/lib/components/file-upload.js.map +0 -1
  199. package/lib/components/icon.d.ts.map +0 -1
  200. package/lib/components/icon.js.map +0 -1
  201. package/lib/components/input.d.ts.map +0 -1
  202. package/lib/components/input.js.map +0 -1
  203. package/lib/components/modal.d.ts.map +0 -1
  204. package/lib/components/modal.js.map +0 -1
  205. package/lib/components/option.d.ts.map +0 -1
  206. package/lib/components/option.js.map +0 -1
  207. package/lib/components/popup.d.ts.map +0 -1
  208. package/lib/components/popup.js.map +0 -1
  209. package/lib/components/radio.d.ts.map +0 -1
  210. package/lib/components/radio.js.map +0 -1
  211. package/lib/components/resize-observer.d.ts.map +0 -1
  212. package/lib/components/resize-observer.js.map +0 -1
  213. package/lib/components/scroll-container.d.ts.map +0 -1
  214. package/lib/components/scroll-container.js.map +0 -1
  215. package/lib/components/select.d.ts.map +0 -1
  216. package/lib/components/select.js.map +0 -1
  217. package/lib/components/selected-tags.d.ts.map +0 -1
  218. package/lib/components/selected-tags.js.map +0 -1
  219. package/lib/components/step.d.ts.map +0 -1
  220. package/lib/components/step.js.map +0 -1
  221. package/lib/components/switch.d.ts.map +0 -1
  222. package/lib/components/switch.js.map +0 -1
  223. package/lib/components/tab.d.ts.map +0 -1
  224. package/lib/components/tab.js.map +0 -1
  225. package/lib/components/tabs.d.ts.map +0 -1
  226. package/lib/components/tabs.js.map +0 -1
  227. package/lib/components/tag.d.ts.map +0 -1
  228. package/lib/components/tag.js.map +0 -1
  229. package/lib/components/textarea.d.ts.map +0 -1
  230. package/lib/components/textarea.js.map +0 -1
  231. package/lib/components/tooltip.d.ts.map +0 -1
  232. package/lib/components/tooltip.js.map +0 -1
  233. package/lib/components/wizard.d.ts.map +0 -1
  234. package/lib/components/wizard.js.map +0 -1
  235. package/lib/icons/fontawesome/brands.d.ts.map +0 -1
  236. package/lib/icons/fontawesome/brands.js.map +0 -1
  237. package/lib/icons/fontawesome/regular.d.ts.map +0 -1
  238. package/lib/icons/fontawesome/regular.js.map +0 -1
  239. package/lib/icons/fontawesome/solid.d.ts.map +0 -1
  240. package/lib/icons/fontawesome/solid.js.map +0 -1
  241. package/lib/icons/heroicons/micro.d.ts.map +0 -1
  242. package/lib/icons/heroicons/micro.js.map +0 -1
  243. package/lib/icons/heroicons/mini.d.ts.map +0 -1
  244. package/lib/icons/heroicons/mini.js.map +0 -1
  245. package/lib/icons/heroicons/outline.d.ts.map +0 -1
  246. package/lib/icons/heroicons/outline.js.map +0 -1
  247. package/lib/icons/heroicons/solid.d.ts.map +0 -1
  248. package/lib/icons/heroicons/solid.js.map +0 -1
  249. package/lib/icons/lucide.d.ts.map +0 -1
  250. package/lib/icons/lucide.js.map +0 -1
  251. package/lib/icons/material/filled.d.ts.map +0 -1
  252. package/lib/icons/material/filled.js.map +0 -1
  253. package/lib/icons/material/outlined.d.ts.map +0 -1
  254. package/lib/icons/material/outlined.js.map +0 -1
  255. package/lib/icons/material/round.d.ts.map +0 -1
  256. package/lib/icons/material/round.js.map +0 -1
  257. package/lib/icons/material/sharp.d.ts.map +0 -1
  258. package/lib/icons/material/sharp.js.map +0 -1
  259. package/lib/icons/material/two-tone.d.ts.map +0 -1
  260. package/lib/icons/material/two-tone.js.map +0 -1
  261. package/lib/index.d.ts.map +0 -1
  262. package/lib/index.js.map +0 -1
  263. package/lib/styles/button.d.ts.map +0 -1
  264. package/lib/styles/button.js.map +0 -1
  265. package/lib/styles/calendar-month.d.ts.map +0 -1
  266. package/lib/styles/calendar-month.js.map +0 -1
  267. package/lib/styles/calendar-navigation.d.ts.map +0 -1
  268. package/lib/styles/calendar-navigation.js.map +0 -1
  269. package/lib/styles/calendar.d.ts.map +0 -1
  270. package/lib/styles/calendar.js.map +0 -1
  271. package/lib/styles/checkbox.d.ts.map +0 -1
  272. package/lib/styles/checkbox.js.map +0 -1
  273. package/lib/styles/copy.d.ts.map +0 -1
  274. package/lib/styles/copy.js.map +0 -1
  275. package/lib/styles/custom-scrollbar.d.ts.map +0 -1
  276. package/lib/styles/custom-scrollbar.js.map +0 -1
  277. package/lib/styles/date-picker.d.ts.map +0 -1
  278. package/lib/styles/date-picker.js.map +0 -1
  279. package/lib/styles/file-upload.d.ts.map +0 -1
  280. package/lib/styles/file-upload.js.map +0 -1
  281. package/lib/styles/icon.d.ts.map +0 -1
  282. package/lib/styles/icon.js.map +0 -1
  283. package/lib/styles/input.d.ts.map +0 -1
  284. package/lib/styles/input.js.map +0 -1
  285. package/lib/styles/modal.d.ts.map +0 -1
  286. package/lib/styles/modal.js.map +0 -1
  287. package/lib/styles/multiselect.d.ts +0 -8
  288. package/lib/styles/multiselect.d.ts.map +0 -1
  289. package/lib/styles/multiselect.js +0 -849
  290. package/lib/styles/multiselect.js.map +0 -1
  291. package/lib/styles/option.d.ts.map +0 -1
  292. package/lib/styles/option.js.map +0 -1
  293. package/lib/styles/popup.d.ts.map +0 -1
  294. package/lib/styles/popup.js.map +0 -1
  295. package/lib/styles/radio.d.ts.map +0 -1
  296. package/lib/styles/radio.js.map +0 -1
  297. package/lib/styles/resize-observer.d.ts.map +0 -1
  298. package/lib/styles/resize-observer.js.map +0 -1
  299. package/lib/styles/scroll-container.d.ts.map +0 -1
  300. package/lib/styles/scroll-container.js.map +0 -1
  301. package/lib/styles/select-base.d.ts.map +0 -1
  302. package/lib/styles/select-base.js.map +0 -1
  303. package/lib/styles/select.d.ts.map +0 -1
  304. package/lib/styles/select.js.map +0 -1
  305. package/lib/styles/step.d.ts.map +0 -1
  306. package/lib/styles/step.js.map +0 -1
  307. package/lib/styles/switch.d.ts.map +0 -1
  308. package/lib/styles/switch.js.map +0 -1
  309. package/lib/styles/tab.d.ts.map +0 -1
  310. package/lib/styles/tab.js.map +0 -1
  311. package/lib/styles/tabs.d.ts.map +0 -1
  312. package/lib/styles/tabs.js.map +0 -1
  313. package/lib/styles/tag.d.ts.map +0 -1
  314. package/lib/styles/tag.js.map +0 -1
  315. package/lib/styles/textarea.d.ts.map +0 -1
  316. package/lib/styles/textarea.js.map +0 -1
  317. package/lib/styles/tooltip.d.ts.map +0 -1
  318. package/lib/styles/tooltip.js.map +0 -1
  319. package/lib/styles/wizard.d.ts.map +0 -1
  320. package/lib/styles/wizard.js.map +0 -1
  321. package/lib/types/common.d.ts.map +0 -1
  322. package/lib/types/common.js.map +0 -1
  323. package/lib/utils/calendar-utils.d.ts.map +0 -1
  324. package/lib/utils/calendar-utils.js.map +0 -1
  325. package/lib/utils/custom-scrollbar.d.ts.map +0 -1
  326. package/lib/utils/custom-scrollbar.js.map +0 -1
  327. package/lib/utils/flavor-sheet.d.ts.map +0 -1
  328. package/lib/utils/flavor-sheet.js.map +0 -1
  329. package/lib/utils/icon-registry.d.ts.map +0 -1
  330. package/lib/utils/icon-registry.js.map +0 -1
  331. package/lib/utils/icons.d.ts.map +0 -1
  332. package/lib/utils/icons.js.map +0 -1
  333. package/lib/utils/loader-registry.d.ts.map +0 -1
  334. package/lib/utils/loader-registry.js.map +0 -1
  335. package/lib/utils/locale.d.ts.map +0 -1
  336. package/lib/utils/locale.js.map +0 -1
  337. package/lib/utils/mobile.d.ts.map +0 -1
  338. package/lib/utils/mobile.js.map +0 -1
  339. package/lib/utils/number-format.d.ts.map +0 -1
  340. package/lib/utils/number-format.js.map +0 -1
  341. package/lib/utils/parse-boolean.d.ts +0 -39
  342. package/lib/utils/parse-boolean.d.ts.map +0 -1
  343. package/lib/utils/parse-boolean.js +0 -58
  344. package/lib/utils/parse-boolean.js.map +0 -1
  345. package/lib/utils/positioning.d.ts.map +0 -1
  346. package/lib/utils/positioning.js.map +0 -1
  347. package/lib/utils/property-capture.d.ts.map +0 -1
  348. package/lib/utils/property-capture.js.map +0 -1
  349. package/lib/utils/property-manager.d.ts.map +0 -1
  350. package/lib/utils/property-manager.js.map +0 -1
  351. package/lib/utils/resize-observer.d.ts.map +0 -1
  352. package/lib/utils/resize-observer.js.map +0 -1
  353. package/lib/utils/scroll-lock.d.ts.map +0 -1
  354. package/lib/utils/scroll-lock.js.map +0 -1
  355. package/lib/utils/styles.d.ts.map +0 -1
  356. package/lib/utils/styles.js.map +0 -1
  357. package/lib/version.d.ts.map +0 -1
  358. package/lib/version.js.map +0 -1
@@ -1,93 +1,16 @@
1
- /**
2
- * Button Component Styles
3
- *
4
- * Three appearance variants × five semantic flavors × three tones (+/base/-).
5
- * Each variant uses ONE token system:
6
- *
7
- * - solid → flat per-segment tokens --ty-solid-{flavor}{,-hover,-active,
8
- * -strong,-soft,-fg}. Derived in tyrell-theme.css via OKLCH on
9
- * 3 axes (color / hover-active / theme chroma-hue); literal in
10
- * tyrell.css. The component does no color math for the fill.
11
- * Depth chrome (border + drop shadow) IS computed here, from
12
- * the fill, via relative color — see "Depth chrome" below.
13
- * - outlined → --ty-color-{flavor}-{strong|base|soft} (text === border at
14
- * rest); hover/active/focus always resolve to -strong,
15
- * whatever the resting tone was — see "Hover escalation".
16
- * - ghost → same as outlined, text only (no border), --ty-bg-{flavor}-soft
17
- * background on hover.
18
- *
19
- * Neutral is the exception in both outlined and ghost: its `+` tone reads
20
- * --ty-solid-neutral-strong (the ink dial solid buttons use — the only
21
- * value that actually inverts light↔dark for full contrast) instead of the
22
- * generic --ty-color-neutral-strong text-emphasis token. Base/soft neutral
23
- * stay on the generic token like every other flavor.
24
- *
25
- * Depth chrome (solid only): a 1px border + soft drop shadow, both derived
26
- * from the fill via oklch(from …), scaled by --ty-button-depth (0–1). The
27
- * border reads the TONE'S RESTING fill (not the current/hover one) so it
28
- * stays stationary while the fill rises toward it on interaction. Offset
29
- * direction/magnitude is --ty-button-border-l (mode-flipped: no border at
30
- * all for colored solids in dark, a lit edge in light) with its own
31
- * stronger dial for neutral, --ty-button-border-l-neutral (ink has no hue
32
- * to separate it from the canvas). Escape hatches --ty-solid-border-color
33
- * / --ty-solid-border-width outrank the derivation entirely.
34
- *
35
- * Hover escalation (outlined/ghost): :hover/:active/:focus-visible always
36
- * resolve to the -strong tier regardless of starting tone — soft and base
37
- * both rise to the same peak on interaction. Composes with `muted` rather
38
- * than fighting it (see the escalation rules themselves for the exact
39
- * split) — a pressed muted button lands on the same color as a pressed
40
- * plain one of the same flavor.
41
- *
42
- * Per-instance overrides via host CSS variables:
43
- * --ty-button-bg, --ty-button-bg-hover, --ty-button-color, --ty-button-border,
44
- * --ty-solid-border-color, --ty-solid-border-width
45
- */
46
1
  import { FLAVORS } from '../types/common.js';
47
- /* One block per flavor, per appearance. Selectors and tokens are formulaic —
48
- see the section comments below for which token system each appearance uses.
49
-
50
- Each generator takes an optional fallback flavor `fb`: token references
51
- then become var(--ty-*-f, var(--ty-*-fb)). Built-ins pass no fallback
52
- (their tokens always exist); buttonCustomFlavorCss() passes 'neutral' so a
53
- custom flavor with missing tokens degrades to neutral instead of an
54
- invisible button. */
55
2
  const tokenRef = (prefix, f, suffix, fb) => fb
56
- ? `var(${prefix}-${f}${suffix}, var(${prefix}-${fb}${suffix}))`
57
- : `var(${prefix}-${f}${suffix})`;
58
- /* Nudge a fill by an interaction offset (hover/active), tone-aware: each
59
- tone's states derive from the TONE's own fill, not the base fill —
60
- otherwise tone- hover jumps from the soft fill to near-base darkness.
61
- States deliberately do NOT re-derive the text color: fg is decided by
62
- the tone's REST fill and stays stable through interaction — the ±0.04/
63
- 0.08 L nudges keep AA for any rest-passing fill, and re-deriving would
64
- flicker black↔white whenever a nudge crosses the fg threshold. */
3
+ ? `var(${prefix}-${f}${suffix}, var(${prefix}-${fb}${suffix}))`
4
+ : `var(${prefix}-${f}${suffix})`;
65
5
  const nudged = (fillExpr, offsetDial) => `oklch(from ${fillExpr} calc(l + var(--ty-${offsetDial})) c h)`;
66
- /* Fill-relative nudge: moves TOWARD mid-lightness by the dial's magnitude,
67
- whatever the fill. Needed for neutral+ — full-contrast ink sits at an L
68
- extreme in both modes (near-black light / near-white dark), where the
69
- MODE-directional dials clip into the L ceiling/floor and vanish. */
70
6
  const nudgedMid = (fillExpr, offsetDial) => `oklch(from ${fillExpr} calc(l + sign(0.5 - l) * abs(var(--ty-${offsetDial}))) c h)`;
71
- /* Solid rules assign --_solid-bg (the CURRENT fill — swapped by hover/
72
- active) and --_solid-rest (the tone's RESTING fill — set only by rest/
73
- tone rules, never by state rules). The base button.solid rule holds the
74
- single set of formulas that consume them: background follows --_solid-bg;
75
- the depth border derives from --_solid-rest so it stays STATIONARY while
76
- the fill steps up on hover — the fill rises toward the lit edge instead
77
- of dragging the edge along with it. */
78
7
  const solidFlavor = (f, fb) => {
79
- const solid = (suffix) => tokenRef('--ty-solid', f, suffix, fb);
80
- // Neutral reads its own, stronger border offset (no hue separates ink
81
- // from the canvas — see --ty-button-border-l-neutral in tyrell.css).
82
- // Colored flavors must RESET to the plain dial: the bare button.solid
83
- // rule (which matches every solid button) sets the neutral chain.
84
- const borderL = f === 'neutral'
85
- ? ' --_border-l: var(--ty-button-border-l-neutral, var(--ty-button-border-l, -0.15));'
86
- : ' --_border-l: var(--ty-button-border-l, -0.15);';
87
- // Neutral+ is full-contrast ink at an L extreme — its hover/active must
88
- // move toward mid (fill-relative), not in the mode's direction.
89
- const plusNudge = f === 'neutral' ? nudgedMid : nudged;
90
- return `
8
+ const solid = (suffix) => tokenRef('--ty-solid', f, suffix, fb);
9
+ const borderL = f === 'neutral'
10
+ ? ' --_border-l: var(--ty-button-border-l-neutral, var(--ty-button-border-l, -0.15));'
11
+ : ' --_border-l: var(--_muted-border-l, var(--ty-button-border-l, -0.15));';
12
+ const plusNudge = f === 'neutral' ? nudgedMid : nudged;
13
+ return `
91
14
  button.solid.${f} { --_ring: ${tokenRef('--ty-color', f, '', fb)}; --_solid-bg: var(--ty-button-bg, var(--_muted-solid-bg, ${solid('')})); --_solid-rest: var(--ty-button-bg, var(--_muted-solid-bg, ${solid('')}));${borderL} color: var(--ty-button-color, var(--_muted-solid-fg, ${solid('-fg')})); }
92
15
  button.solid.${f}.tone-plus { --_solid-bg: var(--ty-button-bg, var(--_muted-solid-bg, ${solid('-strong')})); --_solid-rest: var(--ty-button-bg, var(--_muted-solid-bg, ${solid('-strong')})); color: var(--ty-button-color, var(--_muted-solid-fg, ${solid('-strong-fg')})); }
93
16
  button.solid.${f}.tone-minus { --_solid-bg: var(--ty-button-bg, var(--_muted-solid-bg, ${solid('-soft')})); --_solid-rest: var(--ty-button-bg, var(--_muted-solid-bg, ${solid('-soft')})); color: var(--ty-button-color, var(--_muted-solid-fg, ${solid('-soft-fg')})); }
@@ -100,439 +23,314 @@ button.solid.${f}.tone-minus:active:not(:disabled) { --_solid-bg: ${nudged(solid
100
23
  `;
101
24
  };
102
25
  const outlinedFlavor = (f, fb) => {
103
- const color = (suffix) => tokenRef('--ty-color', f, suffix, fb);
104
- // Neutral+ only: bare text/border need page contrast, which the
105
- // per-flavor ladder is FOR (it deliberately inverts per mode) — pointing
106
- // base/soft at the solid ink ramp was a real bug (dark-mode contrast
107
- // against the canvas measured 1.01/1.20 — invisible; WCAG minimum is
108
- // 4.5). Strong stays on ink: at 0.93 dark / 0.15 light it's still 16.5:1,
109
- // and matches solid's "+" for the loud-CTA identity that was the ask.
110
- const textInk = (suffix) => (f === 'neutral' && suffix === '-strong' ? `var(--ty-solid-neutral${suffix})` : color(suffix));
111
- return `
26
+ const color = (suffix) => tokenRef('--ty-color', f, suffix, fb);
27
+ const textInk = (suffix) => (f === 'neutral' && suffix === '-strong' ? `var(--ty-solid-neutral${suffix})` : color(suffix));
28
+ return `
112
29
  button.outlined.${f} {
113
- color: var(--ty-button-color, var(--_muted-outlined-line, ${textInk('')}));
114
- border-color: var(--ty-button-border, var(--_muted-outlined-line, ${textInk('')}));
30
+ color: var(--ty-button-color, var(--_muted-outlined-line, ${textInk('')}));
31
+ border-color: var(--ty-button-border, var(--_muted-outlined-line, ${textInk('')}));
115
32
  }
116
33
  button.outlined.${f}.tone-plus {
117
- color: var(--ty-button-color, var(--_muted-outlined-line, ${textInk('-strong')}));
118
- border-color: var(--ty-button-border, var(--_muted-outlined-line, ${textInk('-strong')}));
34
+ color: var(--ty-button-color, var(--_muted-outlined-line, ${textInk('-strong')}));
35
+ border-color: var(--ty-button-border, var(--_muted-outlined-line, ${textInk('-strong')}));
119
36
  }
120
37
  button.outlined.${f}.tone-minus {
121
- color: var(--ty-button-color, var(--_muted-outlined-line, ${textInk('-soft')}));
122
- border-color: var(--ty-button-border, var(--_muted-outlined-line, ${textInk('-soft')}));
38
+ color: var(--ty-button-color, var(--_muted-outlined-line, ${textInk('-soft')}));
39
+ border-color: var(--ty-button-border, var(--_muted-outlined-line, ${textInk('-soft')}));
123
40
  }
124
41
  button.outlined.${f}:hover:not(:disabled) {
125
- background: var(--ty-button-bg-hover, ${tokenRef('--ty-bg', f, '-soft', fb)});
126
- }
127
- /* Rest is whatever tone you picked; hover/active/focus always escalates to
128
- the SAME peak — most-emphasized ("+") color, regardless of starting
129
- tone. tone-plus is already there at rest, so this is a no-op for it.
130
- :hover only, :not(.muted) — muted's hover-reveal is a DIFFERENT contract
131
- (reveal the plain button's RESTING color, tested directly against it);
132
- this rule would outrank that fallback and show strong instead.
133
- :active/:focus-visible apply to muted too (no exclusion) — muted's own
134
- reveal-on-press mechanism has much lower specificity than this rule, so
135
- it simply wins outright, landing muted and plain on the same escalated
136
- color when both are pressed — which is what muted's OWN test expects. */
42
+ background: var(--ty-button-bg-hover, ${tokenRef('--ty-bg', f, '-soft', fb)});
43
+ }
137
44
  button.outlined.${f}:not(.muted):hover:not(:disabled),
138
45
  button.outlined.${f}:active:not(:disabled),
139
46
  button.outlined.${f}:focus-visible {
140
- color: var(--ty-button-color, ${textInk('-strong')});
141
- border-color: var(--ty-button-border, ${textInk('-strong')});
47
+ color: var(--ty-button-color, ${textInk('-strong')});
48
+ border-color: var(--ty-button-border, ${textInk('-strong')});
142
49
  }
143
50
  `;
144
51
  };
145
52
  const ghostFlavor = (f, fb) => {
146
- const color = (suffix) => tokenRef('--ty-color', f, suffix, fb);
147
- // Neutral+ only: bare text needs page contrast, which is what the
148
- // per-flavor text ladder is FOR (it deliberately inverts per mode) — the
149
- // solid ink ramp doesn't need to invert for readability, a fill reads as
150
- // a shape regardless of its own lightness. Pointing base/soft neutral
151
- // text at ink was a real bug: dark-mode contrast against the canvas
152
- // measured 1.01/1.20 (WCAG minimum is 4.5), i.e. invisible. Strong stays
153
- // on ink — at 0.93 dark / 0.15 light it's still far above threshold
154
- // (16.5:1), and matches solid's "+" for the loud-CTA identity that was
155
- // the actual ask.
156
- const ink = (suffix) => (f === 'neutral' && suffix === '-strong' ? `var(--ty-solid-neutral${suffix})` : color(suffix));
157
- return `
53
+ const color = (suffix) => tokenRef('--ty-color', f, suffix, fb);
54
+ const ink = (suffix) => (f === 'neutral' && suffix === '-strong' ? `var(--ty-solid-neutral${suffix})` : color(suffix));
55
+ return `
158
56
  button.ghost.${f} { color: var(--ty-button-color, var(--_muted-ghost-fg, ${ink('')})); }
159
57
  button.ghost.${f}.tone-plus { color: var(--ty-button-color, var(--_muted-ghost-fg, ${ink('-strong')})); }
160
58
  button.ghost.${f}.tone-minus { color: var(--ty-button-color, var(--_muted-ghost-fg, ${ink('-soft')})); }
161
59
  button.ghost.${f}:hover:not(:disabled) {
162
- background: var(--ty-button-bg-hover, ${tokenRef('--ty-bg', f, '-soft', fb)});
60
+ background: var(--ty-button-bg-hover, ${tokenRef('--ty-bg', f, '-soft', fb)});
163
61
  }
164
- /* See outlinedFlavor above — same escalation-to-peak, same :hover-only
165
- :not(.muted) split, text only. */
166
62
  button.ghost.${f}:not(.muted):hover:not(:disabled),
167
63
  button.ghost.${f}:active:not(:disabled),
168
64
  button.ghost.${f}:focus-visible {
169
- color: var(--ty-button-color, ${ink('-strong')});
65
+ color: var(--ty-button-color, ${ink('-strong')});
170
66
  }
171
67
  `;
172
68
  };
173
- /** Rules for one custom (non-built-in) flavor across all three appearances. */
174
69
  export const buttonCustomFlavorCss = (base) => solidFlavor(base, 'neutral') + outlinedFlavor(base, 'neutral') + ghostFlavor(base, 'neutral');
175
70
  export const buttonStyles = `
176
71
  :host {
177
- display: inline-block;
178
- font-family: var(--ty-font-sans);
72
+ display: inline-block;
73
+ font-family: inherit;
179
74
  }
180
-
181
75
  :host([wide]) {
182
- display: flex;
183
- flex-grow: 1;
76
+ display: flex;
77
+ flex-grow: 1;
78
+ }
79
+ :host {
80
+ --ty-button-height: 1.75rem;
81
+ --ty-button-min-width: 4rem;
82
+ --ty-button-pad-x: var(--ty-spacing-2);
83
+ --ty-button-pad-y: 0px;
84
+ --ty-button-font: var(--ty-font-xs);
85
+ --ty-button-leading: var(--ty-leading-xs);
86
+ --ty-button-tracking: var(--ty-tracking-xs);
87
+ --ty-button-gap: var(--ty-spacing-1);
88
+ --ty-button-action-size: 1.5rem;
89
+ --ty-button-action-icon: 0.875rem;
90
+ }
91
+ :host([size="xs"]) {
92
+ --ty-button-height: 1.5rem;
93
+ --ty-button-pad-x: var(--ty-spacing-2);
94
+ --ty-button-pad-y: 0px;
95
+ --ty-button-font: var(--ty-font-xs);
96
+ --ty-button-leading: var(--ty-leading-xs);
97
+ --ty-button-tracking: var(--ty-tracking-xs);
98
+ --ty-button-gap: var(--ty-spacing-1);
99
+ --ty-button-action-size: 1.375rem;
100
+ --ty-button-action-icon: 0.75rem;
101
+ }
102
+ :host([size="md"]) {
103
+ --ty-button-height: 2rem;
104
+ --ty-button-pad-x: var(--ty-spacing-3);
105
+ --ty-button-pad-y: 0.375rem;
106
+ --ty-button-font: var(--ty-font-sm);
107
+ --ty-button-leading: var(--ty-leading-sm);
108
+ --ty-button-tracking: var(--ty-tracking-sm);
109
+ --ty-button-gap: var(--ty-spacing-2);
110
+ --ty-button-action-size: 2rem;
111
+ --ty-button-action-icon: 1rem;
112
+ }
113
+ :host([size="lg"]) {
114
+ --ty-button-height: 2.25rem;
115
+ --ty-button-pad-x: var(--ty-spacing-4);
116
+ --ty-button-pad-y: 0.375rem;
117
+ --ty-button-font: var(--ty-font-sm);
118
+ --ty-button-leading: var(--ty-leading-sm);
119
+ --ty-button-tracking: var(--ty-tracking-sm);
120
+ --ty-button-gap: var(--ty-spacing-2);
121
+ --ty-button-action-size: 2.25rem;
122
+ --ty-button-action-icon: 1.125rem;
123
+ }
124
+ :host([size="xl"]) {
125
+ --ty-button-height: 2.5rem;
126
+ --ty-button-pad-x: var(--ty-spacing-6);
127
+ --ty-button-pad-y: var(--ty-spacing-2);
128
+ --ty-button-font: var(--ty-font-base);
129
+ --ty-button-leading: var(--ty-leading-base);
130
+ --ty-button-tracking: var(--ty-tracking-base);
131
+ --ty-button-gap: var(--ty-spacing-2);
132
+ --ty-button-action-size: 2.5rem;
133
+ --ty-button-action-icon: 1.25rem;
184
134
  }
185
-
186
135
  button {
187
- position: relative;
188
- display: flex;
189
- align-items: center;
190
- justify-content: center;
191
- gap: var(--ty-spacing-1);
192
- font-size: var(--ty-font-xs);
193
- line-height: var(--ty-leading-xs);
194
- letter-spacing: var(--ty-tracking-xs);
195
- /* Mode-flipped weight dial (tyrell.css): medium in light, normal in dark —
196
- light-on-dark text blooms, a fixed semibold read heavy there. Override
197
- with --ty-weight-action at any scope. */
198
- font-weight: var(--ty-weight-action, var(--ty-font-medium));
199
- white-space: nowrap;
200
- cursor: pointer;
201
- user-select: none;
202
- transition: var(--ty-local-transition, background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease);
203
- background: transparent;
204
- color: var(--ty-color-neutral);
205
- border: 1px solid transparent;
206
- }
207
-
136
+ font-family: inherit;
137
+ position: relative;
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: center;
141
+ gap: var(--ty-button-gap);
142
+ height: var(--ty-button-height);
143
+ padding: var(--ty-button-pad-y) var(--ty-button-pad-x);
144
+ font-size: var(--ty-button-font);
145
+ line-height: var(--ty-button-leading);
146
+ letter-spacing: var(--ty-button-tracking);
147
+ font-weight: var(--ty-weight-action, var(--ty-font-medium));
148
+ white-space: nowrap;
149
+ cursor: pointer;
150
+ user-select: none;
151
+ transition: var(--ty-local-transition, background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease);
152
+ background: transparent;
153
+ color: var(--ty-color-neutral);
154
+ border: 1px solid transparent;
155
+ }
208
156
  button:not(.action) {
209
- min-width: 4rem;
157
+ min-width: var(--ty-button-min-width);
210
158
  }
211
-
212
159
  button:not(.pill) {
213
- border-radius: var(--ty-radius-md);
160
+ border-radius: var(--ty-radius-md);
214
161
  }
215
-
216
162
  button.xs:not(.pill) { border-radius: var(--ty-button-radius-xs, var(--ty-radius-md)); }
217
163
  button.sm:not(.pill) { border-radius: var(--ty-button-radius-sm, var(--ty-radius-md)); }
218
164
  button.md:not(.pill) { border-radius: var(--ty-button-radius-md, var(--ty-radius-md)); }
219
165
  button.lg:not(.pill) { border-radius: var(--ty-button-radius-lg, var(--ty-radius-md)); }
220
166
  button.xl:not(.pill) { border-radius: var(--ty-button-radius-xl, var(--ty-radius-md)); }
221
-
222
167
  button:focus-visible {
223
- outline: none;
168
+ outline: none;
224
169
  }
225
-
226
170
  button:disabled {
227
- cursor: not-allowed;
228
- opacity: 0.6;
229
- }
230
-
231
- /* ===== LOADING STATE =====
232
- Spinner overlays the button center; original content kept in flow but
233
- hidden via visibility so width/height are preserved (no layout shift).
234
- */
171
+ cursor: not-allowed;
172
+ opacity: 0.6;
173
+ }
235
174
  .loader-icon {
236
- display: none;
237
- align-items: center;
238
- justify-content: center;
239
- flex-shrink: 0;
240
- width: 1em;
241
- height: 1em;
242
- color: currentColor;
175
+ display: none;
176
+ align-items: center;
177
+ justify-content: center;
178
+ flex-shrink: 0;
179
+ width: 1em;
180
+ height: 1em;
181
+ color: currentColor;
243
182
  }
244
183
  .loader-icon svg {
245
- width: 100%;
246
- height: 100%;
184
+ width: 100%;
185
+ height: 100%;
247
186
  }
248
187
  button.loading {
249
- cursor: wait;
188
+ cursor: wait;
250
189
  }
251
190
  button.loading > *:not(.loader-icon) {
252
- visibility: hidden;
191
+ visibility: hidden;
253
192
  }
254
193
  button.loading > .loader-icon {
255
- display: inline-flex;
256
- position: absolute;
257
- top: 50%;
258
- left: 50%;
259
- transform: translate(-50%, -50%);
260
- animation: ty-button-spin 0.7s linear infinite;
194
+ display: inline-flex;
195
+ position: absolute;
196
+ top: 50%;
197
+ left: 50%;
198
+ transform: translate(-50%, -50%);
199
+ animation: ty-button-spin 0.7s linear infinite;
261
200
  }
262
201
  @keyframes ty-button-spin {
263
- to { transform: translate(-50%, -50%) rotate(360deg); }
202
+ to { transform: translate(-50%, -50%) rotate(360deg); }
264
203
  }
265
204
  @media (prefers-reduced-motion: reduce) {
266
- button.loading > .loader-icon {
267
- animation-duration: 1.6s;
268
- }
205
+ button.loading > .loader-icon {
206
+ animation-duration: 1.6s;
207
+ }
269
208
  }
270
-
271
209
  ::slotted(*) {
272
- display: inline-flex;
273
- align-items: center;
274
- justify-content: center;
210
+ display: inline-flex;
211
+ align-items: center;
212
+ justify-content: center;
275
213
  }
276
-
277
214
  ::slotted(ty-icon) {
278
- flex-shrink: 0;
215
+ flex-shrink: 0;
279
216
  }
280
-
281
217
  :host([wide]) button {
282
- flex-grow: 1;
283
- }
284
-
285
- /* ===== SIZES =====
286
- Buttons run a 4px ladder (24-40px). Shared scale with fields
287
- (--ty-size-sm/md/lg — see tyrell.css): field sm = button md (32px),
288
- field md = button lg (36px), field lg = button xl (40px) — a button
289
- and a field of the paired size sit flush, same height, in a row. */
290
-
291
- button.xs {
292
- padding: 0 var(--ty-spacing-2);
293
- font-size: var(--ty-font-xs);
294
- line-height: var(--ty-leading-xs);
295
- letter-spacing: var(--ty-tracking-xs);
296
- gap: var(--ty-spacing-1);
297
- height: 1.5rem; /* 24px */
298
- }
299
-
300
- button.sm {
301
- padding: 0 var(--ty-spacing-2);
302
- font-size: var(--ty-font-xs);
303
- line-height: var(--ty-leading-xs);
304
- letter-spacing: var(--ty-tracking-xs);
305
- gap: var(--ty-spacing-1);
306
- height: 1.75rem; /* 28px */
307
- }
308
-
309
- button.md {
310
- padding: 0.375rem var(--ty-spacing-3);
311
- font-size: var(--ty-font-sm);
312
- line-height: var(--ty-leading-sm);
313
- letter-spacing: var(--ty-tracking-sm);
314
- gap: var(--ty-spacing-2);
315
- height: 2rem; /* 32px — matches field sm */
316
- }
317
-
318
- button.lg {
319
- padding: 0.375rem var(--ty-spacing-4);
320
- font-size: var(--ty-font-sm);
321
- line-height: var(--ty-leading-sm);
322
- letter-spacing: var(--ty-tracking-sm);
323
- gap: var(--ty-spacing-2);
324
- height: 2.25rem; /* 36px — matches field md */
325
- }
326
-
327
- button.xl {
328
- padding: var(--ty-spacing-2) var(--ty-spacing-6);
329
- font-size: var(--ty-font-base);
330
- line-height: var(--ty-leading-base);
331
- letter-spacing: var(--ty-tracking-base);
332
- gap: var(--ty-spacing-2);
333
- height: 2.5rem; /* 40px — matches field lg */
334
- }
335
-
336
- /* ===== ACTION (icon-only square) ===== */
337
-
218
+ flex-grow: 1;
219
+ }
338
220
  button.action {
339
- gap: 0px !important;
340
- height: 2rem;
341
- width: 2rem;
342
- padding: 0px !important;
221
+ gap: 0px !important;
222
+ height: var(--ty-button-action-size);
223
+ width: var(--ty-button-action-size);
224
+ padding: 0px !important;
343
225
  }
344
-
345
226
  button.action ::slotted(ty-icon) {
346
- height: 1rem;
347
- width: 1rem;
348
- }
349
-
350
- button.action.xs { height: 1.375rem; width: 1.375rem; }
351
- button.action.xs ::slotted(ty-icon) { height: 0.75rem; width: 0.75rem; }
352
-
353
- button.action.sm { height: 1.5rem; width: 1.5rem; }
354
- button.action.sm ::slotted(ty-icon) { height: 0.875rem; width: 0.875rem; }
355
-
356
- button.action.lg { height: 2.25rem; width: 2.25rem; }
357
- button.action.lg ::slotted(ty-icon) { height: 1.125rem; width: 1.125rem; }
358
-
359
- button.action.xl { height: 2.5rem; width: 2.5rem; }
360
- button.action.xl ::slotted(ty-icon) { height: 1.25rem; width: 1.25rem; }
361
-
362
- /* ===== MUTED =====
363
- Show the neutral tokens at rest instead of the flavor color; reveal the
364
- real flavor on interaction. Implemented as a fallback tier the flavor
365
- rules above already read (--_muted-*), which sits behind the public
366
- --ty-button-{bg,color,border} override slots and ahead of the flavor's
367
- own token — so a custom brand color still wins, muted or not. Unsetting
368
- --_muted-* on interaction falls the var() chain through to whatever's
369
- next (the user's override, or the real flavor color).
370
- Hover reveal is gated to real pointers (touch has no hover); :active /
371
- :focus-visible cover the tap case so touch still gets the color on press. */
372
-
373
- /* --_muted-solid-* reads the SOLID neutral ink tokens — a fill's L doesn't
374
- need page contrast. --_muted-outlined-line / --_muted-ghost-fg are bare
375
- text/border and DO — they stay on the regular text ladder (it already
376
- inverts correctly per mode), except tone-plus, which is legible AND
377
- loud on the ink dial (16.5:1 dark, matches solid's "+" identity). Base/
378
- soft on ink measured 1.01/1.20 dark contrast against the canvas —
379
- invisible; WCAG minimum is 4.5. */
227
+ height: var(--ty-button-action-icon);
228
+ width: var(--ty-button-action-icon);
229
+ }
380
230
  button.muted {
381
- --_muted-solid-bg: var(--ty-solid-neutral);
382
- --_muted-solid-fg: var(--ty-solid-neutral-fg);
383
- --_muted-outlined-line: var(--ty-color-neutral);
384
- --_muted-ghost-fg: var(--ty-color-neutral);
231
+ --_muted-solid-bg: var(--ty-solid-neutral);
232
+ --_muted-solid-fg: var(--ty-solid-neutral-fg);
233
+ --_muted-outlined-line: var(--ty-color-neutral);
234
+ --_muted-ghost-fg: var(--ty-color-neutral);
235
+ --_muted-border-l: var(--ty-button-border-l-neutral, var(--ty-button-border-l, -0.15));
385
236
  }
386
237
  button.muted.tone-plus {
387
- --_muted-solid-bg: var(--ty-solid-neutral-strong);
388
- /* fg must track the tone's fill: neutral+ is inverted ink in dark mode
389
- (white fill), so inheriting base neutral's white fg would be
390
- white-on-white. */
391
- --_muted-solid-fg: var(--ty-solid-neutral-strong-fg);
392
- --_muted-outlined-line: var(--ty-solid-neutral-strong);
393
- --_muted-ghost-fg: var(--ty-solid-neutral-strong);
238
+ --_muted-solid-bg: var(--ty-solid-neutral-strong);
239
+ --_muted-solid-fg: var(--ty-solid-neutral-strong-fg);
240
+ --_muted-outlined-line: var(--ty-solid-neutral-strong);
241
+ --_muted-ghost-fg: var(--ty-solid-neutral-strong);
394
242
  }
395
243
  button.muted.tone-minus {
396
- --_muted-solid-bg: var(--ty-solid-neutral-soft);
397
- --_muted-solid-fg: var(--ty-solid-neutral-soft-fg);
398
- --_muted-outlined-line: var(--ty-color-neutral-soft);
399
- --_muted-ghost-fg: var(--ty-color-neutral-soft);
244
+ --_muted-solid-bg: var(--ty-solid-neutral-soft);
245
+ --_muted-solid-fg: var(--ty-solid-neutral-soft-fg);
246
+ --_muted-outlined-line: var(--ty-color-neutral-soft);
247
+ --_muted-ghost-fg: var(--ty-color-neutral-soft);
400
248
  }
401
249
  @media (hover: hover) and (pointer: fine) {
402
- button.muted:hover:not(:disabled) {
403
- --_muted-solid-bg: unset;
404
- --_muted-solid-fg: unset;
405
- --_muted-outlined-line: unset;
406
- --_muted-ghost-fg: unset;
407
- }
250
+ button.muted:hover:not(:disabled) {
251
+ --_muted-solid-bg: unset;
252
+ --_muted-solid-fg: unset;
253
+ --_muted-outlined-line: unset;
254
+ --_muted-ghost-fg: unset;
255
+ --_muted-border-l: unset;
256
+ }
408
257
  }
409
258
  button.muted:active:not(:disabled),
410
259
  button.muted:focus-visible:not(:disabled) {
411
- --_muted-solid-bg: unset;
412
- --_muted-solid-fg: unset;
413
- --_muted-outlined-line: unset;
414
- --_muted-ghost-fg: unset;
415
- }
416
-
417
- /* ===== PILL ===== */
418
-
260
+ --_muted-solid-bg: unset;
261
+ --_muted-solid-fg: unset;
262
+ --_muted-outlined-line: unset;
263
+ --_muted-ghost-fg: unset;
264
+ --_muted-border-l: unset;
265
+ }
419
266
  button.pill {
420
- border-radius: 9999px;
421
- padding-left: 1.25em;
422
- padding-right: 1.25em;
267
+ border-radius: 9999px;
268
+ padding-left: 1.25em;
269
+ padding-right: 1.25em;
423
270
  }
424
-
425
271
  button.pill.xs { padding-left: 1em; padding-right: 1em; }
426
272
  button.pill.sm { padding-left: 1.125em; padding-right: 1.125em; }
427
273
  button.pill.lg { padding-left: 1.5em; padding-right: 1.5em; }
428
274
  button.pill.xl { padding-left: 1.75em; padding-right: 1.75em; }
429
-
430
275
  button.pill:has(ty-icon:only-child),
431
276
  button.pill:has(slot[name="start"]:only-child),
432
277
  button.pill:has(slot[name="end"]:only-child) {
433
- padding: 0;
434
- aspect-ratio: 1;
435
- min-width: var(--ty-size-md);
278
+ padding: 0;
279
+ aspect-ratio: 1;
280
+ min-width: var(--ty-size-md);
436
281
  }
437
-
438
282
  button.pill.xs:has(ty-icon:only-child) { min-width: 1.375rem; min-height: 1.375rem; }
439
283
  button.pill.sm:has(ty-icon:only-child) { min-width: 1.5rem; min-height: 1.5rem; }
440
284
  button.pill.md:has(ty-icon:only-child) { min-width: 2rem; min-height: 2rem; }
441
285
  button.pill.lg:has(ty-icon:only-child) { min-width: 2.25rem; min-height: 2.25rem; }
442
286
  button.pill.xl:has(ty-icon:only-child) { min-width: 2.5rem; min-height: 2.5rem; }
443
-
444
- /* ============================================================
445
- SOLID — flat: one variable per segment. No color math here; all the
446
- --ty-solid-{flavor}-{hover,active,strong,soft} tokens are DERIVED in
447
- tyrell-theme.css (OKLCH) or set literally in tyrell.css. Override any
448
- single token to recolor that one segment. Bare .solid = custom-flavor
449
- fallback, themable via --ty-button-{bg,bg-hover,color}.
450
- ============================================================ */
451
-
452
287
  button.solid {
453
- --_ring: var(--ty-color-neutral);
454
- --_solid-bg: var(--ty-button-bg, var(--ty-solid-neutral));
455
- --_solid-rest: var(--ty-button-bg, var(--ty-solid-neutral));
456
- /* Bare .solid is the flavorless default = neutral ink, so it takes the
457
- neutral border offset; colored flavor rules reset this to the plain
458
- dial. */
459
- --_border-l: var(--ty-button-border-l-neutral, var(--ty-button-border-l, -0.15));
460
- background: var(--_solid-bg);
461
- color: var(--ty-button-color, var(--ty-solid-neutral-fg));
462
- /* Depth chrome — border + drop shadow, both scaled by --ty-button-depth.
463
- (A top-edge sheen — inset 0 1px 0 <lightened fill> — used to live here
464
- too; dropped, it read as a dizzying bright line rather than a subtle
465
- highlight.) depth 0 = border matches fill exactly and the shadow goes
466
- transparent — pixel-flat, but the 1px border stays in the layout so
467
- toggling depth never shifts geometry (and solid now shares the same
468
- box as outlined/ghost, which always had a 1px border).
469
- --ty-button-border-l flips sign with the mode (tyrell.css): dark mode
470
- runs a LIGHTER-than-fill hairline (fills read washed-out against dark
471
- surfaces without a lit edge), light mode a slightly darker one.
472
- Direct escape hatches outrank the derivation: --ty-solid-border-color
473
- pins the border to ANY color (the L-offset dials stop mattering), and
474
- --ty-solid-border-width changes thickness (widths ≠ 1px do shift
475
- geometry — that's inherent).
476
- Browsers without relative color drop these declarations: border falls
477
- back to the base button's 1px transparent, shadows to none. */
478
- border: var(--ty-solid-border-width, 1px) solid
479
- var(--ty-solid-border-color,
480
- oklch(from var(--_solid-rest, var(--_solid-bg)) calc(l + var(--_border-l, -0.15) * var(--ty-button-depth, 0.35)) c h));
481
- box-shadow: 0 1px 2px oklch(0 0 0 / calc(0.4 * var(--ty-button-depth, 0.35)));
288
+ --_ring: var(--ty-color-neutral);
289
+ --_solid-bg: var(--ty-button-bg, var(--ty-solid-neutral));
290
+ --_solid-rest: var(--ty-button-bg, var(--ty-solid-neutral));
291
+ --_border-l: var(--ty-button-border-l-neutral, var(--ty-button-border-l, -0.15));
292
+ background: var(--_solid-bg);
293
+ color: var(--ty-button-color, var(--ty-solid-neutral-fg));
294
+ border: var(--ty-solid-border-width, 1px) solid
295
+ var(--ty-solid-border-color,
296
+ oklch(from var(--_solid-rest, var(--_solid-bg)) calc(l + var(--_border-l, -0.15) * var(--ty-button-depth, 0.35)) c h));
297
+ box-shadow: 0 1px 2px oklch(0 0 0 / calc(0.4 * var(--ty-button-depth, 0.35)));
482
298
  }
483
299
  button.solid.tone-plus { --_solid-bg: var(--ty-button-bg, var(--ty-solid-neutral-strong)); --_solid-rest: var(--ty-button-bg, var(--ty-solid-neutral-strong)); color: var(--ty-button-color, var(--ty-solid-neutral-strong-fg)); }
484
300
  button.solid.tone-minus { --_solid-bg: var(--ty-button-bg, var(--ty-solid-neutral-soft)); --_solid-rest: var(--ty-button-bg, var(--ty-solid-neutral-soft)); color: var(--ty-button-color, var(--ty-solid-neutral-soft-fg)); }
485
301
  button.solid:hover:not(:disabled) { --_solid-bg: var(--ty-button-bg-hover, var(--ty-solid-neutral-hover)); }
486
302
  button.solid:active:not(:disabled) { --_solid-bg: var(--ty-solid-neutral-active); }
487
303
  button.solid.tone-plus:hover:not(:disabled) {
488
- --_solid-bg: var(--ty-button-bg-hover, oklch(from var(--ty-solid-neutral-strong) calc(l + sign(0.5 - l) * abs(var(--ty-solid-hover-l))) c h));
304
+ --_solid-bg: var(--ty-button-bg-hover, oklch(from var(--ty-solid-neutral-strong) calc(l + sign(0.5 - l) * abs(var(--ty-solid-hover-l))) c h));
489
305
  }
490
306
  button.solid.tone-plus:active:not(:disabled) {
491
- --_solid-bg: oklch(from var(--ty-solid-neutral-strong) calc(l + sign(0.5 - l) * abs(var(--ty-solid-active-l))) c h);
307
+ --_solid-bg: oklch(from var(--ty-solid-neutral-strong) calc(l + sign(0.5 - l) * abs(var(--ty-solid-active-l))) c h);
492
308
  }
493
309
  button.solid.tone-minus:hover:not(:disabled) {
494
- --_solid-bg: var(--ty-button-bg-hover, oklch(from var(--ty-solid-neutral-soft) calc(l + var(--ty-solid-hover-l)) c h));
310
+ --_solid-bg: var(--ty-button-bg-hover, oklch(from var(--ty-solid-neutral-soft) calc(l + var(--ty-solid-hover-l)) c h));
495
311
  }
496
312
  button.solid.tone-minus:active:not(:disabled) {
497
- --_solid-bg: oklch(from var(--ty-solid-neutral-soft) calc(l + var(--ty-solid-active-l)) c h);
313
+ --_solid-bg: oklch(from var(--ty-solid-neutral-soft) calc(l + var(--ty-solid-active-l)) c h);
498
314
  }
499
-
500
315
  ${FLAVORS.map((f) => solidFlavor(f)).join('')}
501
316
  button.solid:focus-visible {
502
- box-shadow: 0 0 0 2px var(--ty-focus-ring-gap), 0 0 0 4px var(--_ring);
503
- }
504
-
505
- /* ============================================================
506
- OUTLINED — transparent bg, text === border (uses --ty-color-*)
507
- Bare .outlined rule = fallback for custom flavors. For outlined,
508
- text is bound to border color (the rule "text === border"), so the
509
- fallback chain prefers --ty-button-border, then --ty-button-color.
510
- ============================================================ */
511
-
317
+ box-shadow: 0 0 0 2px var(--ty-focus-ring-gap), 0 0 0 4px var(--_ring);
318
+ }
512
319
  button.outlined {
513
- background: transparent;
514
- color: var(--ty-button-border, var(--ty-button-color, var(--ty-color-neutral)));
515
- border-color: var(--ty-button-border, var(--ty-button-color, var(--ty-color-neutral)));
320
+ background: transparent;
321
+ color: var(--ty-button-border, var(--ty-button-color, var(--ty-color-neutral)));
322
+ border-color: var(--ty-button-border, var(--ty-button-color, var(--ty-color-neutral)));
516
323
  }
517
324
  button.outlined:hover:not(:disabled) {
518
- background: var(--ty-button-bg-hover, var(--ty-bg-neutral-soft));
325
+ background: var(--ty-button-bg-hover, var(--ty-bg-neutral-soft));
519
326
  }
520
-
521
327
  ${FLAVORS.map((f) => outlinedFlavor(f)).join('')}
522
-
523
- /* ============================================================
524
- GHOST — text only, hover bg (uses --ty-color-* + --ty-bg-*-soft)
525
- Bare .ghost rule = fallback for custom flavors.
526
- ============================================================ */
527
-
528
328
  button.ghost {
529
- background: transparent;
530
- border: none;
531
- color: var(--ty-button-color, var(--ty-color-neutral));
329
+ background: transparent;
330
+ border: none;
331
+ color: var(--ty-button-color, var(--ty-color-neutral));
532
332
  }
533
333
  button.ghost:hover:not(:disabled) {
534
- background: var(--ty-button-bg-hover, var(--ty-bg-neutral-soft));
334
+ background: var(--ty-button-bg-hover, var(--ty-bg-neutral-soft));
535
335
  }
536
-
537
336
  ${FLAVORS.map((f) => ghostFlavor(f)).join('')}`;
538
- //# sourceMappingURL=button.js.map