@just-web/toolkits 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (449) hide show
  1. package/dist/_internal/utils/try-parse-json.cjs +14 -0
  2. package/dist/_internal/utils/try-parse-json.cjs.map +1 -0
  3. package/dist/_internal/utils/try-parse-json.mjs +13 -0
  4. package/dist/_internal/utils/try-parse-json.mjs.map +1 -0
  5. package/dist/_virtual/rolldown_runtime.cjs +29 -0
  6. package/dist/attributes/data-attribute.d.cts +17 -0
  7. package/dist/attributes/data-attribute.d.cts.map +1 -0
  8. package/dist/attributes/data-attribute.d.mts +17 -0
  9. package/dist/attributes/data-attribute.d.mts.map +1 -0
  10. package/dist/attributes/get-attribute.cjs +26 -0
  11. package/dist/attributes/get-attribute.cjs.map +1 -0
  12. package/dist/attributes/get-attribute.d.cts +21 -0
  13. package/dist/attributes/get-attribute.d.cts.map +1 -0
  14. package/dist/attributes/get-attribute.d.mts +21 -0
  15. package/dist/attributes/get-attribute.d.mts.map +1 -0
  16. package/dist/attributes/get-attribute.mjs +25 -0
  17. package/dist/attributes/get-attribute.mjs.map +1 -0
  18. package/dist/attributes/get-data-attribute.cjs +19 -0
  19. package/dist/attributes/get-data-attribute.cjs.map +1 -0
  20. package/dist/attributes/get-data-attribute.d.cts +17 -0
  21. package/dist/attributes/get-data-attribute.d.cts.map +1 -0
  22. package/dist/attributes/get-data-attribute.d.mts +17 -0
  23. package/dist/attributes/get-data-attribute.d.mts.map +1 -0
  24. package/dist/attributes/get-data-attribute.mjs +19 -0
  25. package/dist/attributes/get-data-attribute.mjs.map +1 -0
  26. package/dist/attributes/observe-attribute.cjs +40 -0
  27. package/dist/attributes/observe-attribute.cjs.map +1 -0
  28. package/dist/attributes/observe-attribute.d.cts +23 -0
  29. package/dist/attributes/observe-attribute.d.cts.map +1 -0
  30. package/dist/attributes/observe-attribute.d.mts +23 -0
  31. package/dist/attributes/observe-attribute.d.mts.map +1 -0
  32. package/dist/attributes/observe-attribute.mjs +39 -0
  33. package/dist/attributes/observe-attribute.mjs.map +1 -0
  34. package/dist/attributes/observe-data-attribute.cjs +31 -0
  35. package/dist/attributes/observe-data-attribute.cjs.map +1 -0
  36. package/dist/attributes/observe-data-attribute.d.cts +26 -0
  37. package/dist/attributes/observe-data-attribute.d.cts.map +1 -0
  38. package/dist/attributes/observe-data-attribute.d.mts +26 -0
  39. package/dist/attributes/observe-data-attribute.d.mts.map +1 -0
  40. package/dist/attributes/observe-data-attribute.mjs +31 -0
  41. package/dist/attributes/observe-data-attribute.mjs.map +1 -0
  42. package/dist/children/just-children.d.cts +37 -0
  43. package/dist/children/just-children.d.cts.map +1 -0
  44. package/dist/children/just-children.d.mts +37 -0
  45. package/dist/children/just-children.d.mts.map +1 -0
  46. package/dist/children/resolve-children.cjs +11 -0
  47. package/dist/children/resolve-children.cjs.map +1 -0
  48. package/dist/children/resolve-children.d.cts +9 -0
  49. package/dist/children/resolve-children.d.cts.map +1 -0
  50. package/dist/children/resolve-children.d.mts +9 -0
  51. package/dist/children/resolve-children.d.mts.map +1 -0
  52. package/dist/children/resolve-children.mjs +10 -0
  53. package/dist/children/resolve-children.mjs.map +1 -0
  54. package/dist/class-name/class-name-props.d.cts +11 -0
  55. package/dist/class-name/class-name-props.d.cts.map +1 -0
  56. package/dist/class-name/class-name-props.d.mts +11 -0
  57. package/dist/class-name/class-name-props.d.mts.map +1 -0
  58. package/dist/class-name/clsx.cjs +3 -0
  59. package/dist/class-name/clsx.d.cts +2 -0
  60. package/dist/class-name/clsx.d.mts +2 -0
  61. package/dist/class-name/clsx.mjs +3 -0
  62. package/dist/class-name/just-class-name.d.cts +36 -0
  63. package/dist/class-name/just-class-name.d.cts.map +1 -0
  64. package/dist/class-name/just-class-name.d.mts +36 -0
  65. package/dist/class-name/just-class-name.d.mts.map +1 -0
  66. package/dist/class-name/resolve-class-name.cjs +12 -0
  67. package/dist/class-name/resolve-class-name.cjs.map +1 -0
  68. package/dist/class-name/resolve-class-name.d.cts +8 -0
  69. package/dist/class-name/resolve-class-name.d.cts.map +1 -0
  70. package/dist/class-name/resolve-class-name.d.mts +8 -0
  71. package/dist/class-name/resolve-class-name.d.mts.map +1 -0
  72. package/dist/class-name/resolve-class-name.mjs +10 -0
  73. package/dist/class-name/resolve-class-name.mjs.map +1 -0
  74. package/dist/color-scheme/get-prefers-color-scheme.cjs +21 -0
  75. package/dist/color-scheme/get-prefers-color-scheme.cjs.map +1 -0
  76. package/dist/color-scheme/get-prefers-color-scheme.d.cts +16 -0
  77. package/dist/color-scheme/get-prefers-color-scheme.d.cts.map +1 -0
  78. package/dist/color-scheme/get-prefers-color-scheme.d.mts +16 -0
  79. package/dist/color-scheme/get-prefers-color-scheme.d.mts.map +1 -0
  80. package/dist/color-scheme/get-prefers-color-scheme.mjs +20 -0
  81. package/dist/color-scheme/get-prefers-color-scheme.mjs.map +1 -0
  82. package/dist/color-scheme/observe-prefers-color-scheme.cjs +29 -0
  83. package/dist/color-scheme/observe-prefers-color-scheme.cjs.map +1 -0
  84. package/dist/color-scheme/observe-prefers-color-scheme.d.cts +20 -0
  85. package/dist/color-scheme/observe-prefers-color-scheme.d.cts.map +1 -0
  86. package/dist/color-scheme/observe-prefers-color-scheme.d.mts +20 -0
  87. package/dist/color-scheme/observe-prefers-color-scheme.d.mts.map +1 -0
  88. package/dist/color-scheme/observe-prefers-color-scheme.mjs +28 -0
  89. package/dist/color-scheme/observe-prefers-color-scheme.mjs.map +1 -0
  90. package/dist/index.cjs +39 -0
  91. package/dist/index.d.cts +26 -0
  92. package/dist/index.d.mts +26 -0
  93. package/dist/index.mjs +20 -0
  94. package/dist/react/hooks/use-attribute.cjs +41 -0
  95. package/dist/react/hooks/use-attribute.cjs.map +1 -0
  96. package/dist/react/hooks/use-attribute.d.cts +21 -0
  97. package/dist/react/hooks/use-attribute.d.cts.map +1 -0
  98. package/dist/react/hooks/use-attribute.d.mts +21 -0
  99. package/dist/react/hooks/use-attribute.d.mts.map +1 -0
  100. package/dist/react/hooks/use-attribute.mjs +40 -0
  101. package/dist/react/hooks/use-attribute.mjs.map +1 -0
  102. package/dist/react/hooks/use-prefers-color-scheme.cjs +42 -0
  103. package/dist/react/hooks/use-prefers-color-scheme.cjs.map +1 -0
  104. package/dist/react/hooks/use-prefers-color-scheme.d.cts +29 -0
  105. package/dist/react/hooks/use-prefers-color-scheme.d.cts.map +1 -0
  106. package/dist/react/hooks/use-prefers-color-scheme.d.mts +29 -0
  107. package/dist/react/hooks/use-prefers-color-scheme.d.mts.map +1 -0
  108. package/dist/react/hooks/use-prefers-color-scheme.mjs +41 -0
  109. package/dist/react/hooks/use-prefers-color-scheme.mjs.map +1 -0
  110. package/dist/react/hooks/use-theme-by-class-name.cjs +60 -0
  111. package/dist/react/hooks/use-theme-by-class-name.cjs.map +1 -0
  112. package/dist/react/hooks/use-theme-by-class-name.d.cts +34 -0
  113. package/dist/react/hooks/use-theme-by-class-name.d.cts.map +1 -0
  114. package/dist/react/hooks/use-theme-by-class-name.d.mts +34 -0
  115. package/dist/react/hooks/use-theme-by-class-name.d.mts.map +1 -0
  116. package/dist/react/hooks/use-theme-by-class-name.mjs +59 -0
  117. package/dist/react/hooks/use-theme-by-class-name.mjs.map +1 -0
  118. package/dist/react/hooks/use-theme-by-data-attribute.cjs +73 -0
  119. package/dist/react/hooks/use-theme-by-data-attribute.cjs.map +1 -0
  120. package/dist/react/hooks/use-theme-by-data-attribute.d.cts +39 -0
  121. package/dist/react/hooks/use-theme-by-data-attribute.d.cts.map +1 -0
  122. package/dist/react/hooks/use-theme-by-data-attribute.d.mts +39 -0
  123. package/dist/react/hooks/use-theme-by-data-attribute.d.mts.map +1 -0
  124. package/dist/react/hooks/use-theme-by-data-attribute.mjs +72 -0
  125. package/dist/react/hooks/use-theme-by-data-attribute.mjs.map +1 -0
  126. package/dist/react/hooks/use-theme-by-local-storage.cjs +53 -0
  127. package/dist/react/hooks/use-theme-by-local-storage.cjs.map +1 -0
  128. package/dist/react/hooks/use-theme-by-local-storage.d.cts +37 -0
  129. package/dist/react/hooks/use-theme-by-local-storage.d.cts.map +1 -0
  130. package/dist/react/hooks/use-theme-by-local-storage.d.mts +37 -0
  131. package/dist/react/hooks/use-theme-by-local-storage.d.mts.map +1 -0
  132. package/dist/react/hooks/use-theme-by-local-storage.mjs +52 -0
  133. package/dist/react/hooks/use-theme-by-local-storage.mjs.map +1 -0
  134. package/dist/react/hooks/use-theme-stores.cjs +40 -0
  135. package/dist/react/hooks/use-theme-stores.cjs.map +1 -0
  136. package/dist/react/hooks/use-theme-stores.d.cts +38 -0
  137. package/dist/react/hooks/use-theme-stores.d.cts.map +1 -0
  138. package/dist/react/hooks/use-theme-stores.d.mts +38 -0
  139. package/dist/react/hooks/use-theme-stores.d.mts.map +1 -0
  140. package/dist/react/hooks/use-theme-stores.mjs +39 -0
  141. package/dist/react/hooks/use-theme-stores.mjs.map +1 -0
  142. package/dist/react/theme/create-theme-hook.cjs +105 -0
  143. package/dist/react/theme/create-theme-hook.cjs.map +1 -0
  144. package/dist/react/theme/create-theme-hook.d.cts +29 -0
  145. package/dist/react/theme/create-theme-hook.d.cts.map +1 -0
  146. package/dist/react/theme/create-theme-hook.d.mts +29 -0
  147. package/dist/react/theme/create-theme-hook.d.mts.map +1 -0
  148. package/dist/react/theme/create-theme-hook.mjs +104 -0
  149. package/dist/react/theme/create-theme-hook.mjs.map +1 -0
  150. package/dist/react.cjs +15 -0
  151. package/dist/react.d.cts +8 -0
  152. package/dist/react.d.mts +8 -0
  153. package/dist/react.mjs +9 -0
  154. package/dist/style/css-properties.d.cts +20 -0
  155. package/dist/style/css-properties.d.cts.map +1 -0
  156. package/dist/style/css-properties.d.mts +20 -0
  157. package/dist/style/css-properties.d.mts.map +1 -0
  158. package/dist/style/define-css-properties.cjs +25 -0
  159. package/dist/style/define-css-properties.cjs.map +1 -0
  160. package/dist/style/define-css-properties.d.cts +24 -0
  161. package/dist/style/define-css-properties.d.cts.map +1 -0
  162. package/dist/style/define-css-properties.d.mts +24 -0
  163. package/dist/style/define-css-properties.d.mts.map +1 -0
  164. package/dist/style/define-css-properties.mjs +24 -0
  165. package/dist/style/define-css-properties.mjs.map +1 -0
  166. package/dist/style/get-css-variable-value.cjs +11 -0
  167. package/dist/style/get-css-variable-value.cjs.map +1 -0
  168. package/dist/style/get-css-variable-value.d.cts +22 -0
  169. package/dist/style/get-css-variable-value.d.cts.map +1 -0
  170. package/dist/style/get-css-variable-value.d.mts +22 -0
  171. package/dist/style/get-css-variable-value.d.mts.map +1 -0
  172. package/dist/style/get-css-variable-value.mjs +10 -0
  173. package/dist/style/get-css-variable-value.mjs.map +1 -0
  174. package/dist/style/just-style.d.cts +44 -0
  175. package/dist/style/just-style.d.cts.map +1 -0
  176. package/dist/style/just-style.d.mts +44 -0
  177. package/dist/style/just-style.d.mts.map +1 -0
  178. package/dist/style/resolve-style.cjs +14 -0
  179. package/dist/style/resolve-style.cjs.map +1 -0
  180. package/dist/style/resolve-style.d.cts +11 -0
  181. package/dist/style/resolve-style.d.cts.map +1 -0
  182. package/dist/style/resolve-style.d.mts +11 -0
  183. package/dist/style/resolve-style.d.mts.map +1 -0
  184. package/dist/style/resolve-style.mjs +13 -0
  185. package/dist/style/resolve-style.mjs.map +1 -0
  186. package/dist/style/style-props.d.cts +13 -0
  187. package/dist/style/style-props.d.cts.map +1 -0
  188. package/dist/style/style-props.d.mts +13 -0
  189. package/dist/style/style-props.d.mts.map +1 -0
  190. package/dist/style/to-dom-style.cjs +33 -0
  191. package/dist/style/to-dom-style.cjs.map +1 -0
  192. package/dist/style/to-dom-style.d.cts +29 -0
  193. package/dist/style/to-dom-style.d.cts.map +1 -0
  194. package/dist/style/to-dom-style.d.mts +29 -0
  195. package/dist/style/to-dom-style.d.mts.map +1 -0
  196. package/dist/style/to-dom-style.mjs +32 -0
  197. package/dist/style/to-dom-style.mjs.map +1 -0
  198. package/dist/testing/theme/dummy-theme-store.cjs +11 -0
  199. package/dist/testing/theme/dummy-theme-store.cjs.map +1 -0
  200. package/dist/testing/theme/dummy-theme-store.mjs +10 -0
  201. package/dist/testing/theme/dummy-theme-store.mjs.map +1 -0
  202. package/dist/theme/_utils/get-theme-from-stores.cjs +24 -0
  203. package/dist/theme/_utils/get-theme-from-stores.cjs.map +1 -0
  204. package/dist/theme/_utils/get-theme-from-stores.mjs +23 -0
  205. package/dist/theme/_utils/get-theme-from-stores.mjs.map +1 -0
  206. package/dist/theme/_utils/observe-theme-from-stores.cjs +39 -0
  207. package/dist/theme/_utils/observe-theme-from-stores.cjs.map +1 -0
  208. package/dist/theme/_utils/observe-theme-from-stores.mjs +39 -0
  209. package/dist/theme/_utils/observe-theme-from-stores.mjs.map +1 -0
  210. package/dist/theme/_utils/parse-stored-theme.cjs +22 -0
  211. package/dist/theme/_utils/parse-stored-theme.cjs.map +1 -0
  212. package/dist/theme/_utils/parse-stored-theme.mjs +22 -0
  213. package/dist/theme/_utils/parse-stored-theme.mjs.map +1 -0
  214. package/dist/theme/_utils/set-theme-to-stores.cjs +16 -0
  215. package/dist/theme/_utils/set-theme-to-stores.cjs.map +1 -0
  216. package/dist/theme/_utils/set-theme-to-stores.mjs +15 -0
  217. package/dist/theme/_utils/set-theme-to-stores.mjs.map +1 -0
  218. package/dist/theme/class-name/apply-theme-to-class-name.cjs +23 -0
  219. package/dist/theme/class-name/apply-theme-to-class-name.cjs.map +1 -0
  220. package/dist/theme/class-name/apply-theme-to-class-name.mjs +22 -0
  221. package/dist/theme/class-name/apply-theme-to-class-name.mjs.map +1 -0
  222. package/dist/theme/class-name/resolve-theme-from-class-name.cjs +23 -0
  223. package/dist/theme/class-name/resolve-theme-from-class-name.cjs.map +1 -0
  224. package/dist/theme/class-name/resolve-theme-from-class-name.mjs +22 -0
  225. package/dist/theme/class-name/resolve-theme-from-class-name.mjs.map +1 -0
  226. package/dist/theme/compose-theme-stores.cjs +74 -0
  227. package/dist/theme/compose-theme-stores.cjs.map +1 -0
  228. package/dist/theme/compose-theme-stores.d.cts +33 -0
  229. package/dist/theme/compose-theme-stores.d.cts.map +1 -0
  230. package/dist/theme/compose-theme-stores.d.mts +33 -0
  231. package/dist/theme/compose-theme-stores.d.mts.map +1 -0
  232. package/dist/theme/compose-theme-stores.mjs +74 -0
  233. package/dist/theme/compose-theme-stores.mjs.map +1 -0
  234. package/dist/theme/data-attribute/apply-theme-to-data-attribute.cjs +23 -0
  235. package/dist/theme/data-attribute/apply-theme-to-data-attribute.cjs.map +1 -0
  236. package/dist/theme/data-attribute/apply-theme-to-data-attribute.mjs +22 -0
  237. package/dist/theme/data-attribute/apply-theme-to-data-attribute.mjs.map +1 -0
  238. package/dist/theme/data-attribute/resolve-theme-from-data-attribute.cjs +23 -0
  239. package/dist/theme/data-attribute/resolve-theme-from-data-attribute.cjs.map +1 -0
  240. package/dist/theme/data-attribute/resolve-theme-from-data-attribute.mjs +22 -0
  241. package/dist/theme/data-attribute/resolve-theme-from-data-attribute.mjs.map +1 -0
  242. package/dist/theme/theme-entry.cjs +13 -0
  243. package/dist/theme/theme-entry.cjs.map +1 -0
  244. package/dist/theme/theme-entry.d.cts +9 -0
  245. package/dist/theme/theme-entry.d.cts.map +1 -0
  246. package/dist/theme/theme-entry.d.mts +9 -0
  247. package/dist/theme/theme-entry.d.mts.map +1 -0
  248. package/dist/theme/theme-entry.mjs +12 -0
  249. package/dist/theme/theme-entry.mjs.map +1 -0
  250. package/dist/theme/theme-entry.types.d.cts +16 -0
  251. package/dist/theme/theme-entry.types.d.cts.map +1 -0
  252. package/dist/theme/theme-entry.types.d.mts +16 -0
  253. package/dist/theme/theme-entry.types.d.mts.map +1 -0
  254. package/dist/theme/theme-map.types.d.cts +10 -0
  255. package/dist/theme/theme-map.types.d.cts.map +1 -0
  256. package/dist/theme/theme-map.types.d.mts +10 -0
  257. package/dist/theme/theme-map.types.d.mts.map +1 -0
  258. package/dist/theme/theme-store/async-theme-store.types.d.cts +25 -0
  259. package/dist/theme/theme-store/async-theme-store.types.d.cts.map +1 -0
  260. package/dist/theme/theme-store/async-theme-store.types.d.mts +25 -0
  261. package/dist/theme/theme-store/async-theme-store.types.d.mts.map +1 -0
  262. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.cjs +53 -0
  263. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.cjs.map +1 -0
  264. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.d.cts +28 -0
  265. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.d.cts.map +1 -0
  266. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.d.mts +28 -0
  267. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.d.mts.map +1 -0
  268. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.mjs +53 -0
  269. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.mjs.map +1 -0
  270. package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.cjs +121 -0
  271. package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.cjs.map +1 -0
  272. package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.d.cts +65 -0
  273. package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.d.cts.map +1 -0
  274. package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.d.mts +65 -0
  275. package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.d.mts.map +1 -0
  276. package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.mjs +120 -0
  277. package/dist/theme/theme-store/cookie-theme-store/cookie-theme-store.mjs.map +1 -0
  278. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.cjs +51 -0
  279. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.cjs.map +1 -0
  280. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.d.cts +30 -0
  281. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.d.cts.map +1 -0
  282. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.d.mts +30 -0
  283. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.d.mts.map +1 -0
  284. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.mjs +51 -0
  285. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.mjs.map +1 -0
  286. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.cjs +54 -0
  287. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.cjs.map +1 -0
  288. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.d.cts +31 -0
  289. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.d.cts.map +1 -0
  290. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.d.mts +31 -0
  291. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.d.mts.map +1 -0
  292. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.mjs +54 -0
  293. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.mjs.map +1 -0
  294. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.cjs +67 -0
  295. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.cjs.map +1 -0
  296. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.d.cts +34 -0
  297. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.d.cts.map +1 -0
  298. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.d.mts +34 -0
  299. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.d.mts.map +1 -0
  300. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.mjs +67 -0
  301. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.mjs.map +1 -0
  302. package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.cjs +39 -0
  303. package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.cjs.map +1 -0
  304. package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.d.cts +32 -0
  305. package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.d.cts.map +1 -0
  306. package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.d.mts +32 -0
  307. package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.d.mts.map +1 -0
  308. package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.mjs +39 -0
  309. package/dist/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.mjs.map +1 -0
  310. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.cjs +67 -0
  311. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.cjs.map +1 -0
  312. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.d.cts +34 -0
  313. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.d.cts.map +1 -0
  314. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.d.mts +34 -0
  315. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.d.mts.map +1 -0
  316. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.mjs +67 -0
  317. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.mjs.map +1 -0
  318. package/dist/theme/theme-store/theme-store-factory.types.d.cts +10 -0
  319. package/dist/theme/theme-store/theme-store-factory.types.d.cts.map +1 -0
  320. package/dist/theme/theme-store/theme-store-factory.types.d.mts +10 -0
  321. package/dist/theme/theme-store/theme-store-factory.types.d.mts.map +1 -0
  322. package/dist/theme/theme-store/theme-store.types.d.cts +33 -0
  323. package/dist/theme/theme-store/theme-store.types.d.cts.map +1 -0
  324. package/dist/theme/theme-store/theme-store.types.d.mts +33 -0
  325. package/dist/theme/theme-store/theme-store.types.d.mts.map +1 -0
  326. package/dist/theme.cjs +20 -0
  327. package/dist/theme.d.cts +15 -0
  328. package/dist/theme.d.mts +15 -0
  329. package/dist/theme.mjs +11 -0
  330. package/dist/units/get-rem-to-px-scale.cjs +30 -0
  331. package/dist/units/get-rem-to-px-scale.cjs.map +1 -0
  332. package/dist/units/get-rem-to-px-scale.d.cts +21 -0
  333. package/dist/units/get-rem-to-px-scale.d.cts.map +1 -0
  334. package/dist/units/get-rem-to-px-scale.d.mts +21 -0
  335. package/dist/units/get-rem-to-px-scale.d.mts.map +1 -0
  336. package/dist/units/get-rem-to-px-scale.mjs +29 -0
  337. package/dist/units/get-rem-to-px-scale.mjs.map +1 -0
  338. package/dist/units/px-2-num.cjs +23 -0
  339. package/dist/units/px-2-num.cjs.map +1 -0
  340. package/dist/units/px-2-num.d.cts +19 -0
  341. package/dist/units/px-2-num.d.cts.map +1 -0
  342. package/dist/units/px-2-num.d.mts +19 -0
  343. package/dist/units/px-2-num.d.mts.map +1 -0
  344. package/dist/units/px-2-num.mjs +22 -0
  345. package/dist/units/px-2-num.mjs.map +1 -0
  346. package/dist/units/px-2-rem.cjs +31 -0
  347. package/dist/units/px-2-rem.cjs.map +1 -0
  348. package/dist/units/px-2-rem.d.cts +25 -0
  349. package/dist/units/px-2-rem.d.cts.map +1 -0
  350. package/dist/units/px-2-rem.d.mts +25 -0
  351. package/dist/units/px-2-rem.d.mts.map +1 -0
  352. package/dist/units/px-2-rem.mjs +30 -0
  353. package/dist/units/px-2-rem.mjs.map +1 -0
  354. package/dist/units/rem-2-px.cjs +31 -0
  355. package/dist/units/rem-2-px.cjs.map +1 -0
  356. package/dist/units/rem-2-px.d.cts +25 -0
  357. package/dist/units/rem-2-px.d.cts.map +1 -0
  358. package/dist/units/rem-2-px.d.mts +25 -0
  359. package/dist/units/rem-2-px.d.mts.map +1 -0
  360. package/dist/units/rem-2-px.mjs +30 -0
  361. package/dist/units/rem-2-px.mjs.map +1 -0
  362. package/dist/utils/append-id.cjs +16 -0
  363. package/dist/utils/append-id.cjs.map +1 -0
  364. package/dist/utils/append-id.d.cts +12 -0
  365. package/dist/utils/append-id.d.cts.map +1 -0
  366. package/dist/utils/append-id.d.mts +12 -0
  367. package/dist/utils/append-id.d.mts.map +1 -0
  368. package/dist/utils/append-id.mjs +15 -0
  369. package/dist/utils/append-id.mjs.map +1 -0
  370. package/package.json +120 -0
  371. package/readme.md +15 -0
  372. package/src/_internal/utils/try-parse-json.ts +8 -0
  373. package/src/attributes/data-attribute.ts +49 -0
  374. package/src/attributes/get-attribute.ts +20 -0
  375. package/src/attributes/get-data-attribute.ts +15 -0
  376. package/src/attributes/observe-attribute.ts +37 -0
  377. package/src/attributes/observe-data-attribute.ts +29 -0
  378. package/src/children/just-children-fn-props.editor.default.tsx +29 -0
  379. package/src/children/just-children-props.editor.default.tsx +17 -0
  380. package/src/children/just-children.editor.default.tsx +11 -0
  381. package/src/children/just-children.ts +37 -0
  382. package/src/children/resolve-children.ts +16 -0
  383. package/src/class-name/class-name-props.editor.tsx +13 -0
  384. package/src/class-name/class-name-props.ts +7 -0
  385. package/src/class-name/clsx.ts +3 -0
  386. package/src/class-name/just-class-name-props.editor.default.tsx +23 -0
  387. package/src/class-name/just-class-name-resolver-state.editor.default.tsx +18 -0
  388. package/src/class-name/just-class-name.editor.default-class-name.tsx +28 -0
  389. package/src/class-name/just-class-name.editor.default.tsx +14 -0
  390. package/src/class-name/just-class-name.editor.type-param.tsx +25 -0
  391. package/src/class-name/just-class-name.ts +36 -0
  392. package/src/class-name/resolve-class-name.ts +12 -0
  393. package/src/color-scheme/get-prefers-color-scheme.ts +17 -0
  394. package/src/color-scheme/observe-prefers-color-scheme.ts +24 -0
  395. package/src/index.ts +25 -0
  396. package/src/react/hooks/use-attribute.ts +59 -0
  397. package/src/react/hooks/use-prefers-color-scheme.ts +42 -0
  398. package/src/react/hooks/use-theme-by-class-name.ts +69 -0
  399. package/src/react/hooks/use-theme-by-data-attribute.ts +84 -0
  400. package/src/react/hooks/use-theme-by-local-storage.ts +68 -0
  401. package/src/react/hooks/use-theme-stores.ts +83 -0
  402. package/src/react/theme/create-theme-hook.ts +197 -0
  403. package/src/react.ts +7 -0
  404. package/src/style/css-properties.ts +20 -0
  405. package/src/style/define-css-properties.ts +23 -0
  406. package/src/style/get-css-variable-value.ts +32 -0
  407. package/src/style/just-style-props.editor.default.tsx +17 -0
  408. package/src/style/just-style-resolver-state.editor.default.tsx +22 -0
  409. package/src/style/just-style.editor.default.tsx +17 -0
  410. package/src/style/just-style.editor.type-param.tsx +31 -0
  411. package/src/style/just-style.ts +60 -0
  412. package/src/style/resolve-style.ts +23 -0
  413. package/src/style/style-props.editor.tsx +13 -0
  414. package/src/style/style-props.ts +8 -0
  415. package/src/style/to-dom-style.ts +36 -0
  416. package/src/testing/button.theme.ts +21 -0
  417. package/src/testing/button.tsx +11 -0
  418. package/src/testing/log-panel.tsx +14 -0
  419. package/src/testing/theme/dummy-theme-store.ts +7 -0
  420. package/src/testing/theme/theme-result-card.tsx +43 -0
  421. package/src/testing/theme/theme-store-demo.tsx +87 -0
  422. package/src/theme/_utils/get-theme-from-stores.ts +34 -0
  423. package/src/theme/_utils/observe-theme-from-stores.ts +57 -0
  424. package/src/theme/_utils/parse-stored-theme.ts +21 -0
  425. package/src/theme/_utils/set-theme-to-stores.ts +23 -0
  426. package/src/theme/class-name/apply-theme-to-class-name.ts +26 -0
  427. package/src/theme/class-name/resolve-theme-from-class-name.ts +22 -0
  428. package/src/theme/compose-theme-stores.ts +139 -0
  429. package/src/theme/data-attribute/apply-theme-to-data-attribute.ts +27 -0
  430. package/src/theme/data-attribute/resolve-theme-from-data-attribute.ts +23 -0
  431. package/src/theme/theme-entry.ts +10 -0
  432. package/src/theme/theme-entry.types.ts +11 -0
  433. package/src/theme/theme-map.types.ts +6 -0
  434. package/src/theme/theme-store/async-theme-store.types.ts +24 -0
  435. package/src/theme/theme-store/class-name-theme-store/class-name-theme-store.ts +62 -0
  436. package/src/theme/theme-store/cookie-theme-store/cookie-theme-store.ts +174 -0
  437. package/src/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.ts +60 -0
  438. package/src/theme/theme-store/in-memory-theme-store/in-memory-theme-store.ts +54 -0
  439. package/src/theme/theme-store/local-storage-theme-store/local-storage-theme-store.ts +83 -0
  440. package/src/theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.ts +43 -0
  441. package/src/theme/theme-store/session-storage-theme-store/session-storage-theme-store.ts +83 -0
  442. package/src/theme/theme-store/theme-store-factory.types.ts +9 -0
  443. package/src/theme/theme-store/theme-store.types.ts +30 -0
  444. package/src/theme.ts +14 -0
  445. package/src/units/get-rem-to-px-scale.ts +27 -0
  446. package/src/units/px-2-num.ts +17 -0
  447. package/src/units/px-2-rem.ts +30 -0
  448. package/src/units/rem-2-px.ts +30 -0
  449. package/src/utils/append-id.ts +10 -0
@@ -0,0 +1,174 @@
1
+ import type { Required } from 'type-plus'
2
+ import { dummyThemeStore } from '../../../testing/theme/dummy-theme-store.ts'
3
+ import { parseStoredTheme } from '../../_utils/parse-stored-theme.ts'
4
+ import { themeEntry } from '../../theme-entry.ts'
5
+ import type { ThemeEntry } from '../../theme-entry.types.ts'
6
+ import type { ThemeMap } from '../../theme-map.types.ts'
7
+ import type { ThemeStore } from '../theme-store.types.ts'
8
+
9
+ export interface CookieThemeStoreOptions {
10
+ cookieName: string
11
+ path?: string | undefined
12
+ maxAge?: number | undefined
13
+ sameSite?: 'lax' | 'strict' | 'none' | undefined
14
+ secure?: boolean | undefined
15
+ }
16
+
17
+ function getCookieValue(name: string): string | null {
18
+ if (typeof document === 'undefined' || !document.cookie) return null
19
+ const match = document.cookie.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`))
20
+ const value = match?.[1]
21
+ return value !== undefined ? decodeURIComponent(value) : null
22
+ }
23
+
24
+ function setCookie(
25
+ name: string,
26
+ value: string,
27
+ options: {
28
+ path?: string | undefined
29
+ maxAge?: number | undefined
30
+ sameSite?: 'lax' | 'strict' | 'none' | undefined
31
+ secure?: boolean | undefined
32
+ }
33
+ ) {
34
+ const parts = [`${name}=${encodeURIComponent(value)}`]
35
+ parts.push(`path=${options.path ?? '/'}`)
36
+ if (options.maxAge !== undefined) parts.push(`max-age=${options.maxAge}`)
37
+ if (options.sameSite !== undefined) parts.push(`samesite=${options.sameSite}`)
38
+ if (options.secure) parts.push('secure')
39
+ // biome-ignore lint/suspicious/noDocumentCookie: Cookie Store API has limited support; document.cookie is standard for theme persistence
40
+ document.cookie = parts.join('; ')
41
+ }
42
+
43
+ function deleteCookie(name: string, path = '/') {
44
+ // biome-ignore lint/suspicious/noDocumentCookie: Cookie Store API has limited support; document.cookie is standard for theme persistence
45
+ document.cookie = `${name}=; path=${path}; max-age=0`
46
+ }
47
+
48
+ /**
49
+ * Creates a theme store backed by cookies.
50
+ *
51
+ * Persists across sessions. Cookies are sent with every request, so the server can
52
+ * read the theme during SSR to avoid flash of wrong theme. Cross-tab sync is not
53
+ * supported (cookies have no StorageEvent).
54
+ *
55
+ * @param themes - Record mapping theme keys to values (for validation)
56
+ * @param options.cookieName - Cookie name for theme storage
57
+ * @param options.path - Cookie path (default: '/')
58
+ * @param options.maxAge - Cookie max-age in seconds
59
+ * @param options.sameSite - Cookie sameSite attribute
60
+ * @param options.secure - Cookie secure attribute
61
+ * @returns ThemeStore
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * const themes = { current: 'theme-current', grayscale: 'theme-grayscale' }
66
+ * const store = cookieThemeStore(themes, { cookieName: 'theme' })
67
+ * store.read()
68
+ * store.write(themeEntry(themes, 'grayscale'))
69
+ * store.subscribe((themeResult) => {})
70
+ * ```
71
+ */
72
+ export function cookieThemeStore<Themes extends ThemeMap>(
73
+ themes: Themes,
74
+ options: CookieThemeStoreOptions
75
+ ): Required<ThemeStore<Themes>> {
76
+ const { cookieName, path = '/', maxAge, sameSite, secure } = options
77
+
78
+ if (document.cookie === undefined) {
79
+ return dummyThemeStore
80
+ }
81
+
82
+ const handlers = new Set<(theme: ThemeEntry<Themes> | undefined) => void>()
83
+ let lastNotifiedKey: keyof Themes | undefined = read()?.theme ?? undefined
84
+
85
+ function read() {
86
+ const stored = getCookieValue(cookieName)
87
+ const theme = parseStoredTheme(themes, stored)
88
+ if (theme === undefined) return undefined
89
+ return themeEntry(themes, theme)
90
+ }
91
+
92
+ function notify() {
93
+ const result = read()
94
+ const key = result?.theme ?? undefined
95
+ if (key === lastNotifiedKey) return
96
+ lastNotifiedKey = key
97
+ for (const h of handlers) h(result)
98
+ }
99
+
100
+ return {
101
+ read,
102
+ write(entry) {
103
+ try {
104
+ if (entry === undefined) {
105
+ deleteCookie(cookieName, path)
106
+ } else {
107
+ const opts: {
108
+ path: string
109
+ maxAge?: number
110
+ sameSite?: 'lax' | 'strict' | 'none'
111
+ secure?: boolean
112
+ } = { path }
113
+ if (maxAge !== undefined) opts.maxAge = maxAge
114
+ if (sameSite !== undefined) opts.sameSite = sameSite
115
+ if (secure) opts.secure = true
116
+ setCookie(cookieName, JSON.stringify(entry), opts)
117
+ }
118
+ notify()
119
+ } catch {
120
+ // Ignore quota or other errors
121
+ }
122
+ },
123
+ subscribe(handler) {
124
+ handlers.add(handler)
125
+ return () => {
126
+ handlers.delete(handler)
127
+ }
128
+ }
129
+ } satisfies ThemeStore<Themes>
130
+ }
131
+
132
+ function getCookieFromHeader(cookieHeader: string, name: string): string | null {
133
+ const match = cookieHeader.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`))
134
+ const value = match?.[1]
135
+ return value !== undefined ? decodeURIComponent(value.trim()) : null
136
+ }
137
+
138
+ /**
139
+ * Reads the theme from cookies during SSR. Use with the request's Cookie header or
140
+ * a framework's cookie API (e.g. Next.js cookies()).
141
+ *
142
+ * @param cookieSource - Raw Cookie header string, or a getter (name) => value for framework APIs
143
+ * @param themes - Record mapping theme keys to values (for validation)
144
+ * @param options - Optional cookie name (default: 'theme')
145
+ * @returns ThemeEntry if valid cookie found, otherwise undefined
146
+ *
147
+ * @example
148
+ * ```ts
149
+ * // With raw Cookie header (Express, Remix, etc.)
150
+ * const theme = getThemeFromCookie(request.headers.get('Cookie') ?? '', themes)
151
+ *
152
+ * // With Next.js cookies()
153
+ * const theme = getThemeFromCookie(
154
+ * (name) => cookies().get(name)?.value ?? undefined,
155
+ * themes
156
+ * )
157
+ * ```
158
+ */
159
+ export function getThemeFromCookie<Themes extends ThemeMap>(
160
+ cookieSource: string | null | undefined | ((name: string) => string | null | undefined),
161
+ themes: Themes,
162
+ options: { cookieName?: string | undefined } = {}
163
+ ): ThemeEntry<Themes> | undefined {
164
+ const cookieName = options.cookieName ?? 'theme'
165
+ const stored =
166
+ typeof cookieSource === 'function'
167
+ ? (cookieSource(cookieName) ?? null)
168
+ : cookieSource
169
+ ? getCookieFromHeader(cookieSource, cookieName)
170
+ : null
171
+ const theme = parseStoredTheme(themes, stored)
172
+ if (theme === undefined) return undefined
173
+ return themeEntry(themes, theme)
174
+ }
@@ -0,0 +1,60 @@
1
+ import type { Required } from 'type-plus'
2
+ import { getDataAttribute } from '../../../attributes/get-data-attribute.ts'
3
+ import { observeDataAttributes } from '../../../attributes/observe-data-attribute.ts'
4
+ import { dummyThemeStore } from '../../../testing/theme/dummy-theme-store.ts'
5
+ import { applyThemeToDataAttribute } from '../../data-attribute/apply-theme-to-data-attribute.ts'
6
+ import { resolveThemeFromDataAttribute } from '../../data-attribute/resolve-theme-from-data-attribute.ts'
7
+ import { themeEntry } from '../../theme-entry.ts'
8
+ import type { ThemeMap } from '../../theme-map.types.ts'
9
+ import type { ThemeStore } from '../theme-store.types.ts'
10
+
11
+ /**
12
+ * Creates a theme store that reads and writes via a data attribute.
13
+ *
14
+ * @param themes - Record mapping theme keys to attribute values
15
+ * @param options.attributeName - Data attribute name (e.g. `data-theme`)
16
+ * @param options.element - Element to operate on (defaults to document.documentElement)
17
+ * @returns ThemeStore
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const themes = { current: 'current', grayscale: 'grayscale' }
22
+ * const store = dataAttributeThemeStore(themes, { attributeName: 'data-theme' })
23
+ * store.read() // returns themeResult from data attribute
24
+ * store.write(themeEntry(themes, 'grayscale'))
25
+ * store.subscribe((themeResult) => {})
26
+ * ```
27
+ */
28
+ export function dataAttributeThemeStore<Themes extends ThemeMap>(
29
+ themes: Themes,
30
+ options: { attributeName: `data-${string}`; element?: Element | undefined }
31
+ ) {
32
+ const element = options.element ?? document?.documentElement
33
+ const { attributeName } = options
34
+
35
+ if (!element) return dummyThemeStore as Required<ThemeStore<Themes>>
36
+
37
+ return {
38
+ read() {
39
+ const value = getDataAttribute(attributeName, element)
40
+ const theme = resolveThemeFromDataAttribute(themes, value)
41
+ if (theme === undefined) return undefined
42
+ return themeEntry(themes, theme)
43
+ },
44
+ write(entry) {
45
+ applyThemeToDataAttribute(element, attributeName, entry)
46
+ },
47
+ subscribe(handler) {
48
+ const observer = observeDataAttributes<string, `data-${string}`>(
49
+ {
50
+ [attributeName]: (value) => {
51
+ const theme = value ? resolveThemeFromDataAttribute(themes, value) : undefined
52
+ handler(theme ? themeEntry(themes, theme) : undefined)
53
+ }
54
+ },
55
+ element
56
+ )
57
+ return () => observer.disconnect()
58
+ }
59
+ } satisfies ThemeStore<Themes>
60
+ }
@@ -0,0 +1,54 @@
1
+ import { themeEntry } from '../../theme-entry.ts'
2
+ import type { ThemeEntry } from '../../theme-entry.types.ts'
3
+ import type { ThemeMap } from '../../theme-map.types.ts'
4
+ import type { ThemeStore } from '../theme-store.types.ts'
5
+
6
+ /**
7
+ * In-memory theme store. Transient state; no persistence.
8
+ *
9
+ * Bakes themes at creation. Validates theme keys on write; read/write use theme keys
10
+ * and build ThemeEntry from the themes map, consistent with other stores.
11
+ *
12
+ * @param themes - Record mapping theme keys to values (for validation and entry construction)
13
+ * @returns ThemeStore
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const themes = { current: 'theme-current', grayscale: 'theme-grayscale' } as const
18
+ * const store = inMemoryThemeStore(themes)
19
+ * store.read() // undefined when empty
20
+ * store.write(themeEntry(themes, 'grayscale'))
21
+ * store.subscribe((themeResult) => {})
22
+ * ```
23
+ */
24
+ export function inMemoryThemeStore<Themes extends ThemeMap>(themes: Themes) {
25
+ let value: keyof Themes | undefined | null
26
+ const listeners = new Set<(v: ThemeEntry<Themes> | undefined) => void>()
27
+
28
+ function read(): ThemeEntry<Themes> | undefined {
29
+ if (value === undefined || value === null) return undefined
30
+ return themeEntry(themes, value)
31
+ }
32
+
33
+ return {
34
+ read,
35
+ write(entry) {
36
+ if (entry === undefined) {
37
+ if (value === undefined || value === null) return
38
+ value = undefined
39
+ for (const fn of listeners) fn(undefined)
40
+ return
41
+ }
42
+ if (!(entry.theme in themes)) return
43
+ if (value === entry.theme) return
44
+ value = entry.theme
45
+ for (const fn of listeners) fn(themeEntry(themes, entry.theme))
46
+ },
47
+ subscribe(handler) {
48
+ listeners.add(handler)
49
+ return () => {
50
+ listeners.delete(handler)
51
+ }
52
+ }
53
+ } satisfies ThemeStore<Themes>
54
+ }
@@ -0,0 +1,83 @@
1
+ import { dummyThemeStore } from '../../../testing/theme/dummy-theme-store.ts'
2
+ import { parseStoredTheme } from '../../_utils/parse-stored-theme.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
+ import type { ThemeStore } from '../theme-store.types.ts'
7
+
8
+ /**
9
+ * Creates a theme store backed by localStorage.
10
+ *
11
+ * Persists across sessions; cross-tab sync via StorageEvent.
12
+ * Same-tab writes trigger manual notify (StorageEvent does not fire for same tab).
13
+ *
14
+ * @param themes - Record mapping theme keys to values (for validation)
15
+ * @param options.storageKey - localStorage key
16
+ * @returns ThemeStore
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const themes = { current: 'theme-current', grayscale: 'theme-grayscale' }
21
+ * const store = localStorageThemeStore(themes, { storageKey: 'theme' })
22
+ * store.read() // returns themeResult from localStorage
23
+ * store.write(themeEntry(themes, 'grayscale'))
24
+ * store.subscribe((themeResult) => {})
25
+ * ```
26
+ */
27
+ export function localStorageThemeStore<Themes extends ThemeMap>(
28
+ themes: Themes,
29
+ options: { storageKey: string }
30
+ ) {
31
+ const { storageKey } = options
32
+
33
+ if (typeof window === 'undefined' || !window.localStorage) {
34
+ return dummyThemeStore satisfies ThemeStore<Themes>
35
+ }
36
+
37
+ const handlers = new Set<(theme: ThemeEntry<Themes> | undefined) => void>()
38
+ let lastNotifiedKey: keyof Themes | undefined = read()?.theme ?? undefined
39
+
40
+ function read() {
41
+ const stored = window.localStorage.getItem(storageKey)
42
+ const theme = parseStoredTheme(themes, stored)
43
+ if (theme === undefined) return undefined
44
+ return themeEntry(themes, theme)
45
+ }
46
+
47
+ function notify() {
48
+ const result = read()
49
+ const key = result?.theme ?? undefined
50
+ if (key === lastNotifiedKey) return
51
+ lastNotifiedKey = key
52
+ for (const h of handlers) h(result)
53
+ }
54
+
55
+ return {
56
+ read,
57
+ write(entry) {
58
+ try {
59
+ if (entry === undefined) {
60
+ window.localStorage.removeItem(storageKey)
61
+ } else {
62
+ window.localStorage.setItem(storageKey, JSON.stringify(entry))
63
+ }
64
+ notify()
65
+ } catch {
66
+ // Ignore quota or other errors
67
+ }
68
+ },
69
+ subscribe(handler) {
70
+ handlers.add(handler)
71
+
72
+ const onStorage = (e: StorageEvent) => {
73
+ if (e.key === storageKey && e.storageArea === window.localStorage) notify()
74
+ }
75
+ window.addEventListener('storage', onStorage)
76
+
77
+ return () => {
78
+ handlers.delete(handler)
79
+ window.removeEventListener('storage', onStorage)
80
+ }
81
+ }
82
+ } satisfies ThemeStore<Themes>
83
+ }
@@ -0,0 +1,43 @@
1
+ import type { RequiredPick } from 'type-plus'
2
+ import { getPrefersColorScheme } from '../../../color-scheme/get-prefers-color-scheme.ts'
3
+ import { observePrefersColorScheme } from '../../../color-scheme/observe-prefers-color-scheme.ts'
4
+ import { themeEntry } from '../../theme-entry.ts'
5
+ import type { ThemeStore } from '../theme-store.types.ts'
6
+
7
+ type PrefersColorSchemeThemes = {
8
+ light: string | readonly string[]
9
+ dark: string | readonly string[]
10
+ }
11
+
12
+ /**
13
+ * Creates a read-only theme store that reads from `prefers-color-scheme`.
14
+ *
15
+ * **Color-scheme specific:** Themes must only include `light` and `dark` keys—this store
16
+ * mirrors the system preference which is always one of these.
17
+ *
18
+ * Returns `ThemeEntry` for `light` or `dark` based on system preference.
19
+ * No write method—the value is controlled by the system.
20
+ *
21
+ * @param themes - Record with `light` and `dark` keys mapping to theme values
22
+ * @returns ThemeStore with read and subscribe only
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const themes = { light: 'theme-light', dark: 'theme-dark' }
27
+ * const store = prefersColorSchemeThemeStore(themes)
28
+ * store.read() // ThemeEntry for current system preference
29
+ * store.subscribe((entry) => {})
30
+ * ```
31
+ */
32
+ export function prefersColorSchemeThemeStore<Themes extends PrefersColorSchemeThemes>(
33
+ themes: Themes
34
+ ): RequiredPick<ThemeStore<Themes>, 'read' | 'subscribe'> {
35
+ return {
36
+ read() {
37
+ return themeEntry(themes, getPrefersColorScheme())
38
+ },
39
+ subscribe(handler) {
40
+ return observePrefersColorScheme((scheme) => handler(themeEntry(themes, scheme)))
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,83 @@
1
+ import { dummyThemeStore } from '../../../testing/theme/dummy-theme-store.ts'
2
+ import { parseStoredTheme } from '../../_utils/parse-stored-theme.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
+ import type { ThemeStore } from '../theme-store.types.ts'
7
+
8
+ /**
9
+ * Creates a theme store backed by sessionStorage.
10
+ *
11
+ * Persists per tab; cross-tab sync via StorageEvent when available.
12
+ * Same-tab writes trigger manual notify (StorageEvent does not fire for same tab).
13
+ *
14
+ * @param themes - Record mapping theme keys to values (for validation)
15
+ * @param options.storageKey - sessionStorage key
16
+ * @returns ThemeStore
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const themes = { current: 'theme-current', grayscale: 'theme-grayscale' }
21
+ * const store = sessionStorageThemeStore(themes, { storageKey: 'theme' })
22
+ * store.read() // returns themeResult from sessionStorage
23
+ * store.write(themeEntry(themes, 'grayscale'))
24
+ * store.subscribe((themeResult) => {})
25
+ * ```
26
+ */
27
+ export function sessionStorageThemeStore<Themes extends ThemeMap>(
28
+ themes: Themes,
29
+ options: { storageKey: string }
30
+ ) {
31
+ const { storageKey } = options
32
+
33
+ if (typeof window === 'undefined' || !window.sessionStorage) {
34
+ return dummyThemeStore satisfies ThemeStore<Themes>
35
+ }
36
+
37
+ const handlers = new Set<(theme: ThemeEntry<Themes> | undefined) => void>()
38
+ let lastNotifiedKey: keyof Themes | undefined = read()?.theme ?? undefined
39
+
40
+ function read() {
41
+ const stored = window.sessionStorage.getItem(storageKey)
42
+ const theme = parseStoredTheme(themes, stored)
43
+ if (theme === undefined) return undefined
44
+ return themeEntry(themes, theme)
45
+ }
46
+
47
+ function notify() {
48
+ const result = read()
49
+ const key = result?.theme ?? undefined
50
+ if (key === lastNotifiedKey) return
51
+ lastNotifiedKey = key
52
+ for (const h of handlers) h(result)
53
+ }
54
+
55
+ return {
56
+ read,
57
+ write(entry) {
58
+ try {
59
+ if (entry === undefined) {
60
+ window.sessionStorage.removeItem(storageKey)
61
+ } else {
62
+ window.sessionStorage.setItem(storageKey, JSON.stringify(entry))
63
+ }
64
+ notify()
65
+ } catch {
66
+ // Ignore quota or other errors
67
+ }
68
+ },
69
+ subscribe(handler) {
70
+ handlers.add(handler)
71
+
72
+ const onStorage = (e: StorageEvent) => {
73
+ if (e.key === storageKey && e.storageArea === window.sessionStorage) notify()
74
+ }
75
+ window.addEventListener('storage', onStorage)
76
+
77
+ return () => {
78
+ handlers.delete(handler)
79
+ window.removeEventListener('storage', onStorage)
80
+ }
81
+ }
82
+ } satisfies ThemeStore<Themes>
83
+ }
@@ -0,0 +1,9 @@
1
+ import type { ThemeMap } from '../theme-map.types.ts'
2
+ import type { AsyncThemeStore } from './async-theme-store.types.ts'
3
+ import type { ThemeStore } from './theme-store.types.ts'
4
+
5
+ /** Factory signature for theme stores. */
6
+ export type ThemeStoreFactory<Themes extends ThemeMap> = (
7
+ themes: Themes,
8
+ ...args: any[]
9
+ ) => ThemeStore<Themes> | AsyncThemeStore<Themes>
@@ -0,0 +1,30 @@
1
+ import type { ThemeEntry } from '../theme-entry.types.ts'
2
+ import type { ThemeMap } from '../theme-map.types.ts'
3
+
4
+ /**
5
+ * Interface for theme stores with optional read, write, and subscribe methods.
6
+ * Data flow participation is inferred from which methods exist:
7
+ *
8
+ * - **read** – Participates in waterfall read for `getThemeFromStores`
9
+ * - **write** – Receives writes from `setThemeToStores`
10
+ * - **subscribe** – GObserved for external changes via `observeThemeFromStores`
11
+ *
12
+ * All methods are optional.
13
+ * Built-in implementations:
14
+ * - `classNameThemeStore`
15
+ * - `cookieThemeStore`
16
+ * - `dataAttributeThemeStore`
17
+ * - `inMemoryThemeStore`
18
+ * - `localStorageThemeStore`
19
+ * - `prefersColorSchemeThemeStore`
20
+ * - `sessionStorageThemeStore`
21
+ *
22
+ * @typeParam Themes - Map of theme keys to their value types (string or readonly string[])
23
+ */
24
+ export interface ThemeStore<Themes extends ThemeMap = ThemeMap> {
25
+ read?: (() => ThemeEntry<Themes> | undefined) | undefined
26
+ write?: ((entry: ThemeEntry<Themes> | undefined) => void) | undefined
27
+ subscribe?:
28
+ | ((handler: (theme: ThemeEntry<Themes> | undefined | null) => void) => () => void)
29
+ | undefined
30
+ }
package/src/theme.ts ADDED
@@ -0,0 +1,14 @@
1
+ export * from './theme/compose-theme-stores.ts'
2
+ export * from './theme/theme-entry.ts'
3
+ export type * from './theme/theme-entry.types.ts'
4
+ export type * from './theme/theme-map.types.ts'
5
+ export type * from './theme/theme-store/async-theme-store.types.ts'
6
+ export * from './theme/theme-store/class-name-theme-store/class-name-theme-store.ts'
7
+ export * from './theme/theme-store/cookie-theme-store/cookie-theme-store.ts'
8
+ export * from './theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.ts'
9
+ export * from './theme/theme-store/in-memory-theme-store/in-memory-theme-store.ts'
10
+ export * from './theme/theme-store/local-storage-theme-store/local-storage-theme-store.ts'
11
+ export * from './theme/theme-store/prefers-color-scheme-theme-store/prefers-color-scheme-theme-store.ts'
12
+ export * from './theme/theme-store/session-storage-theme-store/session-storage-theme-store.ts'
13
+ export type * from './theme/theme-store/theme-store.types.ts'
14
+ export type * from './theme/theme-store/theme-store-factory.types.ts'
@@ -0,0 +1,27 @@
1
+ const DEFAULT_REM_TO_PX_SCALE = 16
2
+
3
+ /**
4
+ * Returns the current document's rem-to-px scale (the pixel value of 1rem).
5
+ *
6
+ * Reads the computed font size of the root element (`html`), which is the value
7
+ * the browser uses to resolve rem units. In non-browser environments (e.g. SSR,
8
+ * Node), returns {@link DEFAULT_REM_TO_PX_SCALE} as a fallback.
9
+ *
10
+ * @returns The number of pixels that 1rem equals in the current document,
11
+ * or {@link DEFAULT_REM_TO_PX_SCALE} when not in a browser.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * getRemToPxScale() // e.g. 16 (or 20 if user increased default font size)
16
+ * rem2px(1, { base: getRemToPxScale() }) // matches actual 1rem in the document
17
+ * ```
18
+ */
19
+ export function getRemToPxScale(): number {
20
+ /* c8 ignore start */
21
+ if (typeof document === 'undefined' || !document.documentElement) {
22
+ return DEFAULT_REM_TO_PX_SCALE
23
+ }
24
+ /* c8 ignore end */
25
+ const rootFontSize = getComputedStyle(document.documentElement).fontSize
26
+ return Number.parseFloat(rootFontSize) ?? DEFAULT_REM_TO_PX_SCALE
27
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Converts pixel values to numbers.
3
+ *
4
+ * @param px - The pixel value to convert. Can be a number or string (e.g. '16px' or '16')
5
+ * @returns The numeric value
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * px2num(16) // 16
10
+ * px2num('32px') // 32
11
+ * px2num('12.5px') // 12.5
12
+ * px2num('0px') // 0
13
+ * ```
14
+ */
15
+ export function px2num(px: number | string | undefined): number {
16
+ return typeof px === 'string' ? Number.parseFloat(px.replace(/px$/, '')) : Number(px)
17
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Converts pixel values to rem units.
3
+ *
4
+ * @param px - The pixel value to convert. Can be a number or string (e.g. '16px' or '16')
5
+ * @param options - Optional configuration
6
+ * @param options.base - Base pixel value to calculate rem units from. Defaults to 16
7
+ * @param options.precision - Number of decimal places in the output. Defaults to 4
8
+ * @returns The converted value as a string with 'rem' units
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * px2rem(16) // '1.0000'
13
+ * px2rem('32px') // '2.0000'
14
+ * px2rem(20, { base: 20 }) // '1.0000'
15
+ * px2rem(13, { precision: 2 }) // '0.81'
16
+ * ```
17
+ */
18
+ export function px2rem(
19
+ px: number | string,
20
+ options?: { base?: number | undefined; precision?: number | undefined }
21
+ ): number {
22
+ const { base = 16, precision = 4 } = options ?? {}
23
+
24
+ if (typeof px === 'string') {
25
+ px = px.replace(/px$/, '')
26
+ px = Number.parseFloat(px)
27
+ }
28
+
29
+ return Number((px / base).toFixed(precision))
30
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Converts rem values to pixel units.
3
+ *
4
+ * @param rem - The rem value to convert. Can be a number or string (e.g. '1rem' or '1')
5
+ * @param options - Optional configuration
6
+ * @param options.base - Base pixel value to calculate pixels from. Defaults to 16
7
+ * @param options.precision - Number of decimal places in the output. Defaults to 4
8
+ * @returns The converted value as a string with 'px' units
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * rem2px(1) // '16.0000'
13
+ * rem2px('2rem') // '32.0000'
14
+ * rem2px(1, { base: 20 }) // '20.0000'
15
+ * rem2px(0.8125, { precision: 2 }) // '13.00'
16
+ * ```
17
+ */
18
+ export function rem2px(
19
+ rem: number | string,
20
+ options?: { base?: number | undefined; precision?: number | undefined }
21
+ ): number {
22
+ const { base = 16, precision = 4 } = options ?? {}
23
+
24
+ if (typeof rem === 'string') {
25
+ rem = rem.replace(/rem$/, '')
26
+ rem = Number.parseFloat(rem)
27
+ }
28
+
29
+ return Number((rem * base).toFixed(precision))
30
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Appends a suffix to an ID if the ID is defined.
3
+ *
4
+ * @param id - The ID to append the suffix to.
5
+ * @param suffix - The suffix to append to the ID.
6
+ * @returns The ID with the suffix appended, or undefined if the ID is undefined.
7
+ */
8
+ export function appendId(id: string | undefined, suffix: string): string | undefined {
9
+ return id ? `${id}-${suffix}` : undefined
10
+ }