@bug-on/md3-react 2.0.2 → 3.0.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 (296) hide show
  1. package/.turbo/turbo-build.log +33 -0
  2. package/CHANGELOG.md +55 -0
  3. package/dist/index.css +23 -0
  4. package/dist/index.css.d.ts +2 -0
  5. package/dist/index.d.mts +6127 -0
  6. package/dist/index.d.ts +6127 -69
  7. package/dist/index.js +2536 -665
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +2443 -603
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/material-symbols-cdn.css.d.ts +2 -0
  12. package/dist/material-symbols-self-hosted.css.d.ts +2 -0
  13. package/dist/typography.css.d.ts +2 -0
  14. package/package.json +23 -19
  15. package/scripts/copy-assets.js +82 -0
  16. package/src/assets/fonts/GoogleSansFlex-VariableFont.woff2 +0 -0
  17. package/src/assets/fonts/MaterialSymbolsOutlined-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
  18. package/src/assets/fonts/MaterialSymbolsRounded-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
  19. package/src/assets/fonts/MaterialSymbolsSharp-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
  20. package/src/assets/loading-indicator.svg +19 -0
  21. package/src/assets/material-symbols-cdn.css +65 -0
  22. package/src/assets/material-symbols-self-hosted.css +90 -0
  23. package/src/css.d.ts +20 -0
  24. package/{dist/hooks/index.d.ts → src/hooks/index.ts} +1 -0
  25. package/src/hooks/useClickOutside.ts +37 -0
  26. package/src/hooks/useMediaQuery.ts +28 -0
  27. package/src/hooks/useRipple.ts +88 -0
  28. package/src/index.css +23 -0
  29. package/src/index.ts +349 -0
  30. package/src/lib/material-symbols-preconnect.tsx +82 -0
  31. package/src/lib/theme-utils.ts +180 -0
  32. package/src/lib/utils.ts +6 -0
  33. package/src/test/button.test.tsx +59 -0
  34. package/src/test/icon.test.tsx +91 -0
  35. package/src/test/loading-indicator.test.tsx +128 -0
  36. package/src/test/progress-indicator.test.tsx +306 -0
  37. package/src/test/setup.ts +80 -0
  38. package/src/test/typography.test.tsx +206 -0
  39. package/src/types/index.ts +7 -0
  40. package/src/types/md3.ts +31 -0
  41. package/src/ui/Text.tsx +60 -0
  42. package/src/ui/__snapshots__/divider.test.tsx.snap +63 -0
  43. package/src/ui/app-bar/app-bar-column.tsx +99 -0
  44. package/src/ui/app-bar/app-bar-item-button.tsx +71 -0
  45. package/src/ui/app-bar/app-bar-items.test.tsx +89 -0
  46. package/src/ui/app-bar/app-bar-overflow-indicator.tsx +108 -0
  47. package/src/ui/app-bar/app-bar-row.tsx +104 -0
  48. package/src/ui/app-bar/app-bar.test.tsx +87 -0
  49. package/src/ui/app-bar/app-bar.tokens.ts +223 -0
  50. package/src/ui/app-bar/app-bar.types.ts +441 -0
  51. package/src/ui/app-bar/bottom-app-bar.test.tsx +42 -0
  52. package/src/ui/app-bar/bottom-app-bar.tsx +84 -0
  53. package/src/ui/app-bar/docked-toolbar.test.tsx +34 -0
  54. package/src/ui/app-bar/docked-toolbar.tsx +54 -0
  55. package/src/ui/app-bar/flexible-app-bar.test.tsx +75 -0
  56. package/src/ui/app-bar/hooks/use-app-bar-scroll.ts +110 -0
  57. package/src/ui/app-bar/hooks/use-flexible-app-bar.ts +123 -0
  58. package/{dist/ui/app-bar/index.d.ts → src/ui/app-bar/index.ts} +35 -2
  59. package/src/ui/app-bar/large-flexible-app-bar.tsx +165 -0
  60. package/src/ui/app-bar/medium-flexible-app-bar.tsx +167 -0
  61. package/src/ui/app-bar/search-app-bar.test.tsx +49 -0
  62. package/src/ui/app-bar/search-app-bar.tsx +176 -0
  63. package/src/ui/app-bar/search-view.tsx +227 -0
  64. package/src/ui/app-bar/small-app-bar.test.tsx +48 -0
  65. package/src/ui/app-bar/small-app-bar.tsx +203 -0
  66. package/src/ui/badge.test.tsx +345 -0
  67. package/src/ui/badge.tsx +282 -0
  68. package/src/ui/button-group.test.tsx +71 -0
  69. package/src/ui/button-group.tsx +350 -0
  70. package/src/ui/button.test.tsx +297 -0
  71. package/src/ui/button.tsx +669 -0
  72. package/src/ui/card.test.tsx +187 -0
  73. package/src/ui/card.tsx +259 -0
  74. package/src/ui/checkbox.test.tsx +423 -0
  75. package/src/ui/checkbox.tsx +525 -0
  76. package/src/ui/chip.test.tsx +292 -0
  77. package/src/ui/chip.tsx +548 -0
  78. package/src/ui/code-block.tsx +219 -0
  79. package/src/ui/dialog.test.tsx +300 -0
  80. package/src/ui/dialog.tsx +384 -0
  81. package/src/ui/divider.test.tsx +314 -0
  82. package/src/ui/divider.tsx +412 -0
  83. package/src/ui/drawer.tsx +240 -0
  84. package/src/ui/fab-menu.test.tsx +494 -0
  85. package/src/ui/fab-menu.tsx +739 -0
  86. package/src/ui/fab.test.tsx +232 -0
  87. package/src/ui/fab.tsx +505 -0
  88. package/src/ui/icon-button.test.tsx +515 -0
  89. package/src/ui/icon-button.tsx +525 -0
  90. package/src/ui/icon.test.tsx +197 -0
  91. package/src/ui/icon.tsx +179 -0
  92. package/src/ui/loading-indicator.test.tsx +73 -0
  93. package/src/ui/loading-indicator.tsx +312 -0
  94. package/src/ui/menu/context-menu.tsx +275 -0
  95. package/src/ui/menu/index.ts +77 -0
  96. package/src/ui/menu/menu-animations.ts +102 -0
  97. package/src/ui/menu/menu-context.tsx +99 -0
  98. package/src/ui/menu/menu-divider.tsx +47 -0
  99. package/src/ui/menu/menu-group.tsx +200 -0
  100. package/src/ui/menu/menu-item.tsx +294 -0
  101. package/src/ui/menu/menu-tokens.ts +208 -0
  102. package/src/ui/menu/menu-types.ts +313 -0
  103. package/src/ui/menu/menu.test.tsx +624 -0
  104. package/src/ui/menu/menu.tsx +289 -0
  105. package/src/ui/menu/sub-menu.tsx +223 -0
  106. package/src/ui/menu/vertical-menu.tsx +382 -0
  107. package/src/ui/navigation-rail.test.tsx +404 -0
  108. package/src/ui/navigation-rail.tsx +604 -0
  109. package/src/ui/progress-indicator/circular.tsx +248 -0
  110. package/src/ui/progress-indicator/hooks.ts +51 -0
  111. package/{dist/ui/progress-indicator/index.d.ts → src/ui/progress-indicator/index.tsx} +20 -2
  112. package/src/ui/progress-indicator/linear-flat.tsx +83 -0
  113. package/src/ui/progress-indicator/linear-wavy.tsx +243 -0
  114. package/src/ui/progress-indicator/linear.tsx +143 -0
  115. package/src/ui/progress-indicator/types.ts +158 -0
  116. package/src/ui/progress-indicator/utils.ts +73 -0
  117. package/src/ui/radio-button.test.tsx +407 -0
  118. package/src/ui/radio-button.tsx +551 -0
  119. package/src/ui/ripple.test.tsx +72 -0
  120. package/src/ui/ripple.tsx +234 -0
  121. package/src/ui/scroll-area.test.tsx +58 -0
  122. package/src/ui/scroll-area.tsx +139 -0
  123. package/src/ui/search/animated-placeholder.tsx +145 -0
  124. package/src/ui/search/hooks/use-search-keyboard.test.ts +202 -0
  125. package/src/ui/search/hooks/use-search-keyboard.ts +104 -0
  126. package/src/ui/search/hooks/use-search-view-focus.test.ts +96 -0
  127. package/src/ui/search/hooks/use-search-view-focus.ts +24 -0
  128. package/src/ui/search/index.ts +44 -0
  129. package/src/ui/search/search-bar.tsx +220 -0
  130. package/src/ui/search/search-context.tsx +42 -0
  131. package/src/ui/search/search-view-docked.tsx +194 -0
  132. package/src/ui/search/search-view-fullscreen.tsx +247 -0
  133. package/src/ui/search/search.test.tsx +233 -0
  134. package/src/ui/search/search.tokens.ts +134 -0
  135. package/src/ui/search/search.tsx +131 -0
  136. package/src/ui/search/search.types.ts +154 -0
  137. package/src/ui/search/trailing-action.tsx +49 -0
  138. package/src/ui/shared/constants.ts +122 -0
  139. package/{dist/ui/shared/touch-target.d.ts → src/ui/shared/touch-target.tsx} +13 -1
  140. package/src/ui/slider/hooks/useSliderMath.ts +195 -0
  141. package/{dist/ui/slider/index.d.ts → src/ui/slider/index.ts} +12 -1
  142. package/src/ui/slider/range-slider.tsx +561 -0
  143. package/src/ui/slider/slider-thumb.tsx +379 -0
  144. package/src/ui/slider/slider-track.tsx +912 -0
  145. package/src/ui/slider/slider.tokens.ts +189 -0
  146. package/src/ui/slider/slider.tsx +259 -0
  147. package/src/ui/slider/slider.types.ts +288 -0
  148. package/src/ui/snackbar/index.ts +20 -0
  149. package/src/ui/snackbar/snackbar.test.tsx +338 -0
  150. package/src/ui/snackbar/snackbar.tsx +476 -0
  151. package/{dist/ui/switch/index.d.ts → src/ui/switch/index.ts} +1 -0
  152. package/src/ui/switch/switch.stories.tsx +309 -0
  153. package/src/ui/switch/switch.test.tsx +243 -0
  154. package/src/ui/switch/switch.tokens.ts +89 -0
  155. package/src/ui/switch/switch.tsx +504 -0
  156. package/src/ui/switch/switch.types.ts +62 -0
  157. package/{dist/ui/tabs/index.d.ts → src/ui/tabs/index.ts} +8 -1
  158. package/src/ui/tabs/tab.tsx +407 -0
  159. package/src/ui/tabs/tabs-content.tsx +89 -0
  160. package/src/ui/tabs/tabs-list.tsx +146 -0
  161. package/src/ui/tabs/tabs.test.tsx +290 -0
  162. package/src/ui/tabs/tabs.tokens.ts +121 -0
  163. package/src/ui/tabs/tabs.tsx +229 -0
  164. package/src/ui/tabs/tabs.types.ts +185 -0
  165. package/{dist/ui/text-field/index.d.ts → src/ui/text-field/index.ts} +8 -1
  166. package/src/ui/text-field/subcomponents/active-indicator.tsx +67 -0
  167. package/src/ui/text-field/subcomponents/floating-label.tsx +161 -0
  168. package/src/ui/text-field/subcomponents/leading-icon.tsx +46 -0
  169. package/src/ui/text-field/subcomponents/outline-container.tsx +170 -0
  170. package/src/ui/text-field/subcomponents/prefix-suffix.tsx +59 -0
  171. package/src/ui/text-field/subcomponents/supporting-text.tsx +145 -0
  172. package/src/ui/text-field/subcomponents/trailing-icon.tsx +199 -0
  173. package/src/ui/text-field/text-field.test.tsx +454 -0
  174. package/src/ui/text-field/text-field.tokens.ts +104 -0
  175. package/src/ui/text-field/text-field.tsx +548 -0
  176. package/src/ui/text-field/text-field.types.ts +180 -0
  177. package/src/ui/theme-provider/index.tsx +190 -0
  178. package/src/ui/toc.test.tsx +108 -0
  179. package/src/ui/toc.tsx +172 -0
  180. package/src/ui/tooltip/plain-tooltip.tsx +63 -0
  181. package/src/ui/tooltip/rich-tooltip.tsx +94 -0
  182. package/src/ui/tooltip/tooltip-box.tsx +266 -0
  183. package/src/ui/tooltip/tooltip-caret-shape.tsx +68 -0
  184. package/src/ui/tooltip/tooltip.tokens.ts +26 -0
  185. package/src/ui/tooltip/tooltip.types.ts +70 -0
  186. package/src/ui/tooltip/use-tooltip-position.ts +208 -0
  187. package/src/ui/tooltip/use-tooltip-state.ts +41 -0
  188. package/src/ui/typography/__tests__/typography.test.tsx +170 -0
  189. package/{dist/ui/typography/index.d.ts → src/ui/typography/index.ts} +21 -3
  190. package/src/ui/typography/type-scale-tokens.ts +205 -0
  191. package/src/ui/typography/typography-key-tokens.ts +43 -0
  192. package/src/ui/typography/typography-tokens.ts +360 -0
  193. package/src/ui/typography/typography.css +22 -0
  194. package/src/ui/typography/typography.tsx +559 -0
  195. package/test-render.tsx +4 -0
  196. package/test-shadow.html +26 -0
  197. package/test_output.txt +164 -0
  198. package/test_output_v2.txt +5 -0
  199. package/tsconfig.build.json +10 -0
  200. package/tsconfig.json +18 -0
  201. package/tsup.config.ts +20 -0
  202. package/vitest.config.ts +11 -0
  203. package/dist/hooks/useMediaQuery.d.ts +0 -11
  204. package/dist/hooks/useRipple.d.ts +0 -26
  205. package/dist/lib/material-symbols-preconnect.d.ts +0 -42
  206. package/dist/lib/theme-utils.d.ts +0 -63
  207. package/dist/lib/utils.d.ts +0 -2
  208. package/dist/types/index.d.ts +0 -1
  209. package/dist/types/md3.d.ts +0 -14
  210. package/dist/ui/app-bar/app-bar-column.d.ts +0 -28
  211. package/dist/ui/app-bar/app-bar-item-button.d.ts +0 -16
  212. package/dist/ui/app-bar/app-bar-overflow-indicator.d.ts +0 -18
  213. package/dist/ui/app-bar/app-bar-row.d.ts +0 -36
  214. package/dist/ui/app-bar/app-bar.tokens.d.ts +0 -184
  215. package/dist/ui/app-bar/app-bar.types.d.ts +0 -392
  216. package/dist/ui/app-bar/bottom-app-bar.d.ts +0 -31
  217. package/dist/ui/app-bar/docked-toolbar.d.ts +0 -25
  218. package/dist/ui/app-bar/hooks/use-app-bar-scroll.d.ts +0 -42
  219. package/dist/ui/app-bar/hooks/use-flexible-app-bar.d.ts +0 -37
  220. package/dist/ui/app-bar/large-flexible-app-bar.d.ts +0 -26
  221. package/dist/ui/app-bar/medium-flexible-app-bar.d.ts +0 -28
  222. package/dist/ui/app-bar/search-app-bar.d.ts +0 -43
  223. package/dist/ui/app-bar/search-view.d.ts +0 -54
  224. package/dist/ui/app-bar/small-app-bar.d.ts +0 -37
  225. package/dist/ui/badge.d.ts +0 -125
  226. package/dist/ui/button-group.d.ts +0 -59
  227. package/dist/ui/button.d.ts +0 -148
  228. package/dist/ui/card.d.ts +0 -62
  229. package/dist/ui/checkbox.d.ts +0 -82
  230. package/dist/ui/chip.d.ts +0 -110
  231. package/dist/ui/code-block.d.ts +0 -14
  232. package/dist/ui/dialog.d.ts +0 -111
  233. package/dist/ui/divider.d.ts +0 -164
  234. package/dist/ui/drawer.d.ts +0 -39
  235. package/dist/ui/dropdown.d.ts +0 -29
  236. package/dist/ui/fab-menu.d.ts +0 -204
  237. package/dist/ui/fab.d.ts +0 -162
  238. package/dist/ui/icon-button.d.ts +0 -131
  239. package/dist/ui/icon.d.ts +0 -88
  240. package/dist/ui/loading-indicator.d.ts +0 -42
  241. package/dist/ui/navigation-rail.d.ts +0 -29
  242. package/dist/ui/progress-indicator/circular.d.ts +0 -3
  243. package/dist/ui/progress-indicator/hooks.d.ts +0 -3
  244. package/dist/ui/progress-indicator/linear-flat.d.ts +0 -10
  245. package/dist/ui/progress-indicator/linear-wavy.d.ts +0 -18
  246. package/dist/ui/progress-indicator/linear.d.ts +0 -3
  247. package/dist/ui/progress-indicator/types.d.ts +0 -151
  248. package/dist/ui/progress-indicator/utils.d.ts +0 -3
  249. package/dist/ui/radio-button.d.ts +0 -106
  250. package/dist/ui/ripple.d.ts +0 -126
  251. package/dist/ui/scroll-area.d.ts +0 -27
  252. package/dist/ui/shared/constants.d.ts +0 -86
  253. package/dist/ui/slider/hooks/useSliderMath.d.ts +0 -101
  254. package/dist/ui/slider/range-slider.d.ts +0 -47
  255. package/dist/ui/slider/slider-thumb.d.ts +0 -33
  256. package/dist/ui/slider/slider-track.d.ts +0 -25
  257. package/dist/ui/slider/slider.d.ts +0 -60
  258. package/dist/ui/slider/slider.tokens.d.ts +0 -151
  259. package/dist/ui/slider/slider.types.d.ts +0 -259
  260. package/dist/ui/snackbar/index.d.ts +0 -6
  261. package/dist/ui/snackbar/snackbar.d.ts +0 -197
  262. package/dist/ui/switch/switch.d.ts +0 -30
  263. package/dist/ui/switch/switch.stories.d.ts +0 -48
  264. package/dist/ui/switch/switch.tokens.d.ts +0 -67
  265. package/dist/ui/switch/switch.types.d.ts +0 -59
  266. package/dist/ui/tabs/tab.d.ts +0 -43
  267. package/dist/ui/tabs/tabs-content.d.ts +0 -36
  268. package/dist/ui/tabs/tabs-list.d.ts +0 -40
  269. package/dist/ui/tabs/tabs.d.ts +0 -60
  270. package/dist/ui/tabs/tabs.tokens.d.ts +0 -94
  271. package/dist/ui/tabs/tabs.types.d.ts +0 -172
  272. package/dist/ui/text-field/subcomponents/active-indicator.d.ts +0 -24
  273. package/dist/ui/text-field/subcomponents/floating-label.d.ts +0 -43
  274. package/dist/ui/text-field/subcomponents/leading-icon.d.ts +0 -23
  275. package/dist/ui/text-field/subcomponents/outline-container.d.ts +0 -42
  276. package/dist/ui/text-field/subcomponents/prefix-suffix.d.ts +0 -24
  277. package/dist/ui/text-field/subcomponents/supporting-text.d.ts +0 -37
  278. package/dist/ui/text-field/subcomponents/trailing-icon.d.ts +0 -41
  279. package/dist/ui/text-field/text-field.d.ts +0 -49
  280. package/dist/ui/text-field/text-field.tokens.d.ts +0 -76
  281. package/dist/ui/text-field/text-field.types.d.ts +0 -126
  282. package/dist/ui/theme-provider/index.d.ts +0 -48
  283. package/dist/ui/toc.d.ts +0 -80
  284. package/dist/ui/tooltip/plain-tooltip.d.ts +0 -2
  285. package/dist/ui/tooltip/rich-tooltip.d.ts +0 -2
  286. package/dist/ui/tooltip/tooltip-box.d.ts +0 -2
  287. package/dist/ui/tooltip/tooltip-caret-shape.d.ts +0 -9
  288. package/dist/ui/tooltip/tooltip.tokens.d.ts +0 -26
  289. package/dist/ui/tooltip/tooltip.types.d.ts +0 -56
  290. package/dist/ui/tooltip/use-tooltip-position.d.ts +0 -8
  291. package/dist/ui/tooltip/use-tooltip-state.d.ts +0 -2
  292. package/dist/ui/typography/type-scale-tokens.d.ts +0 -162
  293. package/dist/ui/typography/typography-key-tokens.d.ts +0 -40
  294. package/dist/ui/typography/typography-tokens.d.ts +0 -220
  295. package/dist/ui/typography/typography.d.ts +0 -265
  296. /package/{dist/ui/tooltip/index.d.ts → src/ui/tooltip/index.ts} +0 -0
@@ -0,0 +1,208 @@
1
+ import { useEffect, useLayoutEffect, useState } from "react";
2
+ import type { TooltipPlacement } from "./tooltip.types";
3
+
4
+ interface PositionState {
5
+ top: number;
6
+ left: number;
7
+ actualSide: "top" | "bottom" | "left" | "right";
8
+ }
9
+
10
+ type Side = "top" | "bottom" | "left" | "right";
11
+
12
+ const VIEWPORT_PADDING = 8;
13
+
14
+ const useIsomorphicLayoutEffect =
15
+ typeof window !== "undefined" ? useLayoutEffect : useEffect;
16
+
17
+ function resolveAutoPlacement(
18
+ spaceTop: number,
19
+ spaceBottom: number,
20
+ spaceLeft: number,
21
+ spaceRight: number,
22
+ tooltipWidth: number,
23
+ tooltipHeight: number,
24
+ spacing: number,
25
+ ): Side {
26
+ if (spaceTop >= tooltipHeight + spacing) return "top";
27
+ if (spaceBottom >= tooltipHeight + spacing) return "bottom";
28
+ if (spaceLeft >= tooltipWidth + spacing) return "left";
29
+ if (spaceRight >= tooltipWidth + spacing) return "right";
30
+ return "top";
31
+ }
32
+
33
+ function resolveActualSide(
34
+ targetPlacement: Side,
35
+ spaceTop: number,
36
+ spaceBottom: number,
37
+ spaceLeft: number,
38
+ spaceRight: number,
39
+ tooltipWidth: number,
40
+ tooltipHeight: number,
41
+ spacing: number,
42
+ ): Side {
43
+ const needsHeight = tooltipHeight + spacing;
44
+ const needsWidth = tooltipWidth + spacing;
45
+
46
+ if (targetPlacement === "top")
47
+ return spaceTop < needsHeight && spaceBottom >= needsHeight
48
+ ? "bottom"
49
+ : "top";
50
+ if (targetPlacement === "bottom")
51
+ return spaceBottom < needsHeight && spaceTop >= needsHeight
52
+ ? "top"
53
+ : "bottom";
54
+ if (targetPlacement === "left")
55
+ return spaceLeft < needsWidth && spaceRight >= needsWidth
56
+ ? "right"
57
+ : "left";
58
+ return spaceRight < needsWidth && spaceLeft >= needsWidth ? "left" : "right";
59
+ }
60
+
61
+ function calculateXY(
62
+ side: Side,
63
+ anchorRect: DOMRect,
64
+ tooltipWidth: number,
65
+ tooltipHeight: number,
66
+ spacing: number,
67
+ ): { top: number; left: number } {
68
+ const anchorCenterX = anchorRect.left + anchorRect.width / 2;
69
+ const anchorCenterY = anchorRect.top + anchorRect.height / 2;
70
+
71
+ if (side === "top")
72
+ return {
73
+ top: anchorRect.top - tooltipHeight - spacing,
74
+ left: anchorCenterX - tooltipWidth / 2,
75
+ };
76
+ if (side === "bottom")
77
+ return {
78
+ top: anchorRect.bottom + spacing,
79
+ left: anchorCenterX - tooltipWidth / 2,
80
+ };
81
+ if (side === "left")
82
+ return {
83
+ top: anchorCenterY - tooltipHeight / 2,
84
+ left: anchorRect.left - tooltipWidth - spacing,
85
+ };
86
+ return {
87
+ top: anchorCenterY - tooltipHeight / 2,
88
+ left: anchorRect.right + spacing,
89
+ };
90
+ }
91
+
92
+ function clampToViewport(
93
+ side: Side,
94
+ top: number,
95
+ left: number,
96
+ tooltipWidth: number,
97
+ tooltipHeight: number,
98
+ ): { top: number; left: number } {
99
+ const isVertical = side === "top" || side === "bottom";
100
+ const viewportWidth = window.innerWidth;
101
+ const viewportHeight = window.innerHeight;
102
+
103
+ if (isVertical) {
104
+ left = Math.max(
105
+ VIEWPORT_PADDING,
106
+ Math.min(left, viewportWidth - VIEWPORT_PADDING - tooltipWidth),
107
+ );
108
+ } else {
109
+ top = Math.max(
110
+ VIEWPORT_PADDING,
111
+ Math.min(top, viewportHeight - VIEWPORT_PADDING - tooltipHeight),
112
+ );
113
+ }
114
+
115
+ return { top, left };
116
+ }
117
+
118
+ export function useTooltipPosition(
119
+ anchorRef: React.RefObject<HTMLElement | null>,
120
+ tooltipRef: React.RefObject<HTMLElement | null>,
121
+ placement: TooltipPlacement,
122
+ spacing: number,
123
+ isVisible: boolean,
124
+ ): PositionState {
125
+ const [position, setPosition] = useState<PositionState>({
126
+ top: -9999,
127
+ left: -9999,
128
+ actualSide: placement === "auto" ? "top" : placement,
129
+ });
130
+
131
+ useIsomorphicLayoutEffect(() => {
132
+ if (!isVisible || !anchorRef.current || !tooltipRef.current) return;
133
+
134
+ const calculatePosition = () => {
135
+ const anchorEl = anchorRef.current;
136
+ const tooltipEl = tooltipRef.current;
137
+ if (!anchorEl || !tooltipEl) return;
138
+
139
+ const anchorRect = anchorEl.getBoundingClientRect();
140
+ const tooltipRect = tooltipEl.getBoundingClientRect();
141
+ const tooltipWidth = tooltipRect.width;
142
+ const tooltipHeight = tooltipRect.height;
143
+
144
+ const spaceTop = anchorRect.top;
145
+ const spaceBottom = window.innerHeight - anchorRect.bottom;
146
+ const spaceLeft = anchorRect.left;
147
+ const spaceRight = window.innerWidth - anchorRect.right;
148
+
149
+ const targetSide: Side =
150
+ placement === "auto"
151
+ ? resolveAutoPlacement(
152
+ spaceTop,
153
+ spaceBottom,
154
+ spaceLeft,
155
+ spaceRight,
156
+ tooltipWidth,
157
+ tooltipHeight,
158
+ spacing,
159
+ )
160
+ : placement;
161
+
162
+ const actualSide = resolveActualSide(
163
+ targetSide,
164
+ spaceTop,
165
+ spaceBottom,
166
+ spaceLeft,
167
+ spaceRight,
168
+ tooltipWidth,
169
+ tooltipHeight,
170
+ spacing,
171
+ );
172
+
173
+ const { top, left } = calculateXY(
174
+ actualSide,
175
+ anchorRect,
176
+ tooltipWidth,
177
+ tooltipHeight,
178
+ spacing,
179
+ );
180
+ const clamped = clampToViewport(
181
+ actualSide,
182
+ top,
183
+ left,
184
+ tooltipWidth,
185
+ tooltipHeight,
186
+ );
187
+
188
+ setPosition({ ...clamped, actualSide });
189
+ };
190
+
191
+ calculatePosition();
192
+
193
+ window.addEventListener("resize", calculatePosition);
194
+ window.addEventListener("scroll", calculatePosition, true);
195
+
196
+ const resizeObserver = new ResizeObserver(calculatePosition);
197
+ if (anchorRef.current) resizeObserver.observe(anchorRef.current);
198
+ if (tooltipRef.current) resizeObserver.observe(tooltipRef.current);
199
+
200
+ return () => {
201
+ window.removeEventListener("resize", calculatePosition);
202
+ window.removeEventListener("scroll", calculatePosition, true);
203
+ resizeObserver.disconnect();
204
+ };
205
+ }, [isVisible, placement, spacing, anchorRef, tooltipRef]);
206
+
207
+ return position;
208
+ }
@@ -0,0 +1,41 @@
1
+ import { useCallback, useEffect, useState } from "react";
2
+ import type { TooltipState, TooltipStateConfig } from "./tooltip.types";
3
+
4
+ const activeTooltipDismissals = new Set<() => void>();
5
+
6
+ export function useTooltipState(config?: TooltipStateConfig): TooltipState {
7
+ const {
8
+ initialVisible = false,
9
+ isPersistent = false,
10
+ duration = 1500,
11
+ } = config ?? {};
12
+
13
+ const [isVisible, setIsVisible] = useState(initialVisible);
14
+
15
+ const dismiss = useCallback(() => {
16
+ setIsVisible(false);
17
+ activeTooltipDismissals.delete(dismiss);
18
+ }, []);
19
+
20
+ const show = useCallback(() => {
21
+ for (const otherDismiss of activeTooltipDismissals) {
22
+ if (otherDismiss !== dismiss) otherDismiss();
23
+ }
24
+ setIsVisible(true);
25
+ activeTooltipDismissals.add(dismiss);
26
+ }, [dismiss]);
27
+
28
+ useEffect(() => {
29
+ if (!isVisible || isPersistent) return;
30
+ const timeoutId = setTimeout(dismiss, duration);
31
+ return () => clearTimeout(timeoutId);
32
+ }, [isVisible, isPersistent, duration, dismiss]);
33
+
34
+ useEffect(() => {
35
+ return () => {
36
+ activeTooltipDismissals.delete(dismiss);
37
+ };
38
+ }, [dismiss]);
39
+
40
+ return { isVisible, show, dismiss };
41
+ }
@@ -0,0 +1,170 @@
1
+ import { renderHook } from "@testing-library/react";
2
+ import { describe, expect, it } from "vitest";
3
+ import { Typography, TypographyProvider, useTypography } from "../typography";
4
+ import { TypographyKeyTokens } from "../typography-key-tokens";
5
+ import {
6
+ DEFAULT_FONT_VARIATION_AXES,
7
+ serializeFontVariationAxes,
8
+ TypographyTokens,
9
+ } from "../typography-tokens";
10
+
11
+ describe("Typography", () => {
12
+ describe("serializeFontVariationAxes", () => {
13
+ it("should serialize a single axis correctly", () => {
14
+ expect(serializeFontVariationAxes({ ROND: 100 })).toBe('"ROND" 100');
15
+ });
16
+
17
+ it("should serialize multiple axes correctly", () => {
18
+ const result = serializeFontVariationAxes({ ROND: 50, wght: 700 });
19
+ expect(result).toContain('"ROND" 50');
20
+ expect(result).toContain('"wght" 700');
21
+ });
22
+
23
+ it("should skip undefined axis values", () => {
24
+ const result = serializeFontVariationAxes({ ROND: 100, wght: undefined });
25
+ expect(result).toBe('"ROND" 100');
26
+ });
27
+ });
28
+
29
+ describe("TypographyTokens", () => {
30
+ it("should initialize with default Google Sans Flex font", () => {
31
+ const tokens = new TypographyTokens();
32
+ expect(tokens.BodyLarge.fontFamily).toContain("Google Sans Flex");
33
+ // MD3 Expressive requires ROND: 100 for maximum roundness
34
+ expect(tokens.BodyLarge.fontVariationSettings).toContain('"ROND" 100');
35
+ });
36
+
37
+ it("should support a custom fontFamily override (options object)", () => {
38
+ const tokens = new TypographyTokens({ fontFamily: "Inter, sans-serif" });
39
+ expect(tokens.HeadlineLarge.fontFamily).toBe("Inter, sans-serif");
40
+ });
41
+
42
+ it("should support legacy string constructor for backward compatibility", () => {
43
+ const tokens = new TypographyTokens("Inter, sans-serif");
44
+ expect(tokens.HeadlineLarge.fontFamily).toBe("Inter, sans-serif");
45
+ });
46
+
47
+ it("should apply custom fontVariationAxes", () => {
48
+ const tokens = new TypographyTokens({ fontVariationAxes: { ROND: 0 } });
49
+ expect(tokens.BodyLarge.fontVariationSettings).toContain('"ROND" 0');
50
+ });
51
+
52
+ it("should merge custom axes on top of defaults (ROND stays 100 unless overridden)", () => {
53
+ // Only wght is overridden; ROND should remain at default (100)
54
+ expect(DEFAULT_FONT_VARIATION_AXES.ROND).toBe(100);
55
+ const tokens = new TypographyTokens({ fontVariationAxes: { wght: 600 } });
56
+ expect(tokens.BodyLarge.fontVariationSettings).toContain('"ROND" 100');
57
+ expect(tokens.BodyLarge.fontVariationSettings).toContain('"wght" 600');
58
+ });
59
+
60
+ it("should lazily compute and cache styles on first access", () => {
61
+ const tokens = new TypographyTokens();
62
+ const first = tokens.DisplayLarge;
63
+ const second = tokens.DisplayLarge;
64
+ // Same object reference — cached
65
+ expect(first).toBe(second);
66
+ });
67
+
68
+ it("should return frozen TextStyle objects", () => {
69
+ const tokens = new TypographyTokens();
70
+ expect(Object.isFrozen(tokens.BodyLarge)).toBe(true);
71
+ });
72
+ });
73
+
74
+ describe("Typography (Class)", () => {
75
+ it("should expose all 30 TextStyle getters", () => {
76
+ const typography = new Typography();
77
+ expect(typography.displayLarge).toBeDefined();
78
+ expect(typography.bodyMedium).toBeDefined();
79
+ expect(typography.labelSmallEmphasized).toBeDefined();
80
+
81
+ // Check token delegation — should match token value
82
+ const tokens = new TypographyTokens();
83
+ expect(typography.headlineMedium).toEqual(tokens.HeadlineMedium);
84
+ });
85
+
86
+ it("should map TypographyKeyTokens correctly using fromToken()", () => {
87
+ const typography = new Typography();
88
+ const style = typography.fromToken(TypographyKeyTokens.TitleMedium);
89
+ expect(style.fontSize).toBe("1.143rem");
90
+ expect(style.fontWeight).toBe(500);
91
+
92
+ const emphStyle = typography.fromToken(
93
+ TypographyKeyTokens.TitleMediumEmphasized,
94
+ );
95
+ expect(emphStyle.fontWeight).toBe(700);
96
+ });
97
+
98
+ it("should create an overridden copy without mutating the original via copy()", () => {
99
+ const original = new Typography();
100
+ const overridden = original.copy({
101
+ bodyLarge: { fontSize: "99rem", lineHeight: "120rem" },
102
+ });
103
+
104
+ // Original remains intact
105
+ expect(original.bodyLarge.fontSize).toBe("1.143rem");
106
+
107
+ // Overridden instance reflects custom values
108
+ expect(overridden.bodyLarge.fontSize).toBe("99rem");
109
+ expect(overridden.bodyLarge.lineHeight).toBe("120rem");
110
+
111
+ // Untouched properties remain referentially stable
112
+ expect(overridden.headlineSmall).toBe(original.headlineSmall);
113
+ });
114
+ });
115
+
116
+ describe("TypographyProvider & useTypography", () => {
117
+ it("should provide default Google Sans Flex typography when no props given", () => {
118
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
119
+ <TypographyProvider>{children}</TypographyProvider>
120
+ );
121
+ const { result } = renderHook(() => useTypography(), { wrapper });
122
+ expect(result.current.bodySmall.fontFamily).toContain("Google Sans Flex");
123
+ });
124
+
125
+ it("should respect fontFamily prop shorthand", () => {
126
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
127
+ <TypographyProvider fontFamily="'Roboto', sans-serif">
128
+ {children}
129
+ </TypographyProvider>
130
+ );
131
+ const { result } = renderHook(() => useTypography(), { wrapper });
132
+ expect(result.current.bodySmall.fontFamily).toBe("'Roboto', sans-serif");
133
+ });
134
+
135
+ it("should apply custom fontVariationAxes via prop", () => {
136
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
137
+ <TypographyProvider fontVariationAxes={{ ROND: 0 }}>
138
+ {children}
139
+ </TypographyProvider>
140
+ );
141
+ const { result } = renderHook(() => useTypography(), { wrapper });
142
+ expect(result.current.bodyLarge.fontVariationSettings).toContain(
143
+ '"ROND" 0',
144
+ );
145
+ });
146
+
147
+ it("should prioritize custom typography instance prop", () => {
148
+ const customTokens = new TypographyTokens({
149
+ fontFamily: "'Comic Sans MS'",
150
+ });
151
+ const customTypography = new Typography(customTokens);
152
+
153
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
154
+ <TypographyProvider typography={customTypography}>
155
+ {children}
156
+ </TypographyProvider>
157
+ );
158
+ const { result } = renderHook(() => useTypography(), { wrapper });
159
+ expect(result.current.displayLarge.fontFamily).toBe("'Comic Sans MS'");
160
+ });
161
+
162
+ it("should return the default Typography if used without a provider", () => {
163
+ const { result } = renderHook(() => useTypography());
164
+ expect(result.current).toBeInstanceOf(Typography);
165
+ expect(result.current.displaySmall.fontFamily).toContain(
166
+ "Google Sans Flex",
167
+ );
168
+ });
169
+ });
170
+ });
@@ -7,10 +7,28 @@
7
7
  *
8
8
  * @module typography
9
9
  */
10
+
10
11
  export type { TypeScaleTokensType } from "./type-scale-tokens";
11
12
  export { TypeScaleTokens } from "./type-scale-tokens";
13
+
12
14
  export type { TypographyProviderProps } from "./typography";
13
- export { Typography, TypographyContext, TypographyProvider, useTypography, } from "./typography";
15
+ export {
16
+ Typography,
17
+ TypographyContext,
18
+ TypographyProvider,
19
+ useTypography,
20
+ } from "./typography";
21
+
14
22
  export { TypographyKeyTokens } from "./typography-key-tokens";
15
- export type { FontVariationAxes, TextStyle, TypographyTokensOptions, } from "./typography-tokens";
16
- export { DEFAULT_FONT_VARIATION_AXES, MD3_EXPRESSIVE_FONT_VARIATION, serializeFontVariationAxes, TypographyTokens, } from "./typography-tokens";
23
+
24
+ export type {
25
+ FontVariationAxes,
26
+ TextStyle,
27
+ TypographyTokensOptions,
28
+ } from "./typography-tokens";
29
+ export {
30
+ DEFAULT_FONT_VARIATION_AXES,
31
+ MD3_EXPRESSIVE_FONT_VARIATION,
32
+ serializeFontVariationAxes,
33
+ TypographyTokens,
34
+ } from "./typography-tokens";
@@ -0,0 +1,205 @@
1
+ /**
2
+ * TypeScaleTokens - MD3 Expressive Typography Physical Tokens
3
+ *
4
+ * Port of androidx.compose.material3.tokens.TypeScaleTokens
5
+ * Adapted for Web with Google Sans Flex Variable Font (ROND axis = 100).
6
+ *
7
+ * All font sizes use rem units based on 14px root (project default).
8
+ * Reference: Material Design 3 Type Scale Tokens spec.
9
+ */
10
+
11
+ const font = "'Google Sans Flex', system-ui, sans-serif";
12
+
13
+ export const TypeScaleTokens = {
14
+ // ─── DISPLAY ────────────────────────────────────────────────────────────────
15
+ DisplayLargeFont: font,
16
+ DisplayLargeWeight: 400,
17
+ DisplayLargeSize: "3.571rem", // 57sp → ~57px / 14 = 4.071, MD3 uses 57px on 16px base → 3.5625rem
18
+ DisplayLargeLineHeight: "4rem", // 64px
19
+ DisplayLargeTracking: "-0.25px",
20
+
21
+ DisplayMediumFont: font,
22
+ DisplayMediumWeight: 400,
23
+ DisplayMediumSize: "2.857rem", // 45px
24
+ DisplayMediumLineHeight: "3.286rem", // 52px
25
+ DisplayMediumTracking: "0px",
26
+
27
+ DisplaySmallFont: font,
28
+ DisplaySmallWeight: 400,
29
+ DisplaySmallSize: "2.571rem", // 36px
30
+ DisplaySmallLineHeight: "3.143rem", // 44px
31
+ DisplaySmallTracking: "0px",
32
+
33
+ // ─── HEADLINE ───────────────────────────────────────────────────────────────
34
+ HeadlineLargeFont: font,
35
+ HeadlineLargeWeight: 400,
36
+ HeadlineLargeSize: "2.286rem", // 32px
37
+ HeadlineLargeLineHeight: "2.857rem", // 40px
38
+ HeadlineLargeTracking: "0px",
39
+
40
+ HeadlineMediumFont: font,
41
+ HeadlineMediumWeight: 400,
42
+ HeadlineMediumSize: "2rem", // 28px
43
+ HeadlineMediumLineHeight: "2.571rem", // 36px
44
+ HeadlineMediumTracking: "0px",
45
+
46
+ HeadlineSmallFont: font,
47
+ HeadlineSmallWeight: 400,
48
+ HeadlineSmallSize: "1.714rem", // 24px
49
+ HeadlineSmallLineHeight: "2.286rem", // 32px
50
+ HeadlineSmallTracking: "0px",
51
+
52
+ // ─── TITLE ──────────────────────────────────────────────────────────────────
53
+ TitleLargeFont: font,
54
+ TitleLargeWeight: 400,
55
+ TitleLargeSize: "1.571rem", // 22px
56
+ TitleLargeLineHeight: "2rem", // 28px
57
+ TitleLargeTracking: "0px",
58
+
59
+ TitleMediumFont: font,
60
+ TitleMediumWeight: 500,
61
+ TitleMediumSize: "1.143rem", // 16px
62
+ TitleMediumLineHeight: "1.714rem", // 24px
63
+ TitleMediumTracking: "0.15px",
64
+
65
+ TitleSmallFont: font,
66
+ TitleSmallWeight: 500,
67
+ TitleSmallSize: "1rem", // 14px
68
+ TitleSmallLineHeight: "1.429rem", // 20px
69
+ TitleSmallTracking: "0.1px",
70
+
71
+ // ─── BODY ───────────────────────────────────────────────────────────────────
72
+ BodyLargeFont: font,
73
+ BodyLargeWeight: 400,
74
+ BodyLargeSize: "1.143rem", // 16px
75
+ BodyLargeLineHeight: "1.714rem", // 24px
76
+ BodyLargeTracking: "0.5px",
77
+
78
+ BodyMediumFont: font,
79
+ BodyMediumWeight: 400,
80
+ BodyMediumSize: "1rem", // 14px
81
+ BodyMediumLineHeight: "1.429rem", // 20px
82
+ BodyMediumTracking: "0.25px",
83
+
84
+ BodySmallFont: font,
85
+ BodySmallWeight: 400,
86
+ BodySmallSize: "0.857rem", // 12px
87
+ BodySmallLineHeight: "1.143rem", // 16px
88
+ BodySmallTracking: "0.4px",
89
+
90
+ // ─── LABEL ──────────────────────────────────────────────────────────────────
91
+ LabelLargeFont: font,
92
+ LabelLargeWeight: 500,
93
+ LabelLargeSize: "1rem", // 14px
94
+ LabelLargeLineHeight: "1.429rem", // 20px
95
+ LabelLargeTracking: "0.1px",
96
+
97
+ LabelMediumFont: font,
98
+ LabelMediumWeight: 500,
99
+ LabelMediumSize: "0.857rem", // 12px
100
+ LabelMediumLineHeight: "1.143rem", // 16px
101
+ LabelMediumTracking: "0.5px",
102
+
103
+ LabelSmallFont: font,
104
+ LabelSmallWeight: 500,
105
+ LabelSmallSize: "0.786rem", // 11px
106
+ LabelSmallLineHeight: "1.143rem", // 16px
107
+ LabelSmallTracking: "0.5px",
108
+
109
+ // ─── EMPHASIZED (MD3 Expressive) ────────────────────────────────────────────
110
+ // Emphasized styles share Font/Size/LineHeight/Tracking with baseline.
111
+ // Only fontWeight is elevated to create visual emphasis.
112
+ // Source: TypographyTokens.kt (Emphasized entries reference same scale)
113
+
114
+ DisplayLargeEmphasizedFont: font,
115
+ DisplayLargeEmphasizedWeight: 800,
116
+ DisplayLargeEmphasizedSize: "3.571rem",
117
+ DisplayLargeEmphasizedLineHeight: "4rem",
118
+ DisplayLargeEmphasizedTracking: "-0.25px",
119
+
120
+ DisplayMediumEmphasizedFont: font,
121
+ DisplayMediumEmphasizedWeight: 800,
122
+ DisplayMediumEmphasizedSize: "2.857rem",
123
+ DisplayMediumEmphasizedLineHeight: "3.286rem",
124
+ DisplayMediumEmphasizedTracking: "0px",
125
+
126
+ DisplaySmallEmphasizedFont: font,
127
+ DisplaySmallEmphasizedWeight: 800,
128
+ DisplaySmallEmphasizedSize: "2.571rem",
129
+ DisplaySmallEmphasizedLineHeight: "3.143rem",
130
+ DisplaySmallEmphasizedTracking: "0px",
131
+
132
+ HeadlineLargeEmphasizedFont: font,
133
+ HeadlineLargeEmphasizedWeight: 800,
134
+ HeadlineLargeEmphasizedSize: "2.286rem",
135
+ HeadlineLargeEmphasizedLineHeight: "2.857rem",
136
+ HeadlineLargeEmphasizedTracking: "0px",
137
+
138
+ HeadlineMediumEmphasizedFont: font,
139
+ HeadlineMediumEmphasizedWeight: 800,
140
+ HeadlineMediumEmphasizedSize: "2rem",
141
+ HeadlineMediumEmphasizedLineHeight: "2.571rem",
142
+ HeadlineMediumEmphasizedTracking: "0px",
143
+
144
+ HeadlineSmallEmphasizedFont: font,
145
+ HeadlineSmallEmphasizedWeight: 800,
146
+ HeadlineSmallEmphasizedSize: "1.714rem",
147
+ HeadlineSmallEmphasizedLineHeight: "2.286rem",
148
+ HeadlineSmallEmphasizedTracking: "0px",
149
+
150
+ TitleLargeEmphasizedFont: font,
151
+ TitleLargeEmphasizedWeight: 700,
152
+ TitleLargeEmphasizedSize: "1.571rem",
153
+ TitleLargeEmphasizedLineHeight: "2rem",
154
+ TitleLargeEmphasizedTracking: "0px",
155
+
156
+ TitleMediumEmphasizedFont: font,
157
+ TitleMediumEmphasizedWeight: 700,
158
+ TitleMediumEmphasizedSize: "1.143rem",
159
+ TitleMediumEmphasizedLineHeight: "1.714rem",
160
+ TitleMediumEmphasizedTracking: "0.15px",
161
+
162
+ TitleSmallEmphasizedFont: font,
163
+ TitleSmallEmphasizedWeight: 700,
164
+ TitleSmallEmphasizedSize: "1rem",
165
+ TitleSmallEmphasizedLineHeight: "1.429rem",
166
+ TitleSmallEmphasizedTracking: "0.1px",
167
+
168
+ BodyLargeEmphasizedFont: font,
169
+ BodyLargeEmphasizedWeight: 700,
170
+ BodyLargeEmphasizedSize: "1.143rem",
171
+ BodyLargeEmphasizedLineHeight: "1.714rem",
172
+ BodyLargeEmphasizedTracking: "0.5px",
173
+
174
+ BodyMediumEmphasizedFont: font,
175
+ BodyMediumEmphasizedWeight: 700,
176
+ BodyMediumEmphasizedSize: "1rem",
177
+ BodyMediumEmphasizedLineHeight: "1.429rem",
178
+ BodyMediumEmphasizedTracking: "0.25px",
179
+
180
+ BodySmallEmphasizedFont: font,
181
+ BodySmallEmphasizedWeight: 700,
182
+ BodySmallEmphasizedSize: "0.857rem",
183
+ BodySmallEmphasizedLineHeight: "1.143rem",
184
+ BodySmallEmphasizedTracking: "0.4px",
185
+
186
+ LabelLargeEmphasizedFont: font,
187
+ LabelLargeEmphasizedWeight: 800,
188
+ LabelLargeEmphasizedSize: "1rem",
189
+ LabelLargeEmphasizedLineHeight: "1.429rem",
190
+ LabelLargeEmphasizedTracking: "0.1px",
191
+
192
+ LabelMediumEmphasizedFont: font,
193
+ LabelMediumEmphasizedWeight: 800,
194
+ LabelMediumEmphasizedSize: "0.857rem",
195
+ LabelMediumEmphasizedLineHeight: "1.143rem",
196
+ LabelMediumEmphasizedTracking: "0.5px",
197
+
198
+ LabelSmallEmphasizedFont: font,
199
+ LabelSmallEmphasizedWeight: 800,
200
+ LabelSmallEmphasizedSize: "0.786rem",
201
+ LabelSmallEmphasizedLineHeight: "1.143rem",
202
+ LabelSmallEmphasizedTracking: "0.5px",
203
+ } as const;
204
+
205
+ export type TypeScaleTokensType = typeof TypeScaleTokens;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * TypographyKeyTokens - MD3 Expressive Typography Key Tokens Enum
3
+ *
4
+ * Port of androidx.compose.material3.tokens.TypographyKeyTokens (Kotlin enum class).
5
+ * Used to reference a specific TextStyle via Typography.fromToken().
6
+ *
7
+ * Contains 30 keys: 15 baseline + 15 emphasized (MD3 Expressive).
8
+ */
9
+ export enum TypographyKeyTokens {
10
+ // ─── Baseline (15) ──────────────────────────────────────────────────────
11
+ BodyLarge = "BodyLarge",
12
+ BodyMedium = "BodyMedium",
13
+ BodySmall = "BodySmall",
14
+ DisplayLarge = "DisplayLarge",
15
+ DisplayMedium = "DisplayMedium",
16
+ DisplaySmall = "DisplaySmall",
17
+ HeadlineLarge = "HeadlineLarge",
18
+ HeadlineMedium = "HeadlineMedium",
19
+ HeadlineSmall = "HeadlineSmall",
20
+ LabelLarge = "LabelLarge",
21
+ LabelMedium = "LabelMedium",
22
+ LabelSmall = "LabelSmall",
23
+ TitleLarge = "TitleLarge",
24
+ TitleMedium = "TitleMedium",
25
+ TitleSmall = "TitleSmall",
26
+
27
+ // ─── Emphasized (15) - MD3 Expressive ───────────────────────────────────
28
+ BodyLargeEmphasized = "BodyLargeEmphasized",
29
+ BodyMediumEmphasized = "BodyMediumEmphasized",
30
+ BodySmallEmphasized = "BodySmallEmphasized",
31
+ DisplayLargeEmphasized = "DisplayLargeEmphasized",
32
+ DisplayMediumEmphasized = "DisplayMediumEmphasized",
33
+ DisplaySmallEmphasized = "DisplaySmallEmphasized",
34
+ HeadlineLargeEmphasized = "HeadlineLargeEmphasized",
35
+ HeadlineMediumEmphasized = "HeadlineMediumEmphasized",
36
+ HeadlineSmallEmphasized = "HeadlineSmallEmphasized",
37
+ LabelLargeEmphasized = "LabelLargeEmphasized",
38
+ LabelMediumEmphasized = "LabelMediumEmphasized",
39
+ LabelSmallEmphasized = "LabelSmallEmphasized",
40
+ TitleLargeEmphasized = "TitleLargeEmphasized",
41
+ TitleMediumEmphasized = "TitleMediumEmphasized",
42
+ TitleSmallEmphasized = "TitleSmallEmphasized",
43
+ }