@just-web/toolkits 1.0.0 → 2.1.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 (421) hide show
  1. package/dist/attributes/get-attribute.cjs +1 -1
  2. package/dist/attributes/get-attribute.cjs.map +1 -1
  3. package/dist/attributes/get-attribute.d.cts +2 -2
  4. package/dist/attributes/get-attribute.d.cts.map +1 -1
  5. package/dist/attributes/get-attribute.d.mts +2 -2
  6. package/dist/attributes/get-attribute.d.mts.map +1 -1
  7. package/dist/attributes/get-attribute.mjs +1 -1
  8. package/dist/attributes/get-attribute.mjs.map +1 -1
  9. package/dist/attributes/get-data-attribute.cjs +1 -1
  10. package/dist/attributes/get-data-attribute.cjs.map +1 -1
  11. package/dist/attributes/get-data-attribute.d.cts +2 -2
  12. package/dist/attributes/get-data-attribute.d.cts.map +1 -1
  13. package/dist/attributes/get-data-attribute.d.mts +2 -2
  14. package/dist/attributes/get-data-attribute.d.mts.map +1 -1
  15. package/dist/attributes/get-data-attribute.mjs +1 -1
  16. package/dist/attributes/get-data-attribute.mjs.map +1 -1
  17. package/dist/attributes/observe-attribute.cjs +1 -1
  18. package/dist/attributes/observe-attribute.cjs.map +1 -1
  19. package/dist/attributes/observe-attribute.d.cts +2 -2
  20. package/dist/attributes/observe-attribute.d.cts.map +1 -1
  21. package/dist/attributes/observe-attribute.d.mts +2 -2
  22. package/dist/attributes/observe-attribute.d.mts.map +1 -1
  23. package/dist/attributes/observe-attribute.mjs +1 -1
  24. package/dist/attributes/observe-attribute.mjs.map +1 -1
  25. package/dist/attributes/observe-data-attribute.cjs +1 -1
  26. package/dist/attributes/observe-data-attribute.cjs.map +1 -1
  27. package/dist/attributes/observe-data-attribute.d.cts +2 -2
  28. package/dist/attributes/observe-data-attribute.d.cts.map +1 -1
  29. package/dist/attributes/observe-data-attribute.d.mts +2 -2
  30. package/dist/attributes/observe-data-attribute.d.mts.map +1 -1
  31. package/dist/attributes/observe-data-attribute.mjs +1 -1
  32. package/dist/attributes/observe-data-attribute.mjs.map +1 -1
  33. package/dist/color-scheme/color-scheme.types.d.cts +11 -0
  34. package/dist/color-scheme/color-scheme.types.d.cts.map +1 -0
  35. package/dist/color-scheme/color-scheme.types.d.mts +11 -0
  36. package/dist/color-scheme/color-scheme.types.d.mts.map +1 -0
  37. package/dist/color-scheme/get-prefers-color-scheme.cjs +3 -1
  38. package/dist/color-scheme/get-prefers-color-scheme.cjs.map +1 -1
  39. package/dist/color-scheme/get-prefers-color-scheme.d.cts +7 -2
  40. package/dist/color-scheme/get-prefers-color-scheme.d.cts.map +1 -1
  41. package/dist/color-scheme/get-prefers-color-scheme.d.mts +7 -2
  42. package/dist/color-scheme/get-prefers-color-scheme.d.mts.map +1 -1
  43. package/dist/color-scheme/get-prefers-color-scheme.mjs +3 -1
  44. package/dist/color-scheme/get-prefers-color-scheme.mjs.map +1 -1
  45. package/dist/color-scheme/observe-prefers-color-scheme.cjs.map +1 -1
  46. package/dist/color-scheme/observe-prefers-color-scheme.d.cts +4 -1
  47. package/dist/color-scheme/observe-prefers-color-scheme.d.cts.map +1 -1
  48. package/dist/color-scheme/observe-prefers-color-scheme.d.mts +4 -1
  49. package/dist/color-scheme/observe-prefers-color-scheme.d.mts.map +1 -1
  50. package/dist/color-scheme/observe-prefers-color-scheme.mjs.map +1 -1
  51. package/dist/index.d.cts +2 -1
  52. package/dist/index.d.mts +2 -1
  53. package/dist/react/hooks/use-attribute.cjs +7 -7
  54. package/dist/react/hooks/use-attribute.cjs.map +1 -1
  55. package/dist/react/hooks/use-attribute.d.cts +4 -4
  56. package/dist/react/hooks/use-attribute.d.mts +4 -4
  57. package/dist/react/hooks/use-attribute.mjs +7 -7
  58. package/dist/react/hooks/use-attribute.mjs.map +1 -1
  59. package/dist/react/hooks/use-theme-by-class-name.cjs +2 -6
  60. package/dist/react/hooks/use-theme-by-class-name.cjs.map +1 -1
  61. package/dist/react/hooks/use-theme-by-class-name.d.cts +2 -2
  62. package/dist/react/hooks/use-theme-by-class-name.d.cts.map +1 -1
  63. package/dist/react/hooks/use-theme-by-class-name.d.mts +2 -2
  64. package/dist/react/hooks/use-theme-by-class-name.d.mts.map +1 -1
  65. package/dist/react/hooks/use-theme-by-class-name.mjs +2 -6
  66. package/dist/react/hooks/use-theme-by-class-name.mjs.map +1 -1
  67. package/dist/react/hooks/use-theme-by-data-attribute.cjs +2 -7
  68. package/dist/react/hooks/use-theme-by-data-attribute.cjs.map +1 -1
  69. package/dist/react/hooks/use-theme-by-data-attribute.d.cts +2 -2
  70. package/dist/react/hooks/use-theme-by-data-attribute.d.cts.map +1 -1
  71. package/dist/react/hooks/use-theme-by-data-attribute.d.mts +2 -2
  72. package/dist/react/hooks/use-theme-by-data-attribute.d.mts.map +1 -1
  73. package/dist/react/hooks/use-theme-by-data-attribute.mjs +2 -7
  74. package/dist/react/hooks/use-theme-by-data-attribute.mjs.map +1 -1
  75. package/dist/react/hooks/use-theme-by-local-storage.cjs +1 -5
  76. package/dist/react/hooks/use-theme-by-local-storage.cjs.map +1 -1
  77. package/dist/react/hooks/use-theme-by-local-storage.d.cts.map +1 -1
  78. package/dist/react/hooks/use-theme-by-local-storage.d.mts.map +1 -1
  79. package/dist/react/hooks/use-theme-by-local-storage.mjs +1 -5
  80. package/dist/react/hooks/use-theme-by-local-storage.mjs.map +1 -1
  81. package/dist/react/theme/create-theme-hook.cjs.map +1 -1
  82. package/dist/react/theme/create-theme-hook.mjs.map +1 -1
  83. package/dist/theme/_utils/match-attribute-value-to-theme.cjs +29 -0
  84. package/dist/theme/_utils/match-attribute-value-to-theme.cjs.map +1 -0
  85. package/dist/theme/_utils/match-attribute-value-to-theme.mjs +28 -0
  86. package/dist/theme/_utils/match-attribute-value-to-theme.mjs.map +1 -0
  87. package/dist/theme/_utils/parse-stored-theme.cjs +61 -7
  88. package/dist/theme/_utils/parse-stored-theme.cjs.map +1 -1
  89. package/dist/theme/_utils/parse-stored-theme.mjs +61 -7
  90. package/dist/theme/_utils/parse-stored-theme.mjs.map +1 -1
  91. package/dist/theme/_utils/resolve-theme-map-value.cjs +19 -0
  92. package/dist/theme/_utils/resolve-theme-map-value.cjs.map +1 -0
  93. package/dist/theme/_utils/resolve-theme-map-value.mjs +17 -0
  94. package/dist/theme/_utils/resolve-theme-map-value.mjs.map +1 -0
  95. package/dist/theme/_utils/set-theme-to-stores.cjs +1 -1
  96. package/dist/theme/_utils/set-theme-to-stores.cjs.map +1 -1
  97. package/dist/theme/_utils/set-theme-to-stores.mjs +1 -1
  98. package/dist/theme/_utils/set-theme-to-stores.mjs.map +1 -1
  99. package/dist/theme/class-name/parse-class-name.cjs +32 -0
  100. package/dist/theme/class-name/parse-class-name.cjs.map +1 -0
  101. package/dist/theme/class-name/parse-class-name.d.cts +20 -0
  102. package/dist/theme/class-name/parse-class-name.d.cts.map +1 -0
  103. package/dist/theme/class-name/parse-class-name.d.mts +20 -0
  104. package/dist/theme/class-name/parse-class-name.d.mts.map +1 -0
  105. package/dist/theme/class-name/parse-class-name.mjs +31 -0
  106. package/dist/theme/class-name/parse-class-name.mjs.map +1 -0
  107. package/dist/theme/class-name/read-class-name.cjs +20 -0
  108. package/dist/theme/class-name/read-class-name.cjs.map +1 -0
  109. package/dist/theme/class-name/read-class-name.d.cts +20 -0
  110. package/dist/theme/class-name/read-class-name.d.cts.map +1 -0
  111. package/dist/theme/class-name/read-class-name.d.mts +20 -0
  112. package/dist/theme/class-name/read-class-name.d.mts.map +1 -0
  113. package/dist/theme/class-name/read-class-name.mjs +20 -0
  114. package/dist/theme/class-name/read-class-name.mjs.map +1 -0
  115. package/dist/theme/class-name/stringify-class-name.cjs +31 -0
  116. package/dist/theme/class-name/stringify-class-name.cjs.map +1 -0
  117. package/dist/theme/class-name/stringify-class-name.d.cts +21 -0
  118. package/dist/theme/class-name/stringify-class-name.d.cts.map +1 -0
  119. package/dist/theme/class-name/stringify-class-name.d.mts +21 -0
  120. package/dist/theme/class-name/stringify-class-name.d.mts.map +1 -0
  121. package/dist/theme/class-name/stringify-class-name.mjs +31 -0
  122. package/dist/theme/class-name/stringify-class-name.mjs.map +1 -0
  123. package/dist/theme/class-name/subscribe-class-name.cjs +31 -0
  124. package/dist/theme/class-name/subscribe-class-name.cjs.map +1 -0
  125. package/dist/theme/class-name/subscribe-class-name.d.cts +21 -0
  126. package/dist/theme/class-name/subscribe-class-name.d.cts.map +1 -0
  127. package/dist/theme/class-name/subscribe-class-name.d.mts +21 -0
  128. package/dist/theme/class-name/subscribe-class-name.d.mts.map +1 -0
  129. package/dist/theme/class-name/subscribe-class-name.mjs +31 -0
  130. package/dist/theme/class-name/subscribe-class-name.mjs.map +1 -0
  131. package/dist/theme/class-name/write-class-name.cjs +20 -0
  132. package/dist/theme/class-name/write-class-name.cjs.map +1 -0
  133. package/dist/theme/class-name/write-class-name.d.cts +20 -0
  134. package/dist/theme/class-name/write-class-name.d.cts.map +1 -0
  135. package/dist/theme/class-name/write-class-name.d.mts +20 -0
  136. package/dist/theme/class-name/write-class-name.d.mts.map +1 -0
  137. package/dist/theme/class-name/write-class-name.mjs +20 -0
  138. package/dist/theme/class-name/write-class-name.mjs.map +1 -0
  139. package/dist/theme/compose-theme-stores.cjs.map +1 -1
  140. package/dist/theme/compose-theme-stores.mjs.map +1 -1
  141. package/dist/theme/cookie/_cookie-utils.cjs +37 -0
  142. package/dist/theme/cookie/_cookie-utils.cjs.map +1 -0
  143. package/dist/theme/cookie/_cookie-utils.mjs +33 -0
  144. package/dist/theme/cookie/_cookie-utils.mjs.map +1 -0
  145. package/dist/theme/cookie/read-cookie-theme.cjs +22 -0
  146. package/dist/theme/cookie/read-cookie-theme.cjs.map +1 -0
  147. package/dist/theme/cookie/read-cookie-theme.d.cts +22 -0
  148. package/dist/theme/cookie/read-cookie-theme.d.cts.map +1 -0
  149. package/dist/theme/cookie/read-cookie-theme.d.mts +22 -0
  150. package/dist/theme/cookie/read-cookie-theme.d.mts.map +1 -0
  151. package/dist/theme/cookie/read-cookie-theme.mjs +22 -0
  152. package/dist/theme/cookie/read-cookie-theme.mjs.map +1 -0
  153. package/dist/theme/cookie/write-cookie-theme.cjs +29 -0
  154. package/dist/theme/cookie/write-cookie-theme.cjs.map +1 -0
  155. package/dist/theme/cookie/write-cookie-theme.d.cts +24 -0
  156. package/dist/theme/cookie/write-cookie-theme.d.cts.map +1 -0
  157. package/dist/theme/cookie/write-cookie-theme.d.mts +24 -0
  158. package/dist/theme/cookie/write-cookie-theme.d.mts.map +1 -0
  159. package/dist/theme/cookie/write-cookie-theme.mjs +29 -0
  160. package/dist/theme/cookie/write-cookie-theme.mjs.map +1 -0
  161. package/dist/theme/data-attribute/_constant.cjs +7 -0
  162. package/dist/theme/data-attribute/_constant.cjs.map +1 -0
  163. package/dist/theme/data-attribute/_constant.mjs +6 -0
  164. package/dist/theme/data-attribute/_constant.mjs.map +1 -0
  165. package/dist/theme/data-attribute/parse-data-attribute.cjs +24 -0
  166. package/dist/theme/data-attribute/parse-data-attribute.cjs.map +1 -0
  167. package/dist/theme/data-attribute/parse-data-attribute.d.cts +21 -0
  168. package/dist/theme/data-attribute/parse-data-attribute.d.cts.map +1 -0
  169. package/dist/theme/data-attribute/parse-data-attribute.d.mts +21 -0
  170. package/dist/theme/data-attribute/parse-data-attribute.d.mts.map +1 -0
  171. package/dist/theme/data-attribute/parse-data-attribute.mjs +24 -0
  172. package/dist/theme/data-attribute/parse-data-attribute.mjs.map +1 -0
  173. package/dist/theme/data-attribute/read-data-attribute.cjs +23 -0
  174. package/dist/theme/data-attribute/read-data-attribute.cjs.map +1 -0
  175. package/dist/theme/data-attribute/read-data-attribute.d.cts +21 -0
  176. package/dist/theme/data-attribute/read-data-attribute.d.cts.map +1 -0
  177. package/dist/theme/data-attribute/read-data-attribute.d.mts +21 -0
  178. package/dist/theme/data-attribute/read-data-attribute.d.mts.map +1 -0
  179. package/dist/theme/data-attribute/read-data-attribute.mjs +23 -0
  180. package/dist/theme/data-attribute/read-data-attribute.mjs.map +1 -0
  181. package/dist/theme/data-attribute/stringify-data-attribute.cjs +33 -0
  182. package/dist/theme/data-attribute/stringify-data-attribute.cjs.map +1 -0
  183. package/dist/theme/data-attribute/stringify-data-attribute.d.cts +23 -0
  184. package/dist/theme/data-attribute/stringify-data-attribute.d.cts.map +1 -0
  185. package/dist/theme/data-attribute/stringify-data-attribute.d.mts +23 -0
  186. package/dist/theme/data-attribute/stringify-data-attribute.d.mts.map +1 -0
  187. package/dist/theme/data-attribute/stringify-data-attribute.mjs +33 -0
  188. package/dist/theme/data-attribute/stringify-data-attribute.mjs.map +1 -0
  189. package/dist/theme/data-attribute/subscribe-data-attribute.cjs +28 -0
  190. package/dist/theme/data-attribute/subscribe-data-attribute.cjs.map +1 -0
  191. package/dist/theme/data-attribute/subscribe-data-attribute.d.cts +22 -0
  192. package/dist/theme/data-attribute/subscribe-data-attribute.d.cts.map +1 -0
  193. package/dist/theme/data-attribute/subscribe-data-attribute.d.mts +22 -0
  194. package/dist/theme/data-attribute/subscribe-data-attribute.d.mts.map +1 -0
  195. package/dist/theme/data-attribute/subscribe-data-attribute.mjs +28 -0
  196. package/dist/theme/data-attribute/subscribe-data-attribute.mjs.map +1 -0
  197. package/dist/theme/data-attribute/write-data-attribute.cjs +30 -0
  198. package/dist/theme/data-attribute/write-data-attribute.cjs.map +1 -0
  199. package/dist/theme/data-attribute/write-data-attribute.d.cts +21 -0
  200. package/dist/theme/data-attribute/write-data-attribute.d.cts.map +1 -0
  201. package/dist/theme/data-attribute/write-data-attribute.d.mts +21 -0
  202. package/dist/theme/data-attribute/write-data-attribute.d.mts.map +1 -0
  203. package/dist/theme/data-attribute/write-data-attribute.mjs +30 -0
  204. package/dist/theme/data-attribute/write-data-attribute.mjs.map +1 -0
  205. package/dist/theme/local-storage/read-local-storage.cjs +22 -0
  206. package/dist/theme/local-storage/read-local-storage.cjs.map +1 -0
  207. package/dist/theme/local-storage/read-local-storage.d.cts +19 -0
  208. package/dist/theme/local-storage/read-local-storage.d.cts.map +1 -0
  209. package/dist/theme/local-storage/read-local-storage.d.mts +19 -0
  210. package/dist/theme/local-storage/read-local-storage.d.mts.map +1 -0
  211. package/dist/theme/local-storage/read-local-storage.mjs +22 -0
  212. package/dist/theme/local-storage/read-local-storage.mjs.map +1 -0
  213. package/dist/theme/local-storage/write-local-storage.cjs +26 -0
  214. package/dist/theme/local-storage/write-local-storage.cjs.map +1 -0
  215. package/dist/theme/local-storage/write-local-storage.d.cts +23 -0
  216. package/dist/theme/local-storage/write-local-storage.d.cts.map +1 -0
  217. package/dist/theme/local-storage/write-local-storage.d.mts +23 -0
  218. package/dist/theme/local-storage/write-local-storage.d.mts.map +1 -0
  219. package/dist/theme/local-storage/write-local-storage.mjs +26 -0
  220. package/dist/theme/local-storage/write-local-storage.mjs.map +1 -0
  221. package/dist/theme/prefers-color-scheme-theme/read-prefers-color-scheme-theme.cjs +20 -0
  222. package/dist/theme/prefers-color-scheme-theme/read-prefers-color-scheme-theme.cjs.map +1 -0
  223. package/dist/theme/prefers-color-scheme-theme/read-prefers-color-scheme-theme.d.cts +23 -0
  224. package/dist/theme/prefers-color-scheme-theme/read-prefers-color-scheme-theme.d.cts.map +1 -0
  225. package/dist/theme/prefers-color-scheme-theme/read-prefers-color-scheme-theme.d.mts +23 -0
  226. package/dist/theme/prefers-color-scheme-theme/read-prefers-color-scheme-theme.d.mts.map +1 -0
  227. package/dist/theme/prefers-color-scheme-theme/read-prefers-color-scheme-theme.mjs +20 -0
  228. package/dist/theme/prefers-color-scheme-theme/read-prefers-color-scheme-theme.mjs.map +1 -0
  229. package/dist/theme/prefers-color-scheme-theme/subscribe-prefers-color-scheme-theme.cjs +20 -0
  230. package/dist/theme/prefers-color-scheme-theme/subscribe-prefers-color-scheme-theme.cjs.map +1 -0
  231. package/dist/theme/prefers-color-scheme-theme/subscribe-prefers-color-scheme-theme.d.cts +20 -0
  232. package/dist/theme/prefers-color-scheme-theme/subscribe-prefers-color-scheme-theme.d.cts.map +1 -0
  233. package/dist/theme/prefers-color-scheme-theme/subscribe-prefers-color-scheme-theme.d.mts +20 -0
  234. package/dist/theme/prefers-color-scheme-theme/subscribe-prefers-color-scheme-theme.d.mts.map +1 -0
  235. package/dist/theme/prefers-color-scheme-theme/subscribe-prefers-color-scheme-theme.mjs +20 -0
  236. package/dist/theme/prefers-color-scheme-theme/subscribe-prefers-color-scheme-theme.mjs.map +1 -0
  237. package/dist/theme/session-storage/read-session-storage.cjs +22 -0
  238. package/dist/theme/session-storage/read-session-storage.cjs.map +1 -0
  239. package/dist/theme/session-storage/read-session-storage.d.cts +19 -0
  240. package/dist/theme/session-storage/read-session-storage.d.cts.map +1 -0
  241. package/dist/theme/session-storage/read-session-storage.d.mts +19 -0
  242. package/dist/theme/session-storage/read-session-storage.d.mts.map +1 -0
  243. package/dist/theme/session-storage/read-session-storage.mjs +22 -0
  244. package/dist/theme/session-storage/read-session-storage.mjs.map +1 -0
  245. package/dist/theme/session-storage/write-session-storage.cjs +26 -0
  246. package/dist/theme/session-storage/write-session-storage.cjs.map +1 -0
  247. package/dist/theme/session-storage/write-session-storage.d.cts +23 -0
  248. package/dist/theme/session-storage/write-session-storage.d.cts.map +1 -0
  249. package/dist/theme/session-storage/write-session-storage.d.mts +23 -0
  250. package/dist/theme/session-storage/write-session-storage.d.mts.map +1 -0
  251. package/dist/theme/session-storage/write-session-storage.mjs +26 -0
  252. package/dist/theme/session-storage/write-session-storage.mjs.map +1 -0
  253. package/dist/theme/theme-entry.types.d.cts +13 -1
  254. package/dist/theme/theme-entry.types.d.cts.map +1 -1
  255. package/dist/theme/theme-entry.types.d.mts +13 -1
  256. package/dist/theme/theme-entry.types.d.mts.map +1 -1
  257. package/dist/theme/theme-map.types.d.cts +11 -3
  258. package/dist/theme/theme-map.types.d.cts.map +1 -1
  259. package/dist/theme/theme-map.types.d.mts +11 -3
  260. package/dist/theme/theme-map.types.d.mts.map +1 -1
  261. package/dist/theme/theme-store/async-theme-store.types.d.cts +1 -1
  262. package/dist/theme/theme-store/async-theme-store.types.d.cts.map +1 -1
  263. package/dist/theme/theme-store/async-theme-store.types.d.mts +1 -1
  264. package/dist/theme/theme-store/async-theme-store.types.d.mts.map +1 -1
  265. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.cjs +22 -19
  266. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.cjs.map +1 -1
  267. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.d.cts +7 -2
  268. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.d.cts.map +1 -1
  269. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.d.mts +7 -2
  270. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.d.mts.map +1 -1
  271. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.mjs +22 -19
  272. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.mjs.map +1 -1
  273. package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.cjs +18 -36
  274. package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.cjs.map +1 -1
  275. package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.d.cts +5 -3
  276. package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.d.cts.map +1 -1
  277. package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.d.mts +5 -3
  278. package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.d.mts.map +1 -1
  279. package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.mjs +18 -36
  280. package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.mjs.map +1 -1
  281. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.cjs +40 -20
  282. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.cjs.map +1 -1
  283. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.d.cts +30 -7
  284. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.d.cts.map +1 -1
  285. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.d.mts +30 -7
  286. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.d.mts.map +1 -1
  287. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.mjs +40 -20
  288. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.mjs.map +1 -1
  289. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.cjs +1 -1
  290. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.cjs.map +1 -1
  291. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.d.cts +1 -1
  292. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.d.mts +1 -1
  293. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.mjs +1 -1
  294. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.mjs.map +1 -1
  295. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.cjs +13 -12
  296. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.cjs.map +1 -1
  297. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.d.cts +8 -2
  298. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.d.cts.map +1 -1
  299. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.d.mts +8 -2
  300. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.d.mts.map +1 -1
  301. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.mjs +13 -12
  302. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.mjs.map +1 -1
  303. package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.cjs +4 -5
  304. package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.cjs.map +1 -1
  305. package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.d.cts.map +1 -1
  306. package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.d.mts.map +1 -1
  307. package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.mjs +4 -5
  308. package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.mjs.map +1 -1
  309. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.cjs +13 -12
  310. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.cjs.map +1 -1
  311. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.d.cts +8 -2
  312. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.d.cts.map +1 -1
  313. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.d.mts +8 -2
  314. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.d.mts.map +1 -1
  315. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.mjs +13 -12
  316. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.mjs.map +1 -1
  317. package/dist/theme/theme-store/theme-store.types.d.cts +1 -1
  318. package/dist/theme/theme-store/theme-store.types.d.cts.map +1 -1
  319. package/dist/theme/theme-store/theme-store.types.d.mts +1 -1
  320. package/dist/theme/theme-store/theme-store.types.d.mts.map +1 -1
  321. package/dist/theme/web-storage/read-web-storage.cjs +20 -0
  322. package/dist/theme/web-storage/read-web-storage.cjs.map +1 -0
  323. package/dist/theme/web-storage/read-web-storage.d.cts +21 -0
  324. package/dist/theme/web-storage/read-web-storage.d.cts.map +1 -0
  325. package/dist/theme/web-storage/read-web-storage.d.mts +21 -0
  326. package/dist/theme/web-storage/read-web-storage.d.mts.map +1 -0
  327. package/dist/theme/web-storage/read-web-storage.mjs +20 -0
  328. package/dist/theme/web-storage/read-web-storage.mjs.map +1 -0
  329. package/dist/theme/web-storage/write-web-storage.cjs +33 -0
  330. package/dist/theme/web-storage/write-web-storage.cjs.map +1 -0
  331. package/dist/theme/web-storage/write-web-storage.d.cts +25 -0
  332. package/dist/theme/web-storage/write-web-storage.d.cts.map +1 -0
  333. package/dist/theme/web-storage/write-web-storage.d.mts +25 -0
  334. package/dist/theme/web-storage/write-web-storage.d.mts.map +1 -0
  335. package/dist/theme/web-storage/write-web-storage.mjs +32 -0
  336. package/dist/theme/web-storage/write-web-storage.mjs.map +1 -0
  337. package/dist/theme.cjs +41 -1
  338. package/dist/theme.d.cts +23 -3
  339. package/dist/theme.d.mts +23 -3
  340. package/dist/theme.mjs +21 -1
  341. package/dist/utils/append-id.cjs +2 -2
  342. package/dist/utils/append-id.cjs.map +1 -1
  343. package/dist/utils/append-id.d.cts +3 -3
  344. package/dist/utils/append-id.d.mts +3 -3
  345. package/dist/utils/append-id.mjs +2 -2
  346. package/dist/utils/append-id.mjs.map +1 -1
  347. package/package.json +1 -1
  348. package/src/attributes/get-attribute.ts +5 -2
  349. package/src/attributes/get-data-attribute.ts +5 -2
  350. package/src/attributes/observe-attribute.ts +2 -2
  351. package/src/attributes/observe-data-attribute.ts +2 -2
  352. package/src/color-scheme/color-scheme.types.ts +7 -0
  353. package/src/color-scheme/get-prefers-color-scheme.ts +6 -4
  354. package/src/color-scheme/observe-prefers-color-scheme.ts +3 -1
  355. package/src/index.ts +1 -0
  356. package/src/react/hooks/use-attribute.ts +11 -11
  357. package/src/react/hooks/use-theme-by-class-name.ts +5 -10
  358. package/src/react/hooks/use-theme-by-data-attribute.ts +5 -12
  359. package/src/react/hooks/use-theme-by-local-storage.ts +3 -9
  360. package/src/react/theme/create-theme-hook.ts +4 -6
  361. package/src/testing/theme/theme-result-card.tsx +1 -0
  362. package/src/theme/_utils/match-attribute-value-to-theme.ts +36 -0
  363. package/src/theme/_utils/parse-stored-theme.ts +51 -11
  364. package/src/theme/_utils/resolve-theme-map-value.ts +15 -0
  365. package/src/theme/_utils/set-theme-to-stores.ts +3 -3
  366. package/src/theme/class-name/parse-class-name.ts +31 -0
  367. package/src/theme/class-name/read-class-name.ts +24 -0
  368. package/src/theme/class-name/stringify-class-name.ts +36 -0
  369. package/src/theme/class-name/subscribe-class-name.ts +39 -0
  370. package/src/theme/class-name/write-class-name.ts +24 -0
  371. package/src/theme/compose-theme-stores.ts +1 -1
  372. package/src/theme/cookie/_cookie-utils.ts +45 -0
  373. package/src/theme/cookie/read-cookie-theme.ts +33 -0
  374. package/src/theme/cookie/write-cookie-theme.ts +48 -0
  375. package/src/theme/data-attribute/_constant.ts +1 -0
  376. package/src/theme/data-attribute/parse-data-attribute.ts +25 -0
  377. package/src/theme/data-attribute/read-data-attribute.ts +29 -0
  378. package/src/theme/data-attribute/stringify-data-attribute.ts +39 -0
  379. package/src/theme/data-attribute/subscribe-data-attribute.ts +39 -0
  380. package/src/theme/data-attribute/write-data-attribute.ts +40 -0
  381. package/src/theme/local-storage/read-local-storage.ts +23 -0
  382. package/src/theme/local-storage/write-local-storage.ts +31 -0
  383. package/src/theme/prefers-color-scheme-theme/read-prefers-color-scheme-theme.ts +30 -0
  384. package/src/theme/prefers-color-scheme-theme/subscribe-prefers-color-scheme-theme.ts +24 -0
  385. package/src/theme/session-storage/read-session-storage.ts +23 -0
  386. package/src/theme/session-storage/write-session-storage.ts +31 -0
  387. package/src/theme/theme-entry.types.ts +21 -0
  388. package/src/theme/theme-map.types.ts +9 -2
  389. package/src/theme/theme-store/async-theme-store.types.ts +1 -3
  390. package/src/theme/theme-store/class-name-theme-store/class-name-theme-store.ts +20 -26
  391. package/src/theme/theme-store/cookie-theme-store/cookie-theme-store.ts +18 -63
  392. package/src/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.ts +42 -29
  393. package/src/theme/theme-store/in-memory-theme-store/in-memory-theme-store.ts +1 -1
  394. package/src/theme/theme-store/local-storage-theme-store/local-storage-theme-store.ts +17 -20
  395. package/src/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.ts +4 -5
  396. package/src/theme/theme-store/session-storage-theme-store/session-storage-theme-store.ts +17 -20
  397. package/src/theme/theme-store/theme-store.types.ts +1 -3
  398. package/src/theme/web-storage/read-web-storage.ts +22 -0
  399. package/src/theme/web-storage/write-web-storage.ts +46 -0
  400. package/src/theme.ts +20 -0
  401. package/src/utils/append-id.ts +3 -3
  402. package/dist/theme/class-name/apply-theme-to-class-name.cjs +0 -23
  403. package/dist/theme/class-name/apply-theme-to-class-name.cjs.map +0 -1
  404. package/dist/theme/class-name/apply-theme-to-class-name.mjs +0 -22
  405. package/dist/theme/class-name/apply-theme-to-class-name.mjs.map +0 -1
  406. package/dist/theme/class-name/resolve-theme-from-class-name.cjs +0 -23
  407. package/dist/theme/class-name/resolve-theme-from-class-name.cjs.map +0 -1
  408. package/dist/theme/class-name/resolve-theme-from-class-name.mjs +0 -22
  409. package/dist/theme/class-name/resolve-theme-from-class-name.mjs.map +0 -1
  410. package/dist/theme/data-attribute/apply-theme-to-data-attribute.cjs +0 -23
  411. package/dist/theme/data-attribute/apply-theme-to-data-attribute.cjs.map +0 -1
  412. package/dist/theme/data-attribute/apply-theme-to-data-attribute.mjs +0 -22
  413. package/dist/theme/data-attribute/apply-theme-to-data-attribute.mjs.map +0 -1
  414. package/dist/theme/data-attribute/resolve-theme-from-data-attribute.cjs +0 -23
  415. package/dist/theme/data-attribute/resolve-theme-from-data-attribute.cjs.map +0 -1
  416. package/dist/theme/data-attribute/resolve-theme-from-data-attribute.mjs +0 -22
  417. package/dist/theme/data-attribute/resolve-theme-from-data-attribute.mjs.map +0 -1
  418. package/src/theme/class-name/apply-theme-to-class-name.ts +0 -26
  419. package/src/theme/class-name/resolve-theme-from-class-name.ts +0 -22
  420. package/src/theme/data-attribute/apply-theme-to-data-attribute.ts +0 -27
  421. package/src/theme/data-attribute/resolve-theme-from-data-attribute.ts +0 -23
@@ -0,0 +1,36 @@
1
+ import { findKey } from 'type-plus'
2
+ import type { ThemeMap } from '../theme-map.types.ts'
3
+ import { resolveThemeMapValue } from './resolve-theme-map-value.ts'
4
+
5
+ /**
6
+ * Matches an attribute value string against the theme map and returns the theme key.
7
+ * Pure function: no DOM access. Used by parseDataAttribute and custom parse paths.
8
+ *
9
+ * @param themes - Record mapping theme keys to attribute values
10
+ * @param attrValue - Attribute value string (e.g. from getAttribute)
11
+ * @param options.separator - When defined, split by separator and use first token
12
+ * @returns Theme key if a match is found, otherwise undefined
13
+ */
14
+ export function matchAttributeValueToTheme<Theme extends string>(
15
+ themes: ThemeMap<Theme>,
16
+ attrValue: string | null,
17
+ options?: { separator?: string | undefined } | undefined
18
+ ): Theme | undefined {
19
+ if (attrValue === null || attrValue === '') return undefined
20
+ const valueToMatch =
21
+ options?.separator !== undefined
22
+ ? attrValue
23
+ .trim()
24
+ .split(options.separator)
25
+ .find((s) => s.trim() !== '')
26
+ : attrValue
27
+ if (valueToMatch === undefined) return undefined
28
+ const theme = findKey(themes, (key) => {
29
+ const value = themes[key]
30
+ if (value === undefined) return false
31
+ const resolved = resolveThemeMapValue(value)
32
+ const v = Array.isArray(resolved) ? resolved[0] : resolved
33
+ return v === valueToMatch
34
+ })
35
+ return theme
36
+ }
@@ -1,21 +1,61 @@
1
1
  import { tryParseJSON } from '../../_internal/utils/try-parse-json.ts'
2
- import type { ThemeMap } from '../theme-map.types.ts'
2
+ import type { ThemeEntry } from '../theme-entry.types.ts'
3
+ import type { ThemeMap, ThemeMapValue } from '../theme-map.types.ts'
4
+ import { isReadonlyStringArray } from './resolve-theme-map-value.ts'
5
+
6
+ type Shape = 'string' | 'array' | 'object-string' | 'object-array'
7
+
8
+ function getShapeAndComparable(
9
+ v: unknown
10
+ ): { shape: Shape; comparable: string | undefined } | null {
11
+ if (typeof v === 'string') return { shape: 'string', comparable: v }
12
+ if (Array.isArray(v)) return { shape: 'array', comparable: v[0] }
13
+ if (v !== null && typeof v === 'object' && 'themeValue' in v) {
14
+ const tv = (v as { themeValue: string | string[] }).themeValue
15
+ if (typeof tv === 'string') return { shape: 'object-string', comparable: tv }
16
+ if (Array.isArray(tv)) return { shape: 'object-array', comparable: tv[0] }
17
+ }
18
+ return null
19
+ }
20
+
21
+ function getCanonicalShapeAndComparable(v: ThemeMapValue): {
22
+ shape: Shape
23
+ comparable: string | undefined
24
+ } {
25
+ if (typeof v === 'string') return { shape: 'string', comparable: v }
26
+ if (isReadonlyStringArray(v)) return { shape: 'array', comparable: v[0] }
27
+ const tv = v.themeValue
28
+ if (typeof tv === 'string') return { shape: 'object-string', comparable: tv }
29
+ return { shape: 'object-array', comparable: tv[0] }
30
+ }
3
31
 
4
32
  /**
5
- * Parses stored JSON theme and validates the theme key against theme map.
33
+ * Parses stored JSON theme and validates against theme map with strict shape and comparable matching.
6
34
  *
7
35
  * Expects stored shape: { theme: string, value?: unknown }
8
36
  *
9
- * @param themes - Record of valid theme keys (optional; if omitted, any theme string is accepted)
10
- * @param value - Raw string from localStorage/sessionStorage
11
- * @returns Theme key if valid, otherwise undefined
37
+ * When shape matches AND comparable value (string or [0]) matches themes[theme]:
38
+ * returns { theme, value: stored.value }. Else returns undefined.
39
+ *
40
+ * @param themes - Record of valid theme keys and values (required for validation)
41
+ * @param value - Raw string from localStorage/sessionStorage/cookie (accepts null)
42
+ * @returns ThemeEntry when valid, otherwise undefined
12
43
  */
13
- export function parseStoredTheme<Theme extends string>(
14
- themes: ThemeMap<Theme> | undefined,
44
+ export function parseStoredTheme<Themes extends ThemeMap>(
45
+ themes: Themes | undefined,
15
46
  value: string | null | undefined
16
- ): Theme | undefined {
17
- const parsed = tryParseJSON<{ theme: string }>(value)
47
+ ): ThemeEntry<Themes> | undefined {
48
+ const parsed = tryParseJSON<{ theme: string; value?: unknown }>(value ?? null)
18
49
  if (!parsed?.theme || typeof parsed.theme !== 'string') return undefined
19
- if (themes && !(parsed.theme in themes)) return undefined
20
- return parsed.theme as Theme
50
+ if (!themes || !(parsed.theme in themes)) return undefined
51
+ if (parsed.value === undefined) return undefined
52
+
53
+ const storedInfo = getShapeAndComparable(parsed.value)
54
+ if (!storedInfo) return undefined
55
+
56
+ const canonical = getCanonicalShapeAndComparable(themes[parsed.theme] as ThemeMapValue)
57
+ if (storedInfo.shape !== canonical.shape) return undefined
58
+ if (storedInfo.comparable !== canonical.comparable) return undefined
59
+
60
+ return { theme: parsed.theme as keyof Themes, value: parsed.value as Themes[keyof Themes] }
21
61
  }
@@ -0,0 +1,15 @@
1
+ import type { ThemeMapValue } from '../theme-map.types.ts'
2
+
3
+ export function isReadonlyStringArray(v: ThemeMapValue): v is readonly string[] {
4
+ return Array.isArray(v)
5
+ }
6
+
7
+ /**
8
+ * Resolves ThemeMapValue to its underlying string or string[] for DOM application and matching.
9
+ * Used when applying themes to className, data attributes, or when resolving theme from DOM.
10
+ */
11
+ export function resolveThemeMapValue(v: ThemeMapValue): string | readonly string[] {
12
+ if (typeof v === 'string') return v
13
+ if (isReadonlyStringArray(v)) return v
14
+ return v.themeValue
15
+ }
@@ -4,18 +4,18 @@ import type { AsyncThemeStore } from '../theme-store/async-theme-store.types.ts'
4
4
  import type { ThemeStore } from '../theme-store/theme-store.types.ts'
5
5
 
6
6
  type StoreWithWrite<Themes extends ThemeMap> = (ThemeStore<Themes> | AsyncThemeStore<Themes>) & {
7
- write: (entry: ThemeEntry<Themes> | undefined) => void | Promise<void>
7
+ write: (entry: ThemeEntry<Themes> | null | undefined) => void | Promise<void>
8
8
  }
9
9
 
10
10
  /**
11
11
  * Writes theme entry to all stores that have a write method.
12
12
  *
13
13
  * @param stores - Array of theme stores
14
- * @param entry - Theme entry to write, or undefined to clear
14
+ * @param entry - Theme entry to write, or null/undefined to clear
15
15
  */
16
16
  export async function setThemeToStores<Themes extends ThemeMap>(
17
17
  stores: (ThemeStore<Themes> | AsyncThemeStore<Themes>)[],
18
- entry: ThemeEntry<Themes> | undefined
18
+ entry: ThemeEntry<Themes> | null | undefined
19
19
  ): Promise<void> {
20
20
  const withWrite = stores.filter((s): s is StoreWithWrite<Themes> => typeof s.write === 'function')
21
21
 
@@ -0,0 +1,31 @@
1
+ import { findKey } from 'type-plus'
2
+ import { resolveThemeMapValue } from '../_utils/resolve-theme-map-value.ts'
3
+ import { themeEntry } from '../theme-entry.ts'
4
+ import type { ThemeEntry } from '../theme-entry.types.ts'
5
+ import type { ThemeMap } from '../theme-map.types.ts'
6
+
7
+ /**
8
+ * Parses a class name string into a ThemeEntry.
9
+ *
10
+ * Pure function: no DOM access. Matches class strings by checking if any theme
11
+ * class is included in the className (e.g. `className.includes(themeClass)`).
12
+ * Arrays in theme map use first value for matching.
13
+ *
14
+ * @param themes - Record mapping theme keys to class name(s)
15
+ * @param className - Raw class attribute value (e.g. from element.className; accepts null)
16
+ * @returns ThemeEntry if a match is found, otherwise undefined
17
+ */
18
+ export function parseClassName<Themes extends ThemeMap>(
19
+ themes: Themes,
20
+ className: string | null | undefined
21
+ ): ThemeEntry<Themes> | undefined {
22
+ const cls = className ?? ''
23
+ const theme = findKey(themes, (key) => {
24
+ const value = themes[key]
25
+ if (value === undefined) return false
26
+ const resolved = resolveThemeMapValue(value)
27
+ const v = Array.isArray(resolved) ? resolved[0] : resolved
28
+ return !!v && cls.includes(v)
29
+ })
30
+ return theme !== undefined ? themeEntry(themes, theme) : undefined
31
+ }
@@ -0,0 +1,24 @@
1
+ import type { ParseStoredTheme, ThemeEntry } from '../theme-entry.types.ts'
2
+ import type { ThemeMap } from '../theme-map.types.ts'
3
+ import { parseClassName } from './parse-class-name.ts'
4
+
5
+ /**
6
+ * Reads a theme entry from the class attribute on an element.
7
+ *
8
+ * @param themes - Record mapping theme keys to class name(s)
9
+ * @param options.element - Element to read from (accepts null e.g. from refs). Defaults to document.documentElement.
10
+ * @param options.parse - Custom parser (default: parseClassName)
11
+ * @returns ThemeEntry if found, undefined otherwise. Returns undefined when element is not available (e.g. SSR).
12
+ */
13
+ export function readClassName<Themes extends ThemeMap>(
14
+ themes: Themes,
15
+ options?:
16
+ | { element?: Element | null | undefined; parse?: ParseStoredTheme<Themes> | undefined }
17
+ | undefined
18
+ ): ThemeEntry<Themes> | undefined {
19
+ const element = options?.element ?? document?.documentElement
20
+ if (!element) return undefined
21
+ const parse = options?.parse ?? parseClassName
22
+ const raw = element.className ?? undefined
23
+ return parse(themes, raw)
24
+ }
@@ -0,0 +1,36 @@
1
+ import { resolveThemeMapValue } from '../_utils/resolve-theme-map-value.ts'
2
+ import type { ThemeEntry } from '../theme-entry.types.ts'
3
+ import type { ThemeMap } from '../theme-map.types.ts'
4
+
5
+ /**
6
+ * Stringifies a ThemeEntry for a class attribute value.
7
+ *
8
+ * Pure function: no DOM access. Removes all theme classes from existing, then
9
+ * adds entry's class(es). Applies all classes from entry when value is an array
10
+ * (unlike stringifyDataAttribute which uses first only).
11
+ *
12
+ * @param themes - Record mapping theme keys to class names (used to identify theme tokens)
13
+ * @param existing - Current class attribute value string (accepts null)
14
+ * @param entry - Theme entry to stringify, or null/undefined to clear theme (keeps non-theme classes)
15
+ * @returns Class attribute value string
16
+ */
17
+ export function stringifyClassName<Themes extends ThemeMap>(
18
+ themes: Themes,
19
+ existing: string | null | undefined,
20
+ entry: ThemeEntry<Themes> | null | undefined
21
+ ): string {
22
+ const allThemeClasses = Object.values(themes).flatMap((v) => {
23
+ const resolved = resolveThemeMapValue(v)
24
+ return Array.isArray(resolved) ? [...resolved] : [resolved]
25
+ })
26
+ const existingClasses = (existing ?? '')?.trim() ? (existing ?? '').trim().split(/\s+/) : []
27
+ const withoutThemes = existingClasses.filter((c) => !allThemeClasses.includes(c))
28
+ const activeClasses =
29
+ entry != null
30
+ ? (() => {
31
+ const resolved = resolveThemeMapValue(entry.value)
32
+ return Array.isArray(resolved) ? [...resolved] : [resolved]
33
+ })()
34
+ : []
35
+ return [...withoutThemes, ...activeClasses].filter(Boolean).join(' ')
36
+ }
@@ -0,0 +1,39 @@
1
+ import { observeAttributes } from '../../attributes/observe-attribute.ts'
2
+ import type { ParseStoredTheme, ThemeEntry } from '../theme-entry.types.ts'
3
+ import type { ThemeMap } from '../theme-map.types.ts'
4
+ import { parseClassName } from './parse-class-name.ts'
5
+
6
+ /**
7
+ * Subscribes to changes on the class attribute and invokes the handler with parsed theme entries.
8
+ *
9
+ * @param themes - Record mapping theme keys to class name(s)
10
+ * @param handler - Callback invoked when the class attribute changes
11
+ * @param options.element - Element to observe (accepts null e.g. from refs). Defaults to document.documentElement.
12
+ * @param options.parse - Custom parser (default: parseClassName)
13
+ * @returns Unsubscribe function. Returns a no-op function when element is not available (e.g. SSR).
14
+ */
15
+ export function subscribeClassName<Themes extends ThemeMap>(
16
+ themes: Themes,
17
+ handler: (entry: ThemeEntry<Themes> | undefined) => void,
18
+ options?:
19
+ | { element?: Element | null | undefined; parse?: ParseStoredTheme<Themes> | undefined }
20
+ | undefined
21
+ ): () => void {
22
+ const element = options?.element ?? document?.documentElement
23
+ if (!element) return () => {}
24
+ const parse = options?.parse ?? parseClassName
25
+ let lastEmitted: keyof Themes | undefined | null = null
26
+ const observer = observeAttributes(
27
+ {
28
+ class: (value) => {
29
+ const entry = parse(themes, value ?? undefined)
30
+ const key = entry?.theme ?? undefined
31
+ if (lastEmitted === key) return
32
+ lastEmitted = key
33
+ handler(entry)
34
+ }
35
+ },
36
+ element
37
+ )
38
+ return () => observer.disconnect()
39
+ }
@@ -0,0 +1,24 @@
1
+ import type { StringifyStoredTheme, ThemeEntry } from '../theme-entry.types.ts'
2
+ import type { ThemeMap } from '../theme-map.types.ts'
3
+ import { stringifyClassName } from './stringify-class-name.ts'
4
+
5
+ /**
6
+ * Writes a theme entry to the class attribute on an element.
7
+ *
8
+ * @param themes - Record mapping theme keys to class name(s)
9
+ * @param entry - Theme entry to write, or null/undefined to remove the theme
10
+ * @param options.element - Element to write to (accepts null e.g. from refs). Defaults to document.documentElement.
11
+ * @param options.stringify - Custom serializer (default: stringifyClassName)
12
+ */
13
+ export function writeClassName<Themes extends ThemeMap>(
14
+ themes: Themes,
15
+ entry: ThemeEntry<Themes> | null | undefined,
16
+ options?:
17
+ | { element?: Element | null | undefined; stringify?: StringifyStoredTheme<Themes> | undefined }
18
+ | undefined
19
+ ): void {
20
+ const element = options?.element ?? document?.documentElement
21
+ if (!element) return
22
+ const stringify = options?.stringify ?? stringifyClassName
23
+ element.className = stringify(themes, element.className ?? undefined, entry)
24
+ }
@@ -74,7 +74,7 @@ export function composeThemeStores<
74
74
  (s): s is StoreWithSubscribe<Themes> => typeof s.subscribe === 'function'
75
75
  )
76
76
 
77
- function subscribe(handler: (theme: ThemeEntry<Themes> | undefined | null) => void): () => void {
77
+ function subscribe(handler: (theme: ThemeEntry<Themes> | undefined) => void): () => void {
78
78
  let scheduled = false
79
79
  let lastEmitted: keyof Themes | undefined
80
80
 
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Internal cookie utilities for theme cookie storage.
3
+ * Cookie Store API has limited support; document.cookie is standard for theme persistence.
4
+ */
5
+
6
+ export function getCookieValue(name: string): string | null {
7
+ if (typeof document === 'undefined' || !document.cookie) return null
8
+ const match = document.cookie.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`))
9
+ const value = match?.[1]
10
+ return value !== undefined ? decodeURIComponent(value) : null
11
+ }
12
+
13
+ export function setCookie(
14
+ name: string,
15
+ value: string,
16
+ options: {
17
+ path?: string | undefined
18
+ maxAge?: number | undefined
19
+ sameSite?: 'lax' | 'strict' | 'none' | undefined
20
+ secure?: boolean | undefined
21
+ }
22
+ ) {
23
+ const parts = [`${name}=${encodeURIComponent(value)}`]
24
+ parts.push(`path=${options.path ?? '/'}`)
25
+ if (options.maxAge !== undefined) parts.push(`max-age=${options.maxAge}`)
26
+ if (options.sameSite !== undefined) parts.push(`samesite=${options.sameSite}`)
27
+ if (options.secure) parts.push('secure')
28
+ // biome-ignore lint/suspicious/noDocumentCookie: Cookie Store API has limited support; document.cookie is standard for theme persistence
29
+ document.cookie = parts.join('; ')
30
+ }
31
+
32
+ export function deleteCookie(name: string, path = '/') {
33
+ // biome-ignore lint/suspicious/noDocumentCookie: Cookie Store API has limited support; document.cookie is standard for theme persistence
34
+ document.cookie = `${name}=; path=${path}; max-age=0`
35
+ }
36
+
37
+ /**
38
+ * Parses a cookie value from a raw Cookie header string.
39
+ * Used for SSR when reading from request headers or framework cookie APIs.
40
+ */
41
+ export function getCookieFromHeader(cookieHeader: string, name: string): string | null {
42
+ const match = cookieHeader.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`))
43
+ const value = match?.[1]
44
+ return value !== undefined ? decodeURIComponent(value.trim()) : null
45
+ }
@@ -0,0 +1,33 @@
1
+ import { parseStoredTheme } from '../_utils/parse-stored-theme.ts'
2
+ import type { ParseStoredTheme, ThemeEntry } from '../theme-entry.types.ts'
3
+ import type { ThemeMap } from '../theme-map.types.ts'
4
+ import { getCookieValue } from './_cookie-utils.ts'
5
+
6
+ export interface ReadCookieThemeOptions<Themes extends ThemeMap = ThemeMap> {
7
+ cookieName: string
8
+ path?: string | undefined
9
+ parse?: ParseStoredTheme<Themes> | undefined
10
+ }
11
+
12
+ /**
13
+ * Reads a theme entry from a cookie.
14
+ *
15
+ * @param themes - Record mapping theme keys to values (for validation)
16
+ * @param options.cookieName - Cookie name to read from
17
+ * @param options.path - Cookie path (default: '/')
18
+ * @param options.parse - Custom parser (default: parseStoredTheme)
19
+ * @returns ThemeEntry if found, undefined otherwise. Returns undefined when document.cookie is unavailable (e.g. SSR).
20
+ */
21
+ export function readCookieTheme<Themes extends ThemeMap>(
22
+ themes: Themes,
23
+ options: ReadCookieThemeOptions<Themes>
24
+ ): ThemeEntry<Themes> | undefined {
25
+ const { cookieName, parse = parseStoredTheme } = options
26
+
27
+ if (typeof document === 'undefined' || document.cookie === undefined) {
28
+ return undefined
29
+ }
30
+
31
+ const stored = getCookieValue(cookieName)
32
+ return parse(themes, stored ?? undefined)
33
+ }
@@ -0,0 +1,48 @@
1
+ import type { ThemeEntry } from '../theme-entry.types.ts'
2
+ import type { ThemeMap } from '../theme-map.types.ts'
3
+ import { deleteCookie, setCookie } from './_cookie-utils.ts'
4
+
5
+ export interface WriteCookieThemeOptions<_Themes extends ThemeMap = ThemeMap> {
6
+ cookieName: string
7
+ path?: string | undefined
8
+ maxAge?: number | undefined
9
+ sameSite?: 'lax' | 'strict' | 'none' | undefined
10
+ secure?: boolean | undefined
11
+ }
12
+
13
+ /**
14
+ * Writes a theme entry to a cookie.
15
+ *
16
+ * Performs cookie set/delete only. Does not notify subscribers; the store must call notify() after this.
17
+ *
18
+ * @param themes - Record mapping theme keys to values (used for type validation)
19
+ * @param entry - Theme entry to write, or null/undefined to remove
20
+ * @param options - Cookie options
21
+ */
22
+ export function writeCookieTheme<Themes extends ThemeMap>(
23
+ _themes: Themes,
24
+ entry: ThemeEntry<Themes> | null | undefined,
25
+ options: WriteCookieThemeOptions<Themes>
26
+ ): void {
27
+ const { cookieName, path = '/', maxAge, sameSite, secure } = options
28
+
29
+ if (typeof document === 'undefined' || document.cookie === undefined) {
30
+ return
31
+ }
32
+
33
+ if (entry == null) {
34
+ deleteCookie(cookieName, path)
35
+ return
36
+ }
37
+
38
+ const opts: {
39
+ path: string
40
+ maxAge?: number
41
+ sameSite?: 'lax' | 'strict' | 'none'
42
+ secure?: boolean
43
+ } = { path }
44
+ if (maxAge !== undefined) opts.maxAge = maxAge
45
+ if (sameSite !== undefined) opts.sameSite = sameSite
46
+ if (secure) opts.secure = true
47
+ setCookie(cookieName, JSON.stringify(entry), opts)
48
+ }
@@ -0,0 +1 @@
1
+ export const SEPARATOR_SPACE = ' '
@@ -0,0 +1,25 @@
1
+ import { matchAttributeValueToTheme } from '../_utils/match-attribute-value-to-theme.ts'
2
+ import { themeEntry } from '../theme-entry.ts'
3
+ import type { ThemeEntry } from '../theme-entry.types.ts'
4
+ import type { ThemeMap } from '../theme-map.types.ts'
5
+ import { SEPARATOR_SPACE } from './_constant.ts'
6
+
7
+ /**
8
+ * Parses a data attribute value string into a ThemeEntry.
9
+ *
10
+ * Pure function: no DOM access. Uses first token when separator is defined.
11
+ *
12
+ * @param themes - Record mapping theme keys to attribute values
13
+ * @param value - Raw attribute value string (e.g. from getAttribute; accepts null)
14
+ * @param options.separator - When defined, split by separator and use first token (default: space)
15
+ * @returns ThemeEntry if a match is found, otherwise undefined
16
+ */
17
+ export function parseDataAttribute<Themes extends ThemeMap>(
18
+ themes: Themes,
19
+ value: string | null | undefined,
20
+ options?: { separator?: string | undefined } | undefined
21
+ ): ThemeEntry<Themes> | undefined {
22
+ const separator = options?.separator ?? SEPARATOR_SPACE
23
+ const theme = matchAttributeValueToTheme(themes, value ?? null, { separator })
24
+ return theme !== undefined ? themeEntry(themes, theme) : undefined
25
+ }
@@ -0,0 +1,29 @@
1
+ import { getDataAttribute } from '../../attributes/get-data-attribute.ts'
2
+ import type { ParseStoredTheme, ThemeEntry } from '../theme-entry.types.ts'
3
+ import type { ThemeMap } from '../theme-map.types.ts'
4
+ import { SEPARATOR_SPACE } from './_constant.ts'
5
+ import { parseDataAttribute } from './parse-data-attribute.ts'
6
+
7
+ /**
8
+ * Reads a theme entry from a data attribute on an element.
9
+ *
10
+ * @param themes - Record mapping theme keys to attribute values
11
+ * @param attributeName - Data attribute name (e.g. `data-theme`)
12
+ * @param options.element - Element to read from (accepts null e.g. from refs). Defaults to document.documentElement.
13
+ * @param options.parse - Custom parser (default: parseDataAttribute with space separator)
14
+ * @returns ThemeEntry if found, undefined otherwise. Returns undefined when element is not available (e.g. SSR).
15
+ */
16
+ export function readDataAttribute<Themes extends ThemeMap>(
17
+ themes: Themes,
18
+ attributeName: `data-${string}`,
19
+ options?:
20
+ | { element?: Element | null | undefined; parse?: ParseStoredTheme<Themes> | undefined }
21
+ | undefined
22
+ ): ThemeEntry<Themes> | undefined {
23
+ const element = options?.element ?? document?.documentElement
24
+ if (!element) return undefined
25
+ const parse =
26
+ options?.parse ?? ((t, v) => parseDataAttribute(t, v, { separator: SEPARATOR_SPACE }))
27
+ const raw = getDataAttribute(attributeName, element) ?? undefined
28
+ return parse(themes, raw)
29
+ }
@@ -0,0 +1,39 @@
1
+ import { resolveThemeMapValue } from '../_utils/resolve-theme-map-value.ts'
2
+ import type { ThemeEntry } from '../theme-entry.types.ts'
3
+ import type { ThemeMap } from '../theme-map.types.ts'
4
+ import { SEPARATOR_SPACE } from './_constant.ts'
5
+
6
+ /**
7
+ * Stringifies a ThemeEntry for a data attribute value.
8
+ *
9
+ * Pure function: no DOM access. Removes all theme values from existing, then adds entry's value(s).
10
+ * Aligns with stringifyClassName logic.
11
+ *
12
+ * @param themes - Record mapping theme keys to attribute values (used to identify theme tokens)
13
+ * @param existing - Current attribute value string (accepts null e.g. from getAttribute)
14
+ * @param entry - Theme entry to stringify, or null/undefined to clear theme (keeps non-theme tokens)
15
+ * @param options.separator - Token separator (default: space)
16
+ * @returns Attribute value string
17
+ */
18
+ export function stringifyDataAttribute<Themes extends ThemeMap>(
19
+ themes: Themes,
20
+ existing: string | null | undefined,
21
+ entry: ThemeEntry<Themes> | null | undefined,
22
+ options?: { separator?: string | undefined } | undefined
23
+ ): string {
24
+ const separator = options?.separator ?? SEPARATOR_SPACE
25
+ const allThemeValues = Object.values(themes).flatMap((v) => {
26
+ const resolved = resolveThemeMapValue(v)
27
+ return Array.isArray(resolved) ? [...resolved] : [resolved]
28
+ })
29
+ const existingTokens = (existing ?? '')?.trim() ? (existing ?? '').trim().split(separator) : []
30
+ const withoutThemeValues = existingTokens.filter((t) => !allThemeValues.includes(t.trim()))
31
+ const newTokens =
32
+ entry != null
33
+ ? (() => {
34
+ const resolved = resolveThemeMapValue(entry.value)
35
+ return Array.isArray(resolved) ? [resolved[0]] : [resolved]
36
+ })()
37
+ : []
38
+ return [...withoutThemeValues, ...newTokens].filter(Boolean).join(separator)
39
+ }
@@ -0,0 +1,39 @@
1
+ import { observeDataAttributes } from '../../attributes/observe-data-attribute.ts'
2
+ import type { ParseStoredTheme, ThemeEntry } from '../theme-entry.types.ts'
3
+ import type { ThemeMap } from '../theme-map.types.ts'
4
+ import { SEPARATOR_SPACE } from './_constant.ts'
5
+ import { parseDataAttribute } from './parse-data-attribute.ts'
6
+
7
+ /**
8
+ * Subscribes to changes on a data attribute and invokes the handler with parsed theme entries.
9
+ *
10
+ * @param themes - Record mapping theme keys to attribute values
11
+ * @param attributeName - Data attribute name (e.g. `data-theme`)
12
+ * @param handler - Callback invoked when the attribute changes
13
+ * @param options.element - Element to observe (accepts null e.g. from refs). Defaults to document.documentElement.
14
+ * @param options.parse - Custom parser (default: parseDataAttribute with space separator)
15
+ * @returns Unsubscribe function. Returns a no-op function when element is not available (e.g. SSR).
16
+ */
17
+ export function subscribeDataAttribute<Themes extends ThemeMap>(
18
+ themes: Themes,
19
+ attributeName: `data-${string}`,
20
+ handler: (entry: ThemeEntry<Themes> | undefined) => void,
21
+ options?:
22
+ | { element?: Element | null | undefined; parse?: ParseStoredTheme<Themes> | undefined }
23
+ | undefined
24
+ ): () => void {
25
+ const element = options?.element ?? document?.documentElement
26
+ if (!element) return () => {}
27
+ const parse =
28
+ options?.parse ?? ((t, v) => parseDataAttribute(t, v, { separator: SEPARATOR_SPACE }))
29
+ const observer = observeDataAttributes<string, `data-${string}`>(
30
+ {
31
+ [attributeName]: (value) => {
32
+ const entry = parse(themes, value ?? undefined)
33
+ handler(entry)
34
+ }
35
+ },
36
+ element
37
+ )
38
+ return () => observer.disconnect()
39
+ }
@@ -0,0 +1,40 @@
1
+ import { getDataAttribute } from '../../attributes/get-data-attribute.ts'
2
+ import type { StringifyStoredTheme, ThemeEntry } from '../theme-entry.types.ts'
3
+ import type { ThemeMap } from '../theme-map.types.ts'
4
+ import { SEPARATOR_SPACE } from './_constant.ts'
5
+ import { stringifyDataAttribute } from './stringify-data-attribute.ts'
6
+
7
+ /**
8
+ * Writes a theme entry to a data attribute on an element.
9
+ *
10
+ * @param themes - Record mapping theme keys to attribute values
11
+ * @param attributeName - Data attribute name (e.g. `data-theme`)
12
+ * @param entry - Theme entry to write, or null/undefined to remove the theme
13
+ * @param options.element - Element to write to (accepts null e.g. from refs). Defaults to document.documentElement.
14
+ * @param options.stringify - Custom serializer (default: stringifyDataAttribute with space separator)
15
+ */
16
+ export function writeDataAttribute<Themes extends ThemeMap>(
17
+ themes: Themes,
18
+ attributeName: `data-${string}`,
19
+ entry: ThemeEntry<Themes> | null | undefined,
20
+ options?:
21
+ | { element?: Element | null | undefined; stringify?: StringifyStoredTheme<Themes> | undefined }
22
+ | undefined
23
+ ): void {
24
+ const element = options?.element ?? document?.documentElement
25
+ if (!element) return
26
+ const stringify =
27
+ options?.stringify ??
28
+ ((t, x, e) => stringifyDataAttribute(t, x, e, { separator: SEPARATOR_SPACE }))
29
+ if (entry == null) {
30
+ element.removeAttribute(attributeName)
31
+ return
32
+ }
33
+ const existing = getDataAttribute(attributeName, element) ?? undefined
34
+ const result = stringify(themes, existing, entry)
35
+ if (result === '') {
36
+ element.removeAttribute(attributeName)
37
+ } else {
38
+ element.setAttribute(attributeName, result)
39
+ }
40
+ }
@@ -0,0 +1,23 @@
1
+ import type { ParseStoredTheme, ThemeEntry } from '../theme-entry.types.ts'
2
+ import type { ThemeMap } from '../theme-map.types.ts'
3
+ import { readWebStorage } from '../web-storage/read-web-storage.ts'
4
+
5
+ /**
6
+ * Reads a theme entry from localStorage.
7
+ *
8
+ * @param themes - Record mapping theme keys to values (for validation)
9
+ * @param storageKey - localStorage key to read from
10
+ * @param options.parse - Custom parser (default: parseStoredTheme)
11
+ * @returns ThemeEntry if found, undefined otherwise. Returns undefined when localStorage is unavailable (e.g. SSR).
12
+ */
13
+ export function readLocalStorage<Themes extends ThemeMap>(
14
+ themes: Themes,
15
+ storageKey: string,
16
+ options?: { parse?: ParseStoredTheme<Themes> | undefined }
17
+ ): ThemeEntry<Themes> | undefined {
18
+ if (!window?.localStorage) return undefined
19
+ return readWebStorage(themes, storageKey, {
20
+ storage: window.localStorage,
21
+ parse: options?.parse
22
+ })
23
+ }