@happyvertical/smrt-ui 0.30.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 (330) hide show
  1. package/AGENTS.md +50 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/dist/actions/__tests__/ripple.test.js +28 -0
  5. package/dist/actions/permission.d.ts +34 -0
  6. package/dist/actions/permission.d.ts.map +1 -0
  7. package/dist/actions/permission.js +70 -0
  8. package/dist/actions/ripple.d.ts +7 -0
  9. package/dist/actions/ripple.d.ts.map +1 -0
  10. package/dist/actions/ripple.js +65 -0
  11. package/dist/components/calendar/Calendar.svelte +520 -0
  12. package/dist/components/calendar/Calendar.svelte.d.ts +17 -0
  13. package/dist/components/calendar/Calendar.svelte.d.ts.map +1 -0
  14. package/dist/components/calendar/DayView.svelte +389 -0
  15. package/dist/components/calendar/DayView.svelte.d.ts +13 -0
  16. package/dist/components/calendar/DayView.svelte.d.ts.map +1 -0
  17. package/dist/components/calendar/index.d.ts +6 -0
  18. package/dist/components/calendar/index.d.ts.map +1 -0
  19. package/dist/components/calendar/index.js +5 -0
  20. package/dist/components/chat/MessageBubble.svelte +126 -0
  21. package/dist/components/chat/MessageBubble.svelte.d.ts +30 -0
  22. package/dist/components/chat/MessageBubble.svelte.d.ts.map +1 -0
  23. package/dist/components/chat/ReactionPicker.svelte +89 -0
  24. package/dist/components/chat/ReactionPicker.svelte.d.ts +19 -0
  25. package/dist/components/chat/ReactionPicker.svelte.d.ts.map +1 -0
  26. package/dist/components/chat/TypingIndicator.svelte +90 -0
  27. package/dist/components/chat/TypingIndicator.svelte.d.ts +17 -0
  28. package/dist/components/chat/TypingIndicator.svelte.d.ts.map +1 -0
  29. package/dist/components/chat/__tests__/chat-primitives.test.js +67 -0
  30. package/dist/components/chat/index.d.ts +10 -0
  31. package/dist/components/chat/index.d.ts.map +1 -0
  32. package/dist/components/chat/index.js +9 -0
  33. package/dist/components/data/DataTable.svelte +519 -0
  34. package/dist/components/data/DataTable.svelte.d.ts +49 -0
  35. package/dist/components/data/DataTable.svelte.d.ts.map +1 -0
  36. package/dist/components/data/__tests__/DataTable.test.js +48 -0
  37. package/dist/components/data/__tests__/data-table-helpers.test.js +36 -0
  38. package/dist/components/data/index.d.ts +6 -0
  39. package/dist/components/data/index.d.ts.map +1 -0
  40. package/dist/components/data/index.js +5 -0
  41. package/dist/components/data/types.d.ts +104 -0
  42. package/dist/components/data/types.d.ts.map +1 -0
  43. package/dist/components/data/types.js +45 -0
  44. package/dist/components/display/ConfidenceBadge.svelte +142 -0
  45. package/dist/components/display/ConfidenceBadge.svelte.d.ts +25 -0
  46. package/dist/components/display/ConfidenceBadge.svelte.d.ts.map +1 -0
  47. package/dist/components/display/CurrencyDisplay.svelte +106 -0
  48. package/dist/components/display/CurrencyDisplay.svelte.d.ts +30 -0
  49. package/dist/components/display/CurrencyDisplay.svelte.d.ts.map +1 -0
  50. package/dist/components/display/DateDisplay.svelte +122 -0
  51. package/dist/components/display/DateDisplay.svelte.d.ts +24 -0
  52. package/dist/components/display/DateDisplay.svelte.d.ts.map +1 -0
  53. package/dist/components/display/Icon.svelte +77 -0
  54. package/dist/components/display/Icon.svelte.d.ts +28 -0
  55. package/dist/components/display/Icon.svelte.d.ts.map +1 -0
  56. package/dist/components/display/StatusBadge.svelte +256 -0
  57. package/dist/components/display/StatusBadge.svelte.d.ts +24 -0
  58. package/dist/components/display/StatusBadge.svelte.d.ts.map +1 -0
  59. package/dist/components/display/__tests__/ConfidenceBadge.test.js +96 -0
  60. package/dist/components/display/__tests__/CurrencyDisplay.test.js +114 -0
  61. package/dist/components/display/__tests__/DateDisplay.test.js +114 -0
  62. package/dist/components/display/__tests__/Icon.test.js +93 -0
  63. package/dist/components/display/__tests__/StatusBadge.test.js +98 -0
  64. package/dist/components/display/index.d.ts +10 -0
  65. package/dist/components/display/index.d.ts.map +1 -0
  66. package/dist/components/display/index.js +9 -0
  67. package/dist/components/display/types.d.ts +5 -0
  68. package/dist/components/display/types.d.ts.map +1 -0
  69. package/dist/components/display/types.js +4 -0
  70. package/dist/components/feedback/ConfirmDialog.svelte +226 -0
  71. package/dist/components/feedback/ConfirmDialog.svelte.d.ts +25 -0
  72. package/dist/components/feedback/ConfirmDialog.svelte.d.ts.map +1 -0
  73. package/dist/components/feedback/LoadingOverlay.svelte +281 -0
  74. package/dist/components/feedback/LoadingOverlay.svelte.d.ts +31 -0
  75. package/dist/components/feedback/LoadingOverlay.svelte.d.ts.map +1 -0
  76. package/dist/components/feedback/Modal.svelte +393 -0
  77. package/dist/components/feedback/Modal.svelte.d.ts +46 -0
  78. package/dist/components/feedback/Modal.svelte.d.ts.map +1 -0
  79. package/dist/components/feedback/ProgressBar.svelte +162 -0
  80. package/dist/components/feedback/ProgressBar.svelte.d.ts +21 -0
  81. package/dist/components/feedback/ProgressBar.svelte.d.ts.map +1 -0
  82. package/dist/components/feedback/__tests__/ConfirmDialog.test.js +111 -0
  83. package/dist/components/feedback/__tests__/LoadingOverlay.test.js +99 -0
  84. package/dist/components/feedback/__tests__/Modal.test.js +72 -0
  85. package/dist/components/feedback/__tests__/ProgressBar.test.js +89 -0
  86. package/dist/components/feedback/index.d.ts +8 -0
  87. package/dist/components/feedback/index.d.ts.map +1 -0
  88. package/dist/components/feedback/index.js +10 -0
  89. package/dist/components/layout/Container.svelte +53 -0
  90. package/dist/components/layout/Container.svelte.d.ts +11 -0
  91. package/dist/components/layout/Container.svelte.d.ts.map +1 -0
  92. package/dist/components/layout/EmptyState.svelte +187 -0
  93. package/dist/components/layout/EmptyState.svelte.d.ts +28 -0
  94. package/dist/components/layout/EmptyState.svelte.d.ts.map +1 -0
  95. package/dist/components/layout/Footer.svelte +63 -0
  96. package/dist/components/layout/Footer.svelte.d.ts +8 -0
  97. package/dist/components/layout/Footer.svelte.d.ts.map +1 -0
  98. package/dist/components/layout/Grid.svelte +241 -0
  99. package/dist/components/layout/Grid.svelte.d.ts +56 -0
  100. package/dist/components/layout/Grid.svelte.d.ts.map +1 -0
  101. package/dist/components/layout/Header.svelte +86 -0
  102. package/dist/components/layout/Header.svelte.d.ts +9 -0
  103. package/dist/components/layout/Header.svelte.d.ts.map +1 -0
  104. package/dist/components/layout/Masthead.svelte +219 -0
  105. package/dist/components/layout/Masthead.svelte.d.ts +13 -0
  106. package/dist/components/layout/Masthead.svelte.d.ts.map +1 -0
  107. package/dist/components/layout/PageHeader.svelte +131 -0
  108. package/dist/components/layout/PageHeader.svelte.d.ts +26 -0
  109. package/dist/components/layout/PageHeader.svelte.d.ts.map +1 -0
  110. package/dist/components/layout/SummaryCard.svelte +203 -0
  111. package/dist/components/layout/SummaryCard.svelte.d.ts +20 -0
  112. package/dist/components/layout/SummaryCard.svelte.d.ts.map +1 -0
  113. package/dist/components/layout/__tests__/Container.test.js +62 -0
  114. package/dist/components/layout/__tests__/EmptyState.test.js +83 -0
  115. package/dist/components/layout/__tests__/Footer.test.js +50 -0
  116. package/dist/components/layout/__tests__/Grid.test.js +121 -0
  117. package/dist/components/layout/__tests__/Header.test.js +48 -0
  118. package/dist/components/layout/__tests__/Masthead.test.js +93 -0
  119. package/dist/components/layout/__tests__/PageHeader.test.js +80 -0
  120. package/dist/components/layout/__tests__/SummaryCard.test.js +82 -0
  121. package/dist/components/layout/index.d.ts +12 -0
  122. package/dist/components/layout/index.d.ts.map +1 -0
  123. package/dist/components/layout/index.js +11 -0
  124. package/dist/components/memberships/MembershipCard.svelte +163 -0
  125. package/dist/components/memberships/MembershipCard.svelte.d.ts +12 -0
  126. package/dist/components/memberships/MembershipCard.svelte.d.ts.map +1 -0
  127. package/dist/components/memberships/MembershipList.svelte +98 -0
  128. package/dist/components/memberships/MembershipList.svelte.d.ts +19 -0
  129. package/dist/components/memberships/MembershipList.svelte.d.ts.map +1 -0
  130. package/dist/components/nav/FilterChips.svelte +152 -0
  131. package/dist/components/nav/FilterChips.svelte.d.ts +19 -0
  132. package/dist/components/nav/FilterChips.svelte.d.ts.map +1 -0
  133. package/dist/components/nav/Tabs.svelte +252 -0
  134. package/dist/components/nav/Tabs.svelte.d.ts +34 -0
  135. package/dist/components/nav/Tabs.svelte.d.ts.map +1 -0
  136. package/dist/components/nav/__tests__/FilterChips.test.js +94 -0
  137. package/dist/components/nav/__tests__/Tabs.test.js +128 -0
  138. package/dist/components/nav/index.d.ts +7 -0
  139. package/dist/components/nav/index.d.ts.map +1 -0
  140. package/dist/components/nav/index.js +6 -0
  141. package/dist/components/nav/types.d.ts +24 -0
  142. package/dist/components/nav/types.d.ts.map +1 -0
  143. package/dist/components/nav/types.js +4 -0
  144. package/dist/components/permissions/PermissionCheck.svelte +45 -0
  145. package/dist/components/permissions/PermissionCheck.svelte.d.ts +19 -0
  146. package/dist/components/permissions/PermissionCheck.svelte.d.ts.map +1 -0
  147. package/dist/components/roles/RoleBadge.svelte +84 -0
  148. package/dist/components/roles/RoleBadge.svelte.d.ts +13 -0
  149. package/dist/components/roles/RoleBadge.svelte.d.ts.map +1 -0
  150. package/dist/components/roles/RoleSelector.svelte +216 -0
  151. package/dist/components/roles/RoleSelector.svelte.d.ts +13 -0
  152. package/dist/components/roles/RoleSelector.svelte.d.ts.map +1 -0
  153. package/dist/components/theme/ThemeProvider.svelte +71 -0
  154. package/dist/components/theme/ThemeProvider.svelte.d.ts +10 -0
  155. package/dist/components/theme/ThemeProvider.svelte.d.ts.map +1 -0
  156. package/dist/components/theme/context.svelte.d.ts +15 -0
  157. package/dist/components/theme/context.svelte.d.ts.map +1 -0
  158. package/dist/components/theme/context.svelte.js +42 -0
  159. package/dist/components/theme/index.d.ts +3 -0
  160. package/dist/components/theme/index.d.ts.map +1 -0
  161. package/dist/components/theme/index.js +2 -0
  162. package/dist/components/ui/Avatar.svelte +167 -0
  163. package/dist/components/ui/Avatar.svelte.d.ts +26 -0
  164. package/dist/components/ui/Avatar.svelte.d.ts.map +1 -0
  165. package/dist/components/ui/Badge.svelte +70 -0
  166. package/dist/components/ui/Badge.svelte.d.ts +12 -0
  167. package/dist/components/ui/Badge.svelte.d.ts.map +1 -0
  168. package/dist/components/ui/Button.svelte +226 -0
  169. package/dist/components/ui/Button.svelte.d.ts +28 -0
  170. package/dist/components/ui/Button.svelte.d.ts.map +1 -0
  171. package/dist/components/ui/Card.svelte +122 -0
  172. package/dist/components/ui/Card.svelte.d.ts +15 -0
  173. package/dist/components/ui/Card.svelte.d.ts.map +1 -0
  174. package/dist/components/ui/Chip.svelte +167 -0
  175. package/dist/components/ui/Chip.svelte.d.ts +33 -0
  176. package/dist/components/ui/Chip.svelte.d.ts.map +1 -0
  177. package/dist/components/ui/Dropdown.svelte +250 -0
  178. package/dist/components/ui/Dropdown.svelte.d.ts +20 -0
  179. package/dist/components/ui/Dropdown.svelte.d.ts.map +1 -0
  180. package/dist/components/ui/Pagination.svelte +294 -0
  181. package/dist/components/ui/Pagination.svelte.d.ts +21 -0
  182. package/dist/components/ui/Pagination.svelte.d.ts.map +1 -0
  183. package/dist/components/ui/Skeleton.svelte +113 -0
  184. package/dist/components/ui/Skeleton.svelte.d.ts +24 -0
  185. package/dist/components/ui/Skeleton.svelte.d.ts.map +1 -0
  186. package/dist/components/ui/Tooltip.svelte +120 -0
  187. package/dist/components/ui/Tooltip.svelte.d.ts +24 -0
  188. package/dist/components/ui/Tooltip.svelte.d.ts.map +1 -0
  189. package/dist/components/ui/Tree.svelte +209 -0
  190. package/dist/components/ui/Tree.svelte.d.ts +17 -0
  191. package/dist/components/ui/Tree.svelte.d.ts.map +1 -0
  192. package/dist/components/ui/__tests__/Badge.test.js +76 -0
  193. package/dist/components/ui/__tests__/Button.test.js +69 -0
  194. package/dist/components/ui/__tests__/Card.test.js +103 -0
  195. package/dist/components/ui/__tests__/Pagination.test.js +99 -0
  196. package/dist/components/ui/__tests__/gap-primitives-interactive.test.js +112 -0
  197. package/dist/components/ui/__tests__/gap-primitives.test.js +84 -0
  198. package/dist/components/ui/index.d.ts +14 -0
  199. package/dist/components/ui/index.d.ts.map +1 -0
  200. package/dist/components/ui/index.js +18 -0
  201. package/dist/i18n/Trans.svelte +29 -0
  202. package/dist/i18n/Trans.svelte.d.ts +24 -0
  203. package/dist/i18n/Trans.svelte.d.ts.map +1 -0
  204. package/dist/i18n/__tests__/i18n.test.js +74 -0
  205. package/dist/i18n/__tests__/render-parity.spec.js +37 -0
  206. package/dist/i18n/context.svelte.d.ts +43 -0
  207. package/dist/i18n/context.svelte.d.ts.map +1 -0
  208. package/dist/i18n/context.svelte.js +69 -0
  209. package/dist/i18n/index.d.ts +17 -0
  210. package/dist/i18n/index.d.ts.map +1 -0
  211. package/dist/i18n/index.js +24 -0
  212. package/dist/i18n/registry.d.ts +44 -0
  213. package/dist/i18n/registry.d.ts.map +1 -0
  214. package/dist/i18n/registry.js +60 -0
  215. package/dist/i18n/render.d.ts +22 -0
  216. package/dist/i18n/render.d.ts.map +1 -0
  217. package/dist/i18n/render.js +44 -0
  218. package/dist/i18n/strings.d.ts +7 -0
  219. package/dist/i18n/strings.d.ts.map +1 -0
  220. package/dist/i18n/strings.js +19 -0
  221. package/dist/i18n/strings.ui.d.ts +34 -0
  222. package/dist/i18n/strings.ui.d.ts.map +1 -0
  223. package/dist/i18n/strings.ui.js +44 -0
  224. package/dist/i18n/use-i18n.d.ts +20 -0
  225. package/dist/i18n/use-i18n.d.ts.map +1 -0
  226. package/dist/i18n/use-i18n.js +21 -0
  227. package/dist/index.d.ts +28 -0
  228. package/dist/index.d.ts.map +1 -0
  229. package/dist/index.js +38 -0
  230. package/dist/registry/index.d.ts +6 -0
  231. package/dist/registry/index.d.ts.map +1 -0
  232. package/dist/registry/index.js +4 -0
  233. package/dist/registry/module-registry.d.ts +58 -0
  234. package/dist/registry/module-registry.d.ts.map +1 -0
  235. package/dist/registry/module-registry.js +94 -0
  236. package/dist/styles/index.d.ts +4 -0
  237. package/dist/styles/index.d.ts.map +1 -0
  238. package/dist/styles/index.js +6 -0
  239. package/dist/styles/tokens.css +76 -0
  240. package/dist/test-support/a11y.d.ts +16 -0
  241. package/dist/test-support/a11y.d.ts.map +1 -0
  242. package/dist/test-support/a11y.js +32 -0
  243. package/dist/test-support/setup.d.ts +11 -0
  244. package/dist/test-support/setup.d.ts.map +1 -0
  245. package/dist/test-support/setup.js +33 -0
  246. package/dist/theme/ThemeProvider.svelte +207 -0
  247. package/dist/theme/ThemeProvider.svelte.d.ts +22 -0
  248. package/dist/theme/ThemeProvider.svelte.d.ts.map +1 -0
  249. package/dist/theme/context.d.ts +49 -0
  250. package/dist/theme/context.d.ts.map +1 -0
  251. package/dist/theme/context.js +32 -0
  252. package/dist/theme/index.d.ts +7 -0
  253. package/dist/theme/index.d.ts.map +1 -0
  254. package/dist/theme/index.js +9 -0
  255. package/dist/theme/tokens.d.ts +309 -0
  256. package/dist/theme/tokens.d.ts.map +1 -0
  257. package/dist/theme/tokens.js +418 -0
  258. package/dist/themes/CUSTOM_THEME_GUIDE.md +341 -0
  259. package/dist/themes/README.md +675 -0
  260. package/dist/themes/ThemeProvider.svelte +275 -0
  261. package/dist/themes/ThemeProvider.svelte.d.ts +24 -0
  262. package/dist/themes/ThemeProvider.svelte.d.ts.map +1 -0
  263. package/dist/themes/__tests__/css-generator.test.js +32 -0
  264. package/dist/themes/__tests__/registry.test.js +43 -0
  265. package/dist/themes/__tests__/token-aliases.test.js +176 -0
  266. package/dist/themes/components/ColorSchemeToggle.svelte +205 -0
  267. package/dist/themes/components/ColorSchemeToggle.svelte.d.ts +14 -0
  268. package/dist/themes/components/ColorSchemeToggle.svelte.d.ts.map +1 -0
  269. package/dist/themes/components/ThemeSwitcher.svelte +188 -0
  270. package/dist/themes/components/ThemeSwitcher.svelte.d.ts +14 -0
  271. package/dist/themes/components/ThemeSwitcher.svelte.d.ts.map +1 -0
  272. package/dist/themes/components/index.d.ts +8 -0
  273. package/dist/themes/components/index.d.ts.map +1 -0
  274. package/dist/themes/components/index.js +7 -0
  275. package/dist/themes/context.svelte.d.ts +30 -0
  276. package/dist/themes/context.svelte.d.ts.map +1 -0
  277. package/dist/themes/context.svelte.js +42 -0
  278. package/dist/themes/create-theme.d.ts +99 -0
  279. package/dist/themes/create-theme.d.ts.map +1 -0
  280. package/dist/themes/create-theme.js +389 -0
  281. package/dist/themes/css-generator.d.ts +44 -0
  282. package/dist/themes/css-generator.d.ts.map +1 -0
  283. package/dist/themes/css-generator.js +226 -0
  284. package/dist/themes/glass/index.d.ts +14 -0
  285. package/dist/themes/glass/index.d.ts.map +1 -0
  286. package/dist/themes/glass/index.js +286 -0
  287. package/dist/themes/index.d.ts +31 -0
  288. package/dist/themes/index.d.ts.map +1 -0
  289. package/dist/themes/index.js +37 -0
  290. package/dist/themes/material/index.d.ts +13 -0
  291. package/dist/themes/material/index.d.ts.map +1 -0
  292. package/dist/themes/material/index.js +269 -0
  293. package/dist/themes/registry.d.ts +64 -0
  294. package/dist/themes/registry.d.ts.map +1 -0
  295. package/dist/themes/registry.js +122 -0
  296. package/dist/themes/shared.d.ts +78 -0
  297. package/dist/themes/shared.d.ts.map +1 -0
  298. package/dist/themes/shared.js +179 -0
  299. package/dist/themes/studio/index.d.ts +14 -0
  300. package/dist/themes/studio/index.d.ts.map +1 -0
  301. package/dist/themes/studio/index.js +270 -0
  302. package/dist/themes/styles/all.css +12 -0
  303. package/dist/themes/styles/glass.css +432 -0
  304. package/dist/themes/styles/index.d.ts +22 -0
  305. package/dist/themes/styles/index.d.ts.map +1 -0
  306. package/dist/themes/styles/index.js +23 -0
  307. package/dist/themes/styles/material.css +364 -0
  308. package/dist/themes/styles/studio.css +416 -0
  309. package/dist/themes/types.d.ts +273 -0
  310. package/dist/themes/types.d.ts.map +1 -0
  311. package/dist/themes/types.js +15 -0
  312. package/dist/types-generic.d.ts +75 -0
  313. package/dist/types-generic.d.ts.map +1 -0
  314. package/dist/types-generic.js +1 -0
  315. package/dist/utils/forms/__tests__/formatters.test.js +27 -0
  316. package/dist/utils/forms/formatters.d.ts +14 -0
  317. package/dist/utils/forms/formatters.d.ts.map +1 -0
  318. package/dist/utils/forms/formatters.js +77 -0
  319. package/dist/utils/import-optional.d.ts +5 -0
  320. package/dist/utils/import-optional.d.ts.map +1 -0
  321. package/dist/utils/import-optional.js +7 -0
  322. package/dist/utils/theme/__tests__/color.test.js +72 -0
  323. package/dist/utils/theme/__tests__/typography.test.js +11 -0
  324. package/dist/utils/theme/color.d.ts +70 -0
  325. package/dist/utils/theme/color.d.ts.map +1 -0
  326. package/dist/utils/theme/color.js +221 -0
  327. package/dist/utils/theme/typography.d.ts +27 -0
  328. package/dist/utils/theme/typography.d.ts.map +1 -0
  329. package/dist/utils/theme/typography.js +30 -0
  330. package/package.json +143 -0
@@ -0,0 +1,275 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { onMount } from 'svelte';
4
+ import { setThemeContext, type ThemeContext } from './context.svelte.js';
5
+ import { generateThemeVariables } from './css-generator.js';
6
+ import { getTheme } from './registry.js';
7
+ import type {
8
+ ColorScheme,
9
+ Theme,
10
+ ThemeConfig,
11
+ ThemePreset,
12
+ ThemeState as ThemeStateType,
13
+ } from './types.js';
14
+ import { defaultThemeConfig } from './types.js';
15
+
16
+ interface Props {
17
+ /** Theme preset to use */
18
+ preset?: ThemePreset;
19
+ /** Color scheme preference */
20
+ colorScheme?: ColorScheme;
21
+ /** Primary accent color override */
22
+ primaryColor?: string;
23
+ /** Border radius preference */
24
+ borderRadius?: ThemeConfig['borderRadius'];
25
+ /** Custom CSS variable overrides */
26
+ overrides?: Record<string, string>;
27
+ /** Persist preferences to localStorage */
28
+ persist?: boolean;
29
+ /** Storage key for persistence */
30
+ storageKey?: string;
31
+ /** Content */
32
+ children: Snippet;
33
+ }
34
+
35
+ let {
36
+ preset = defaultThemeConfig.preset,
37
+ colorScheme = defaultThemeConfig.colorScheme,
38
+ primaryColor,
39
+ borderRadius = defaultThemeConfig.borderRadius,
40
+ overrides = {},
41
+ persist = defaultThemeConfig.persist,
42
+ storageKey = defaultThemeConfig.storageKey,
43
+ children,
44
+ }: Props = $props();
45
+
46
+ // Internal state
47
+ let config = $state<ThemeConfig>({
48
+ preset: defaultThemeConfig.preset,
49
+ colorScheme: defaultThemeConfig.colorScheme,
50
+ primaryColor: undefined,
51
+ borderRadius: defaultThemeConfig.borderRadius,
52
+ overrides: {},
53
+ persist: defaultThemeConfig.persist,
54
+ storageKey: defaultThemeConfig.storageKey,
55
+ });
56
+
57
+ let systemPrefersDark = $state(false);
58
+ let mounted = $state(false);
59
+
60
+ // Derived state
61
+ const currentTheme = $derived<Theme>(getTheme(config.preset));
62
+
63
+ const resolvedScheme = $derived<'light' | 'dark'>(
64
+ config.colorScheme === 'system'
65
+ ? systemPrefersDark
66
+ ? 'dark'
67
+ : 'light'
68
+ : config.colorScheme,
69
+ );
70
+
71
+ const isDark = $derived(resolvedScheme === 'dark');
72
+
73
+ // Generate CSS variables - using $derived with immediate value
74
+ const cssVariables = $derived.by(() => {
75
+ const vars = generateThemeVariables(currentTheme, isDark);
76
+
77
+ // Apply primary color override
78
+ if (config.primaryColor) {
79
+ vars['--smrt-color-primary'] = config.primaryColor;
80
+ // Recalculate dependent colors would go here in a full implementation
81
+ }
82
+
83
+ // Apply border radius override
84
+ if (config.borderRadius && config.borderRadius !== 'md') {
85
+ // The border radius values are already in CSS vars, but we could
86
+ // add logic here to override specific radius values
87
+ }
88
+
89
+ // Apply custom overrides
90
+ return { ...vars, ...config.overrides };
91
+ });
92
+
93
+ // Convert to style string
94
+ const styleString = $derived.by(() => {
95
+ return Object.entries(cssVariables)
96
+ .map(([key, value]) => `${key}: ${value}`)
97
+ .join('; ');
98
+ });
99
+
100
+ // Theme state for context
101
+ const themeState = $derived<ThemeStateType>({
102
+ preset: config.preset,
103
+ colorScheme: config.colorScheme,
104
+ resolvedScheme,
105
+ isDark,
106
+ config,
107
+ theme: currentTheme,
108
+ });
109
+
110
+ // Context methods
111
+ function setPreset(newPreset: ThemePreset): void {
112
+ config.preset = newPreset;
113
+ persistConfig();
114
+ }
115
+
116
+ function setColorScheme(scheme: ColorScheme): void {
117
+ config.colorScheme = scheme;
118
+ persistConfig();
119
+ }
120
+
121
+ function toggleColorScheme(): void {
122
+ const newScheme = isDark ? 'light' : 'dark';
123
+ setColorScheme(newScheme);
124
+ }
125
+
126
+ function updateConfig(updates: Partial<ThemeConfig>): void {
127
+ config = { ...config, ...updates };
128
+ persistConfig();
129
+ }
130
+
131
+ // Persistence
132
+ function persistConfig(): void {
133
+ if (!config.persist || typeof localStorage === 'undefined') return;
134
+
135
+ try {
136
+ const data = JSON.stringify({
137
+ preset: config.preset,
138
+ colorScheme: config.colorScheme,
139
+ borderRadius: config.borderRadius,
140
+ });
141
+ localStorage.setItem(config.storageKey!, data);
142
+ } catch {
143
+ // Ignore storage errors (e.g., private mode, quota exceeded)
144
+ }
145
+ }
146
+
147
+ function loadPersistedConfig(): void {
148
+ if (!config.persist || typeof localStorage === 'undefined') return;
149
+
150
+ try {
151
+ const stored = localStorage.getItem(config.storageKey!);
152
+ if (stored) {
153
+ const data = JSON.parse(stored);
154
+ if (data.preset) config.preset = data.preset;
155
+ if (data.colorScheme) config.colorScheme = data.colorScheme;
156
+ if (data.borderRadius) config.borderRadius = data.borderRadius;
157
+ }
158
+ } catch {
159
+ // Ignore storage errors (e.g., corrupted data, JSON parse errors)
160
+ }
161
+ }
162
+
163
+ // Create context
164
+ const context: ThemeContext = {
165
+ get state() {
166
+ return themeState;
167
+ },
168
+ setPreset,
169
+ setColorScheme,
170
+ toggleColorScheme,
171
+ updateConfig,
172
+ };
173
+
174
+ setThemeContext(context);
175
+
176
+ onMount(() => {
177
+ // Check system preference
178
+ if (typeof window !== 'undefined') {
179
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
180
+ systemPrefersDark = mediaQuery.matches;
181
+
182
+ // Listen for system preference changes
183
+ const handler = (e: MediaQueryListEvent) => {
184
+ systemPrefersDark = e.matches;
185
+ };
186
+ mediaQuery.addEventListener('change', handler);
187
+
188
+ // Load persisted preferences
189
+ loadPersistedConfig();
190
+
191
+ mounted = true;
192
+
193
+ return () => {
194
+ mediaQuery.removeEventListener('change', handler);
195
+ };
196
+ }
197
+ });
198
+
199
+ // Update document classes and attributes
200
+ $effect(() => {
201
+ if (typeof document !== 'undefined' && mounted) {
202
+ const html = document.documentElement;
203
+
204
+ // Dark mode class for Tailwind/tailwindcss-dark-mode compatibility
205
+ html.classList.toggle('dark', isDark);
206
+
207
+ // Theme data attributes
208
+ html.setAttribute('data-theme', config.preset);
209
+ html.setAttribute('data-color-scheme', resolvedScheme);
210
+
211
+ // Color scheme for browser UI
212
+ html.style.colorScheme = resolvedScheme;
213
+ }
214
+ });
215
+
216
+ // Sync props to state
217
+ $effect(() => {
218
+ config.preset = preset;
219
+ });
220
+
221
+ $effect(() => {
222
+ config.colorScheme = colorScheme;
223
+ });
224
+
225
+ $effect(() => {
226
+ if (primaryColor !== undefined) {
227
+ config.primaryColor = primaryColor;
228
+ }
229
+ });
230
+
231
+ $effect(() => {
232
+ config.borderRadius = borderRadius;
233
+ });
234
+
235
+ // Sync overrides prop to state
236
+ $effect(() => {
237
+ config.overrides = overrides;
238
+ });
239
+
240
+ $effect(() => {
241
+ config.persist = persist;
242
+ });
243
+
244
+ $effect(() => {
245
+ config.storageKey = storageKey;
246
+ });
247
+ </script>
248
+
249
+ <div
250
+ class="smrt-theme-root"
251
+ class:dark={isDark}
252
+ class:smrt-theme-glass={config.preset === 'glass'}
253
+ style={styleString}
254
+ data-theme={config.preset}
255
+ data-color-scheme={resolvedScheme}
256
+ >
257
+ {@render children()}
258
+ </div>
259
+
260
+ <style>
261
+ .smrt-theme-root {
262
+ /* Ensure theme variables cascade properly */
263
+ /* Note: Using block display instead of contents so styles apply correctly */
264
+ display: block;
265
+ color: var(--smrt-color-on-background);
266
+ background-color: var(--smrt-color-background);
267
+ font-family: var(--smrt-font-family);
268
+ }
269
+
270
+ /* Glass theme specific base styles */
271
+ :global(.smrt-theme-glass) {
272
+ --smrt-glass-surface: var(--smrt-color-surface);
273
+ --smrt-glass-border: var(--smrt-color-outline);
274
+ }
275
+ </style>
@@ -0,0 +1,24 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { ColorScheme, ThemeConfig, ThemePreset } from './types.js';
3
+ interface Props {
4
+ /** Theme preset to use */
5
+ preset?: ThemePreset;
6
+ /** Color scheme preference */
7
+ colorScheme?: ColorScheme;
8
+ /** Primary accent color override */
9
+ primaryColor?: string;
10
+ /** Border radius preference */
11
+ borderRadius?: ThemeConfig['borderRadius'];
12
+ /** Custom CSS variable overrides */
13
+ overrides?: Record<string, string>;
14
+ /** Persist preferences to localStorage */
15
+ persist?: boolean;
16
+ /** Storage key for persistence */
17
+ storageKey?: string;
18
+ /** Content */
19
+ children: Snippet;
20
+ }
21
+ declare const ThemeProvider: import("svelte").Component<Props, {}, "">;
22
+ type ThemeProvider = ReturnType<typeof ThemeProvider>;
23
+ export default ThemeProvider;
24
+ //# sourceMappingURL=ThemeProvider.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThemeProvider.svelte.d.ts","sourceRoot":"","sources":["../../src/themes/ThemeProvider.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAKtC,OAAO,KAAK,EACV,WAAW,EAEX,WAAW,EACX,WAAW,EAEZ,MAAM,YAAY,CAAC;AAIpB,UAAU,KAAK;IACb,0BAA0B;IAC1B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,8BAA8B;IAC9B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,oCAAoC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+BAA+B;IAC/B,YAAY,CAAC,EAAE,WAAW,CAAC,cAAc,CAAC,CAAC;IAC3C,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,0CAA0C;IAC1C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kCAAkC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc;IACd,QAAQ,EAAE,OAAO,CAAC;CACnB;AAyOD,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Unit tests for the theme CSS generator (coverage uplift, S6 gate).
3
+ */
4
+ import { describe, expect, it } from 'vitest';
5
+ import { generateAllThemesCSS, generateThemeCSS, generateThemeVariables, variablesToStyleString, } from '../css-generator';
6
+ import { getTheme } from '../registry';
7
+ describe('theme CSS generator', () => {
8
+ const theme = getTheme('material');
9
+ it('generateThemeVariables returns --smrt-* custom properties', () => {
10
+ const vars = generateThemeVariables(theme);
11
+ const keys = Object.keys(vars);
12
+ expect(keys.length).toBeGreaterThan(0);
13
+ expect(keys.some((k) => k.startsWith('--smrt-'))).toBe(true);
14
+ });
15
+ it('variablesToStyleString emits "name: value;" declarations', () => {
16
+ const css = variablesToStyleString({
17
+ '--smrt-x': '1px',
18
+ '--smrt-y': 'red',
19
+ });
20
+ expect(css).toContain('--smrt-x: 1px');
21
+ expect(css).toContain('--smrt-y: red');
22
+ });
23
+ it('generateThemeCSS produces an embeddable block', () => {
24
+ const css = generateThemeCSS(theme);
25
+ expect(typeof css).toBe('string');
26
+ expect(css).toContain('--smrt-');
27
+ });
28
+ it('generateAllThemesCSS resolves to a non-empty string', async () => {
29
+ const css = await generateAllThemesCSS();
30
+ expect(css.length).toBeGreaterThan(0);
31
+ });
32
+ });
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Unit tests for the theme registry (coverage uplift toward smrt-svelte's T2
3
+ * floor, S11 #1416 / S6 gate). Pure logic — no rendering.
4
+ */
5
+ import { describe, expect, it } from 'vitest';
6
+ import { availablePresets, getAllThemes, getCustomTheme, getTheme, getThemeName, getThemeOptions, hasCustomTheme, isValidPreset, registerCustomTheme, themes, } from '../registry';
7
+ describe('theme registry', () => {
8
+ it('returns each built-in preset from getTheme', () => {
9
+ for (const preset of availablePresets) {
10
+ const theme = getTheme(preset);
11
+ expect(theme).toBeDefined();
12
+ expect(theme.name).toBeTruthy();
13
+ }
14
+ });
15
+ it('throws on an unknown preset', () => {
16
+ expect(() => getTheme('nope')).toThrow(/Unknown theme preset/);
17
+ });
18
+ it('validates presets', () => {
19
+ expect(isValidPreset('material')).toBe(true);
20
+ expect(isValidPreset('bogus')).toBe(false);
21
+ });
22
+ it('returns display names with a fallback', () => {
23
+ expect(getThemeName('material')).toBe(themes.material.name);
24
+ expect(getThemeName('bogus')).toBe('bogus');
25
+ });
26
+ it('lists all built-in themes and options', () => {
27
+ expect(getAllThemes()).toHaveLength(availablePresets.length);
28
+ const options = getThemeOptions();
29
+ expect(options.length).toBeGreaterThanOrEqual(availablePresets.length);
30
+ expect(options[0]).toHaveProperty('value');
31
+ expect(options[0]).toHaveProperty('label');
32
+ });
33
+ it('registers, resolves, and reports custom themes', () => {
34
+ const custom = { id: 'unit-custom', name: 'Unit Custom' };
35
+ expect(hasCustomTheme('unit-custom')).toBe(false);
36
+ registerCustomTheme(custom);
37
+ expect(hasCustomTheme('unit-custom')).toBe(true);
38
+ expect(getCustomTheme('unit-custom')).toBe(custom);
39
+ expect(isValidPreset('unit-custom')).toBe(true);
40
+ expect(getTheme('unit-custom')).toBe(custom);
41
+ expect(getThemeName('unit-custom')).toBe('Unit Custom');
42
+ });
43
+ });
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Design-token alias contract (issue #1431).
3
+ *
4
+ * Components consume a Material-3 token vocabulary (`radius-small`,
5
+ * `duration-short2`, `spacing-md`, `typography-*-font`, …) that must resolve to
6
+ * real values across every preset. These tests pin the additive aliases emitted
7
+ * by the canonical preset generator (`css-generator.ts`) and the simple
8
+ * `src/theme/` ThemeProvider so a regression that drops a consumed alias fails
9
+ * here as well as in scripts/check-svelte-tokens.mjs.
10
+ */
11
+ import { readFileSync } from 'node:fs';
12
+ import { join } from 'node:path';
13
+ import { describe, expect, it } from 'vitest';
14
+ import { generateThemeVariables as generateSimpleThemeVariables } from '../../theme/tokens.js';
15
+ import { generateThemeVariables } from '../css-generator.js';
16
+ import { themes } from '../registry.js';
17
+ /** Material-3 + helper aliases that components consume and expect emitted. */
18
+ const REQUIRED_ALIASES = [
19
+ // Radius (Material-3 named scale)
20
+ '--smrt-radius-extra-small',
21
+ '--smrt-radius-small',
22
+ '--smrt-radius-medium',
23
+ '--smrt-radius-large',
24
+ '--smrt-radius-extra-large',
25
+ // Spacing (named aliases)
26
+ '--smrt-spacing-xs',
27
+ '--smrt-spacing-sm',
28
+ '--smrt-spacing-md',
29
+ '--smrt-spacing-lg',
30
+ '--smrt-spacing-xl',
31
+ '--smrt-spacing-2xl',
32
+ '--smrt-spacing-3xl',
33
+ // Motion (Material-3 duration scale)
34
+ '--smrt-duration-short1',
35
+ '--smrt-duration-short2',
36
+ '--smrt-duration-short3',
37
+ '--smrt-duration-short4',
38
+ '--smrt-duration-medium1',
39
+ '--smrt-duration-medium2',
40
+ '--smrt-duration-medium3',
41
+ '--smrt-duration-medium4',
42
+ '--smrt-duration-long1',
43
+ '--smrt-duration-long2',
44
+ '--smrt-duration-long3',
45
+ '--smrt-duration-long4',
46
+ // Helper tokens (theme-independent)
47
+ '--smrt-font-family-mono',
48
+ '--smrt-typography-weight-normal',
49
+ '--smrt-typography-weight-medium',
50
+ '--smrt-typography-weight-semibold',
51
+ '--smrt-typography-weight-bold',
52
+ '--smrt-z-index-dialog',
53
+ '--smrt-z-index-loading',
54
+ ];
55
+ const SPACING_NUMERIC_TOKENS = [
56
+ '--smrt-spacing-0',
57
+ '--smrt-spacing-0_5',
58
+ '--smrt-spacing-1',
59
+ '--smrt-spacing-1_5',
60
+ '--smrt-spacing-2',
61
+ '--smrt-spacing-2_5',
62
+ '--smrt-spacing-3',
63
+ '--smrt-spacing-3_5',
64
+ '--smrt-spacing-4',
65
+ '--smrt-spacing-5',
66
+ '--smrt-spacing-6',
67
+ '--smrt-spacing-7',
68
+ '--smrt-spacing-8',
69
+ '--smrt-spacing-9',
70
+ '--smrt-spacing-10',
71
+ '--smrt-spacing-11',
72
+ '--smrt-spacing-12',
73
+ '--smrt-spacing-14',
74
+ '--smrt-spacing-16',
75
+ '--smrt-spacing-20',
76
+ '--smrt-spacing-24',
77
+ ];
78
+ const TYPOGRAPHY_FONT_SHORTHANDS = [
79
+ '--smrt-typography-body-large-font',
80
+ '--smrt-typography-body-medium-font',
81
+ '--smrt-typography-body-small-font',
82
+ '--smrt-typography-label-large-font',
83
+ '--smrt-typography-label-medium-font',
84
+ '--smrt-typography-label-small-font',
85
+ '--smrt-typography-title-large-font',
86
+ '--smrt-typography-title-medium-font',
87
+ '--smrt-typography-title-small-font',
88
+ '--smrt-typography-headline-small-font',
89
+ '--smrt-typography-headline-medium-font',
90
+ ];
91
+ const BASE_COMPONENT_COLOR_TOKENS = [
92
+ '--smrt-color-surface-container-highest',
93
+ '--smrt-color-surface-container-lowest',
94
+ '--smrt-color-surface-dim',
95
+ '--smrt-color-surface-bright',
96
+ '--smrt-color-shadow',
97
+ ];
98
+ const packageRoot = process.cwd().endsWith('packages/smrt-ui')
99
+ ? process.cwd()
100
+ : join(process.cwd(), 'packages/smrt-ui');
101
+ const STATIC_STYLE_PATHS = {
102
+ material: join(packageRoot, 'src/themes/styles/material.css'),
103
+ glass: join(packageRoot, 'src/themes/styles/glass.css'),
104
+ studio: join(packageRoot, 'src/themes/styles/studio.css'),
105
+ };
106
+ function collectDefinedTokens(css) {
107
+ return new Set([...css.matchAll(/(--smrt-[a-z0-9_-]+)\s*:/gi)].map((match) => match[1]));
108
+ }
109
+ describe('preset generator alias emission (#1431)', () => {
110
+ for (const theme of Object.values(themes)) {
111
+ for (const isDark of [false, true]) {
112
+ it(`${theme.id} ${isDark ? 'dark' : 'light'} emits every consumed alias`, () => {
113
+ const vars = generateThemeVariables(theme, isDark);
114
+ for (const token of [
115
+ ...REQUIRED_ALIASES,
116
+ ...SPACING_NUMERIC_TOKENS,
117
+ ...TYPOGRAPHY_FONT_SHORTHANDS,
118
+ ]) {
119
+ expect(vars[token], `${token} should be emitted`).toBeDefined();
120
+ expect(vars[token]).not.toBe('');
121
+ }
122
+ });
123
+ }
124
+ }
125
+ it('maps Material-3 radius aliases onto canonical t-shirt values', () => {
126
+ const vars = generateThemeVariables(themes.material, false);
127
+ expect(vars['--smrt-radius-small']).toBe(vars['--smrt-radius-sm']);
128
+ expect(vars['--smrt-radius-medium']).toBe(vars['--smrt-radius-md']);
129
+ expect(vars['--smrt-radius-large']).toBe(vars['--smrt-radius-lg']);
130
+ });
131
+ it('maps named spacing aliases onto numeric-scale values', () => {
132
+ const vars = generateThemeVariables(themes.material, false);
133
+ expect(vars['--smrt-spacing-sm']).toBe(vars['--smrt-spacing-2']);
134
+ expect(vars['--smrt-spacing-md']).toBe(vars['--smrt-spacing-4']);
135
+ expect(vars['--smrt-spacing-xl']).toBe(vars['--smrt-spacing-8']);
136
+ });
137
+ it('builds the typography `font` shorthand from the emitted longhands', () => {
138
+ const vars = generateThemeVariables(themes.material, false);
139
+ const shorthand = vars['--smrt-typography-body-medium-font'];
140
+ expect(shorthand).toContain(vars['--smrt-typography-body-medium-size']);
141
+ // weight size/line-height family
142
+ expect(shorthand).toMatch(/^\d+\s+\S+\/\S+\s+.+$/);
143
+ });
144
+ });
145
+ describe('static preset CSS token surface (#1431)', () => {
146
+ for (const theme of Object.values(themes)) {
147
+ it(`${theme.id} stylesheet contains the generated token surface`, () => {
148
+ const css = readFileSync(STATIC_STYLE_PATHS[theme.id], 'utf8');
149
+ const defined = collectDefinedTokens(css);
150
+ const generatedTokens = new Set([
151
+ ...Object.keys(generateThemeVariables(theme, false)),
152
+ ...Object.keys(generateThemeVariables(theme, true)),
153
+ ]);
154
+ const missing = [...generatedTokens]
155
+ .filter((token) => !defined.has(token))
156
+ .sort();
157
+ expect(missing).toEqual([]);
158
+ });
159
+ }
160
+ });
161
+ describe('simple ThemeProvider alias emission (#1431)', () => {
162
+ for (const isDark of [false, true]) {
163
+ it(`${isDark ? 'dark' : 'light'} emits shared aliases and component scale tokens`, () => {
164
+ const vars = generateSimpleThemeVariables(isDark);
165
+ for (const token of [
166
+ ...REQUIRED_ALIASES,
167
+ ...SPACING_NUMERIC_TOKENS,
168
+ ...TYPOGRAPHY_FONT_SHORTHANDS,
169
+ ...BASE_COMPONENT_COLOR_TOKENS,
170
+ ]) {
171
+ expect(vars[token], `${token} should be emitted`).toBeDefined();
172
+ expect(vars[token]).not.toBe('');
173
+ }
174
+ });
175
+ }
176
+ });