@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,360 @@
1
+ /**
2
+ * @file typography-tokens.ts
3
+ * @description MD3 Expressive Typography Token Definitions.
4
+ *
5
+ * Port of `androidx.compose.material3.tokens.TypographyTokens` (Kotlin class).
6
+ *
7
+ * ### Memory Optimization
8
+ * All 30 `TextStyle` properties are implemented as **lazy getters** — they are
9
+ * computed on first access and cached, rather than being computed eagerly at
10
+ * construction time. This reduces the instantiation cost of `TypographyTokens`.
11
+ *
12
+ * ### Variable Font Axes
13
+ * Supports customizable CSS `font-variation-settings` via {@link FontVariationAxes}.
14
+ * - Default `ROND` value is `100` (maximum roundness of Google Sans Flex).
15
+ * - Other axes remain at font defaults unless explicitly overridden.
16
+ *
17
+ * @example Basic usage (default tokens)
18
+ * ```ts
19
+ * const tokens = new TypographyTokens();
20
+ * const style = tokens.BodyLarge; // { fontSize, fontWeight, ... }
21
+ * ```
22
+ *
23
+ * @example Custom font family
24
+ * ```ts
25
+ * const tokens = new TypographyTokens({ fontFamily: "'Inter', sans-serif" });
26
+ * ```
27
+ *
28
+ * @example Custom ROND axis (partial roundness)
29
+ * ```ts
30
+ * const tokens = new TypographyTokens({ fontVariationAxes: { ROND: 50 } });
31
+ * ```
32
+ */
33
+
34
+ import { TypeScaleTokens } from "./type-scale-tokens";
35
+
36
+ // ─── Font Variation ───────────────────────────────────────────────────────────
37
+
38
+ /**
39
+ * Configurable axes for CSS `font-variation-settings`.
40
+ *
41
+ * Each key maps to a named variable font axis. Any axis not specified falls
42
+ * back to the font's own default value.
43
+ *
44
+ * @see https://developer.mozilla.org/en-US/docs/Web/CSS/font-variation-settings
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * const axes: FontVariationAxes = { ROND: 100, wght: 600 };
49
+ * ```
50
+ */
51
+ export interface FontVariationAxes {
52
+ /**
53
+ * Roundness axis of Google Sans Flex Variable Font.
54
+ * Range: `0` (sharp corners) – `100` (fully rounded).
55
+ * @default 100
56
+ */
57
+ ROND?: number;
58
+ /**
59
+ * Weight axis. Overrides `font-weight` via variation settings.
60
+ * @default font default
61
+ */
62
+ wght?: number;
63
+ /**
64
+ * Width axis. Controls glyph condensation/expansion.
65
+ * @default font default
66
+ */
67
+ wdth?: number;
68
+ /** Any additional named variation axis supported by the font. */
69
+ [axis: string]: number | undefined;
70
+ }
71
+
72
+ /**
73
+ * Default font variation axes for MD3 Expressive.
74
+ * Sets `ROND` to `100` for maximum roundness; all other axes use font defaults.
75
+ */
76
+ export const DEFAULT_FONT_VARIATION_AXES: Readonly<FontVariationAxes> =
77
+ Object.freeze({ ROND: 100 });
78
+
79
+ /**
80
+ * Serializes a {@link FontVariationAxes} map into a CSS `font-variation-settings` string.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * serializeFontVariationAxes({ ROND: 100, wght: 700 });
85
+ * // → '"ROND" 100, "wght" 700'
86
+ * ```
87
+ */
88
+ export function serializeFontVariationAxes(axes: FontVariationAxes): string {
89
+ return Object.entries(axes)
90
+ .filter(([, v]) => v !== undefined)
91
+ .map(([k, v]) => `"${k}" ${v}`)
92
+ .join(", ");
93
+ }
94
+
95
+ /**
96
+ * Pre-computed default `font-variation-settings` string.
97
+ * Equivalent to `'"ROND" 100'`.
98
+ */
99
+ export const MD3_EXPRESSIVE_FONT_VARIATION: string = serializeFontVariationAxes(
100
+ DEFAULT_FONT_VARIATION_AXES,
101
+ );
102
+
103
+ // ─── TextStyle ────────────────────────────────────────────────────────────────
104
+
105
+ /**
106
+ * Web equivalent of Compose's `TextStyle`.
107
+ *
108
+ * All properties are readonly and map directly to CSS font properties.
109
+ * The `fontVariationSettings` field carries the serialized variable-font axes.
110
+ */
111
+ export interface TextStyle {
112
+ /** CSS `font-family` value. */
113
+ readonly fontFamily: string;
114
+ /** CSS `font-weight` numeric value (e.g. `400`, `700`). */
115
+ readonly fontWeight: number;
116
+ /** CSS `font-size` in `rem` units. */
117
+ readonly fontSize: string;
118
+ /** CSS `line-height` in `rem` units. */
119
+ readonly lineHeight: string;
120
+ /** CSS `letter-spacing` in `px` units. */
121
+ readonly letterSpacing: string;
122
+ /**
123
+ * CSS `font-variation-settings` string.
124
+ * @example '"ROND" 100'
125
+ * @example '"ROND" 50, "wght" 600'
126
+ */
127
+ readonly fontVariationSettings: string;
128
+ }
129
+
130
+ // ─── TypographyTokens Options ─────────────────────────────────────────────────
131
+
132
+ /**
133
+ * Constructor options for {@link TypographyTokens}.
134
+ */
135
+ export interface TypographyTokensOptions {
136
+ /**
137
+ * Custom CSS `font-family` string. When provided, overrides the default
138
+ * Google Sans Flex font for all token styles.
139
+ *
140
+ * @example "'Roboto', sans-serif"
141
+ */
142
+ fontFamily?: string;
143
+ /**
144
+ * Variable font axes to apply via `font-variation-settings`.
145
+ * Merged on top of {@link DEFAULT_FONT_VARIATION_AXES}.
146
+ * Provide only the axes you want to override.
147
+ *
148
+ * @example { ROND: 0 } // sharp corners
149
+ */
150
+ fontVariationAxes?: FontVariationAxes;
151
+ }
152
+
153
+ // ─── Internal factory ─────────────────────────────────────────────────────────
154
+
155
+ type TokenRecord = Record<string, string | number>;
156
+
157
+ /**
158
+ * Creates a frozen {@link TextStyle} from the {@link TypeScaleTokens} lookup
159
+ * using the given token prefix (e.g. `"BodyLarge"`).
160
+ *
161
+ * @internal
162
+ */
163
+ function buildStyle(
164
+ prefix: string,
165
+ fontFamily: string | undefined,
166
+ fontVariationSettings: string,
167
+ ): TextStyle {
168
+ const t = TypeScaleTokens as TokenRecord;
169
+ return Object.freeze({
170
+ fontFamily: fontFamily ?? (t[`${prefix}Font`] as string),
171
+ fontWeight: t[`${prefix}Weight`] as number,
172
+ fontSize: t[`${prefix}Size`] as string,
173
+ lineHeight: t[`${prefix}LineHeight`] as string,
174
+ letterSpacing: t[`${prefix}Tracking`] as string,
175
+ fontVariationSettings,
176
+ });
177
+ }
178
+
179
+ // ─── TypographyTokens ─────────────────────────────────────────────────────────
180
+
181
+ /**
182
+ * MD3 Expressive Typography Token class.
183
+ *
184
+ * Port of `internal class TypographyTokens(val fontFamily: FontFamily? = null)`
185
+ * from `androidx.compose.material3.tokens.TypographyTokens`.
186
+ *
187
+ * Provides 30 pre-defined {@link TextStyle} properties (15 baseline + 15 emphasized),
188
+ * each implemented as a **lazy getter** — computed once on first access, then cached.
189
+ *
190
+ * ### Customization
191
+ * Pass {@link TypographyTokensOptions} to the constructor to override:
192
+ * - `fontFamily` — swap the typeface
193
+ * - `fontVariationAxes` — control variable font axes (e.g., `ROND`)
194
+ *
195
+ * @example Default
196
+ * ```ts
197
+ * const tokens = new TypographyTokens();
198
+ * ```
199
+ *
200
+ * @example Custom font + half-rounded
201
+ * ```ts
202
+ * const tokens = new TypographyTokens({
203
+ * fontFamily: "'Inter', sans-serif",
204
+ * fontVariationAxes: { ROND: 50 },
205
+ * });
206
+ * ```
207
+ */
208
+ export class TypographyTokens {
209
+ readonly #fontFamily: string | undefined;
210
+ readonly #fontVariationSettings: string;
211
+
212
+ constructor(options: TypographyTokensOptions | string = {}) {
213
+ // Support legacy string signature: new TypographyTokens("'Inter', sans-serif")
214
+ if (typeof options === "string") {
215
+ this.#fontFamily = options;
216
+ this.#fontVariationSettings = MD3_EXPRESSIVE_FONT_VARIATION;
217
+ } else {
218
+ this.#fontFamily = options.fontFamily;
219
+ const axes: FontVariationAxes = options.fontVariationAxes
220
+ ? { ...DEFAULT_FONT_VARIATION_AXES, ...options.fontVariationAxes }
221
+ : DEFAULT_FONT_VARIATION_AXES;
222
+ this.#fontVariationSettings = serializeFontVariationAxes(axes);
223
+ }
224
+ }
225
+
226
+ // Helper to lazily build + cache a style on first access
227
+ #cache: Map<string, TextStyle> = new Map();
228
+ #get(prefix: string): TextStyle {
229
+ let style = this.#cache.get(prefix);
230
+ if (!style) {
231
+ style = buildStyle(prefix, this.#fontFamily, this.#fontVariationSettings);
232
+ this.#cache.set(prefix, style);
233
+ }
234
+ return style;
235
+ }
236
+
237
+ // ─── Baseline Styles (15) ──────────────────────────────────────────────────
238
+ /** Display Large – `57px`, weight 400 */
239
+ get DisplayLarge(): TextStyle {
240
+ return this.#get("DisplayLarge");
241
+ }
242
+ /** Display Medium – `45px`, weight 400 */
243
+ get DisplayMedium(): TextStyle {
244
+ return this.#get("DisplayMedium");
245
+ }
246
+ /** Display Small – `36px`, weight 400 */
247
+ get DisplaySmall(): TextStyle {
248
+ return this.#get("DisplaySmall");
249
+ }
250
+ /** Headline Large – `32px`, weight 400 */
251
+ get HeadlineLarge(): TextStyle {
252
+ return this.#get("HeadlineLarge");
253
+ }
254
+ /** Headline Medium – `28px`, weight 400 */
255
+ get HeadlineMedium(): TextStyle {
256
+ return this.#get("HeadlineMedium");
257
+ }
258
+ /** Headline Small – `24px`, weight 400 */
259
+ get HeadlineSmall(): TextStyle {
260
+ return this.#get("HeadlineSmall");
261
+ }
262
+ /** Title Large – `22px`, weight 400 */
263
+ get TitleLarge(): TextStyle {
264
+ return this.#get("TitleLarge");
265
+ }
266
+ /** Title Medium – `16px`, weight 500 */
267
+ get TitleMedium(): TextStyle {
268
+ return this.#get("TitleMedium");
269
+ }
270
+ /** Title Small – `14px`, weight 500 */
271
+ get TitleSmall(): TextStyle {
272
+ return this.#get("TitleSmall");
273
+ }
274
+ /** Body Large – `16px`, weight 400 */
275
+ get BodyLarge(): TextStyle {
276
+ return this.#get("BodyLarge");
277
+ }
278
+ /** Body Medium – `14px`, weight 400 */
279
+ get BodyMedium(): TextStyle {
280
+ return this.#get("BodyMedium");
281
+ }
282
+ /** Body Small – `12px`, weight 400 */
283
+ get BodySmall(): TextStyle {
284
+ return this.#get("BodySmall");
285
+ }
286
+ /** Label Large – `14px`, weight 500 */
287
+ get LabelLarge(): TextStyle {
288
+ return this.#get("LabelLarge");
289
+ }
290
+ /** Label Medium – `12px`, weight 500 */
291
+ get LabelMedium(): TextStyle {
292
+ return this.#get("LabelMedium");
293
+ }
294
+ /** Label Small – `11px`, weight 500 */
295
+ get LabelSmall(): TextStyle {
296
+ return this.#get("LabelSmall");
297
+ }
298
+
299
+ // ─── Emphasized Styles (15) – MD3 Expressive ──────────────────────────────
300
+ /** Display Large Emphasized – `57px`, weight 800 */
301
+ get DisplayLargeEmphasized(): TextStyle {
302
+ return this.#get("DisplayLargeEmphasized");
303
+ }
304
+ /** Display Medium Emphasized – `45px`, weight 800 */
305
+ get DisplayMediumEmphasized(): TextStyle {
306
+ return this.#get("DisplayMediumEmphasized");
307
+ }
308
+ /** Display Small Emphasized – `36px`, weight 800 */
309
+ get DisplaySmallEmphasized(): TextStyle {
310
+ return this.#get("DisplaySmallEmphasized");
311
+ }
312
+ /** Headline Large Emphasized – `32px`, weight 800 */
313
+ get HeadlineLargeEmphasized(): TextStyle {
314
+ return this.#get("HeadlineLargeEmphasized");
315
+ }
316
+ /** Headline Medium Emphasized – `28px`, weight 800 */
317
+ get HeadlineMediumEmphasized(): TextStyle {
318
+ return this.#get("HeadlineMediumEmphasized");
319
+ }
320
+ /** Headline Small Emphasized – `24px`, weight 800 */
321
+ get HeadlineSmallEmphasized(): TextStyle {
322
+ return this.#get("HeadlineSmallEmphasized");
323
+ }
324
+ /** Title Large Emphasized – `22px`, weight 700 */
325
+ get TitleLargeEmphasized(): TextStyle {
326
+ return this.#get("TitleLargeEmphasized");
327
+ }
328
+ /** Title Medium Emphasized – `16px`, weight 700 */
329
+ get TitleMediumEmphasized(): TextStyle {
330
+ return this.#get("TitleMediumEmphasized");
331
+ }
332
+ /** Title Small Emphasized – `14px`, weight 700 */
333
+ get TitleSmallEmphasized(): TextStyle {
334
+ return this.#get("TitleSmallEmphasized");
335
+ }
336
+ /** Body Large Emphasized – `16px`, weight 700 */
337
+ get BodyLargeEmphasized(): TextStyle {
338
+ return this.#get("BodyLargeEmphasized");
339
+ }
340
+ /** Body Medium Emphasized – `14px`, weight 700 */
341
+ get BodyMediumEmphasized(): TextStyle {
342
+ return this.#get("BodyMediumEmphasized");
343
+ }
344
+ /** Body Small Emphasized – `12px`, weight 700 */
345
+ get BodySmallEmphasized(): TextStyle {
346
+ return this.#get("BodySmallEmphasized");
347
+ }
348
+ /** Label Large Emphasized – `14px`, weight 800 */
349
+ get LabelLargeEmphasized(): TextStyle {
350
+ return this.#get("LabelLargeEmphasized");
351
+ }
352
+ /** Label Medium Emphasized – `12px`, weight 800 */
353
+ get LabelMediumEmphasized(): TextStyle {
354
+ return this.#get("LabelMediumEmphasized");
355
+ }
356
+ /** Label Small Emphasized – `11px`, weight 800 */
357
+ get LabelSmallEmphasized(): TextStyle {
358
+ return this.#get("LabelSmallEmphasized");
359
+ }
360
+ }
@@ -0,0 +1,22 @@
1
+ /* MD3 Expressive Typography - Google Sans Flex Variable Font */
2
+
3
+ @font-face {
4
+ font-family: "Google Sans Flex";
5
+ src: url("../../assets/fonts/GoogleSansFlex-VariableFont.woff2")
6
+ format("woff2");
7
+ font-weight: 100 1000;
8
+ font-style: normal;
9
+ font-display: swap;
10
+ }
11
+
12
+ /**
13
+ * Applies Google Sans Flex with ROND axis at 100 (maximum roundness)
14
+ * as required by MD3 Expressive design spec.
15
+ *
16
+ * Usage: Add this class to any element or the root element to apply
17
+ * the MD3 Expressive typography defaults.
18
+ */
19
+ .font-md3-expressive {
20
+ font-family: "Google Sans Flex", system-ui, sans-serif;
21
+ font-variation-settings: "ROND" 100;
22
+ }