@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,87 @@
1
+ import { useCallback, useEffect, useState } from 'react'
2
+ import { themeEntry } from '../../theme/theme-entry.ts'
3
+ import type { ThemeEntry } from '../../theme/theme-entry.types.ts'
4
+ import type { ThemeMap } from '../../theme/theme-map.types.ts'
5
+ import type { AsyncThemeStore } from '../../theme/theme-store/async-theme-store.types.ts'
6
+ import type { ThemeStore } from '../../theme/theme-store/theme-store.types.ts'
7
+ import { appendId } from '../../utils/append-id.ts'
8
+ import { Button } from '../button.tsx'
9
+ import { ThemeResultCard } from './theme-result-card.tsx'
10
+
11
+ type ThemeStoreDemoProps<Themes extends ThemeMap> = {
12
+ store: ThemeStore<Themes> | AsyncThemeStore<Themes>
13
+ themes: Themes
14
+ /** Theme keys to show as "Write X" buttons. Defaults to first 3 keys from themes. */
15
+ setThemeKeys?: (keyof Themes)[] | undefined
16
+ 'data-testid'?: string | undefined
17
+ }
18
+
19
+ /**
20
+ * Demo component that uses theme store.read, store.write, and store.subscribe.
21
+ * Renders observed value, a one-time read result, and buttons to trigger read/write for showcasing behavior.
22
+ * All interactive elements and result areas use data-testid for testing.
23
+ */
24
+ export function ThemeStoreDemo<Themes extends ThemeMap>({
25
+ store,
26
+ themes,
27
+ setThemeKeys,
28
+ 'data-testid': dataTestId = 'theme-store-demo'
29
+ }: ThemeStoreDemoProps<Themes>) {
30
+ const [observedResult, setObservedResult] = useState<ThemeEntry<Themes> | undefined | null>(
31
+ undefined
32
+ )
33
+ const [getResult, setGetResult] = useState<ThemeEntry<Themes> | undefined | null>(undefined)
34
+
35
+ const keys = (setThemeKeys ??
36
+ (Object.keys(themes) as (keyof Themes)[]).slice(0, 3)) as (keyof Themes)[]
37
+
38
+ useEffect(() => {
39
+ void Promise.resolve(store.read?.()).then((r) => setObservedResult(r ?? undefined))
40
+ }, [store])
41
+
42
+ useEffect(() => {
43
+ return store.subscribe?.(setObservedResult)
44
+ }, [store])
45
+
46
+ const handleGet = useCallback(async () => {
47
+ const result = store.read?.()
48
+ const resolved = await Promise.resolve(result)
49
+ setGetResult(resolved ?? undefined)
50
+ }, [store])
51
+
52
+ const handleSet = useCallback(
53
+ (theme: keyof Themes) => async () => {
54
+ const ret = store.write?.(themeEntry(themes, theme))
55
+ await Promise.resolve(ret)
56
+ },
57
+ [store, themes]
58
+ )
59
+
60
+ const observeTestId = appendId(dataTestId, 'observe')
61
+ const readTestId = appendId(dataTestId, 'read')
62
+
63
+ return (
64
+ <div className="flex flex-col gap-2" data-testid={dataTestId}>
65
+ <div className="flex flex-wrap gap-2">
66
+ <Button onClick={handleGet} data-testid={appendId(dataTestId, 'btn-read')}>
67
+ Read theme
68
+ </Button>
69
+ {keys.map((key) => (
70
+ <Button
71
+ key={String(key)}
72
+ onClick={handleSet(key)}
73
+ data-testid={appendId(dataTestId, `btn-write-${String(key)}`)}
74
+ >
75
+ Write {String(key)}
76
+ </Button>
77
+ ))}
78
+ </div>
79
+ <ThemeResultCard title="Read (one-time)" result={getResult} data-testid={readTestId} />
80
+ <ThemeResultCard
81
+ title="Observed (subscribe)"
82
+ result={observedResult}
83
+ data-testid={observeTestId}
84
+ />
85
+ </div>
86
+ )
87
+ }
@@ -0,0 +1,34 @@
1
+ import type { ThemeEntry } from '../theme-entry.types.ts'
2
+ import type { ThemeMap } from '../theme-map.types.ts'
3
+ import type { AsyncThemeStore } from '../theme-store/async-theme-store.types.ts'
4
+ import type { ThemeStore } from '../theme-store/theme-store.types.ts'
5
+
6
+ type StoreWithRead<Themes extends ThemeMap> = (ThemeStore<Themes> | AsyncThemeStore<Themes>) & {
7
+ read: () => ThemeEntry<Themes> | undefined | Promise<ThemeEntry<Themes> | undefined>
8
+ }
9
+
10
+ /**
11
+ * Reads theme from stores using waterfall strategy.
12
+ *
13
+ * Iterates stores in order; returns first non-empty result.
14
+ * Only includes stores that have a `read` method.
15
+ *
16
+ * @param stores - Array of theme stores
17
+ * @param defaultTheme - Fallback when all stores return empty
18
+ * @returns Resolved theme key or defaultTheme
19
+ */
20
+ export async function getThemeFromStores<Themes extends ThemeMap>(
21
+ stores: (ThemeStore<Themes> | AsyncThemeStore<Themes>)[],
22
+ defaultTheme: keyof Themes | undefined
23
+ ): Promise<keyof Themes | undefined> {
24
+ const withRead = stores.filter((s): s is StoreWithRead<Themes> => typeof s.read === 'function')
25
+
26
+ for (const store of withRead) {
27
+ const result = await Promise.resolve(store.read())
28
+ if (result !== undefined) {
29
+ return result.theme
30
+ }
31
+ }
32
+
33
+ return defaultTheme
34
+ }
@@ -0,0 +1,57 @@
1
+ import type { RequiredPick } from 'type-plus'
2
+ import type { ThemeMap } from '../theme-map.types.ts'
3
+ import type { AsyncThemeStore } from '../theme-store/async-theme-store.types.ts'
4
+ import type { ThemeStore } from '../theme-store/theme-store.types.ts'
5
+ import { getThemeFromStores } from './get-theme-from-stores.ts'
6
+
7
+ type StoreWithSubscribe<Themes extends ThemeMap> = RequiredPick<
8
+ AsyncThemeStore<Themes>,
9
+ 'subscribe'
10
+ >
11
+
12
+ /**
13
+ * Subscribes to stores that have a subscribe method.
14
+ *
15
+ * When any store emits, runs coalesced handler (getThemeFromStores + callback).
16
+ * Skips handler if resolved theme equals last emitted (value equality).
17
+ *
18
+ * @param stores - Array of theme stores
19
+ * @param defaultTheme - Fallback when all stores return empty
20
+ * @param handler - Callback with resolved theme key
21
+ * @returns Unsubscribe function
22
+ */
23
+ export function observeThemeFromStores<Themes extends ThemeMap>(
24
+ stores: (ThemeStore<Themes> | AsyncThemeStore<Themes>)[],
25
+ defaultTheme: keyof Themes | undefined,
26
+ handler: (theme: keyof Themes | undefined) => void
27
+ ): () => void {
28
+ const withSubscribe = stores.filter(
29
+ (s): s is StoreWithSubscribe<Themes> => typeof s.subscribe === 'function'
30
+ )
31
+
32
+ let scheduled = false
33
+ let lastEmitted: keyof Themes | undefined
34
+
35
+ const scheduleNotify = () => {
36
+ if (scheduled) return
37
+ scheduled = true
38
+ queueMicrotask(async () => {
39
+ scheduled = false
40
+ const theme = await getThemeFromStores(stores, defaultTheme)
41
+ if (theme === lastEmitted) return
42
+ lastEmitted = theme
43
+ handler(theme)
44
+ })
45
+ }
46
+
47
+ // Initial notify
48
+ scheduleNotify()
49
+
50
+ const unSubs = withSubscribe.map((s) => s.subscribe!((_result) => scheduleNotify()))
51
+
52
+ return () => {
53
+ for (const unSub of unSubs) {
54
+ unSub()
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,21 @@
1
+ import { tryParseJSON } from '../../_internal/utils/try-parse-json.ts'
2
+ import type { ThemeMap } from '../theme-map.types.ts'
3
+
4
+ /**
5
+ * Parses stored JSON theme and validates the theme key against theme map.
6
+ *
7
+ * Expects stored shape: { theme: string, value?: unknown }
8
+ *
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
12
+ */
13
+ export function parseStoredTheme<Theme extends string>(
14
+ themes: ThemeMap<Theme> | undefined,
15
+ value: string | null | undefined
16
+ ): Theme | undefined {
17
+ const parsed = tryParseJSON<{ theme: string }>(value)
18
+ if (!parsed?.theme || typeof parsed.theme !== 'string') return undefined
19
+ if (themes && !(parsed.theme in themes)) return undefined
20
+ return parsed.theme as Theme
21
+ }
@@ -0,0 +1,23 @@
1
+ import type { ThemeEntry } from '../theme-entry.types.ts'
2
+ import type { ThemeMap } from '../theme-map.types.ts'
3
+ import type { AsyncThemeStore } from '../theme-store/async-theme-store.types.ts'
4
+ import type { ThemeStore } from '../theme-store/theme-store.types.ts'
5
+
6
+ type StoreWithWrite<Themes extends ThemeMap> = (ThemeStore<Themes> | AsyncThemeStore<Themes>) & {
7
+ write: (entry: ThemeEntry<Themes> | undefined) => void | Promise<void>
8
+ }
9
+
10
+ /**
11
+ * Writes theme entry to all stores that have a write method.
12
+ *
13
+ * @param stores - Array of theme stores
14
+ * @param entry - Theme entry to write, or undefined to clear
15
+ */
16
+ export async function setThemeToStores<Themes extends ThemeMap>(
17
+ stores: (ThemeStore<Themes> | AsyncThemeStore<Themes>)[],
18
+ entry: ThemeEntry<Themes> | undefined
19
+ ): Promise<void> {
20
+ const withWrite = stores.filter((s): s is StoreWithWrite<Themes> => typeof s.write === 'function')
21
+
22
+ await Promise.all(withWrite.map((store) => Promise.resolve(store.write!(entry))))
23
+ }
@@ -0,0 +1,26 @@
1
+ import type { ThemeEntry } from '../theme-entry.types.ts'
2
+ import type { ThemeMap } from '../theme-map.types.ts'
3
+
4
+ /**
5
+ * Applies theme to element by updating its class attribute.
6
+ *
7
+ * Removes all theme classes from the element, then adds classes for the given entry.
8
+ * When entry is undefined, removes all theme classes (themeMap needed for clear path).
9
+ *
10
+ * @param element - Target element
11
+ * @param entry - Theme entry to apply, or undefined to clear
12
+ * @param themes - Record mapping theme keys to class names (used for clear case)
13
+ */
14
+ export function applyThemeToClassName<Themes extends ThemeMap>(
15
+ themes: Themes,
16
+ element: Element,
17
+ entry: ThemeEntry<Themes> | undefined
18
+ ): void {
19
+ const allThemeClasses = Object.values(themes).flatMap((v) => (Array.isArray(v) ? [...v] : [v]))
20
+ const current = element.className.trim()
21
+ const currentClasses = current ? current.split(/\s+/) : []
22
+ const withoutThemes = currentClasses.filter((c) => !allThemeClasses.includes(c))
23
+ const activeClasses =
24
+ entry !== undefined ? (Array.isArray(entry.value) ? [...entry.value] : [entry.value]) : []
25
+ element.className = [...withoutThemes, ...activeClasses].filter(Boolean).join(' ')
26
+ }
@@ -0,0 +1,22 @@
1
+ import { findKey } from 'type-plus'
2
+ import type { ThemeMap } from '../theme-map.types.ts'
3
+
4
+ /**
5
+ * Resolves theme key from class name string by matching against theme map.
6
+ *
7
+ * @param className - Element class attribute value
8
+ * @param themes - Record mapping theme keys to class name(s); arrays use first value for matching
9
+ * @returns Theme key if a match is found, otherwise undefined
10
+ */
11
+ export function resolveThemeFromClassName<Theme extends string>(
12
+ themes: ThemeMap<Theme>,
13
+ className: string
14
+ ): Theme | undefined {
15
+ const theme = findKey(themes, (key) => {
16
+ const value = themes[key]
17
+ if (value === undefined) return false
18
+ const v = Array.isArray(value) ? value[0] : value
19
+ return !!v && className.includes(v)
20
+ })
21
+ return theme as Theme | undefined
22
+ }
@@ -0,0 +1,139 @@
1
+ import type { Required, RequiredPick } from 'type-plus'
2
+ import { setThemeToStores } from './_utils/set-theme-to-stores.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 { AsyncThemeStore } from './theme-store/async-theme-store.types.ts'
7
+ import type { ThemeStore } from './theme-store/theme-store.types.ts'
8
+ import type { ThemeStoreFactory } from './theme-store/theme-store-factory.types.ts'
9
+
10
+ /** Input item for one position: concrete store or factory config [factory, options?]. */
11
+ export type ComposeThemeStoreEntry<
12
+ Themes extends ThemeMap,
13
+ F extends ThemeStoreFactory<Themes> = never
14
+ > = ThemeStore<Themes> | AsyncThemeStore<Themes> | readonly [F] | readonly [F, Parameters<F>[1]]
15
+
16
+ export type ComposeThemeStoresOptions<Themes extends ThemeMap> = {
17
+ defaultTheme?: keyof Themes | undefined
18
+ }
19
+
20
+ /**
21
+ * Composes multiple theme stores into a single store.
22
+ *
23
+ * Accepts concrete stores or store factory tuples `[factory]` or `[factory, options]`.
24
+ * For factory tuples, calls `factory(themes)` or `factory(themes, options)` to create stores.
25
+ *
26
+ * - **read**: Returns first non-empty `ThemeEntry` from stores (waterfall). When all empty
27
+ * and `defaultTheme` is defined, returns `themeEntry(themes, defaultTheme)`.
28
+ * - **write**: Delegates to `setThemeToStores` (writes to all stores with write).
29
+ * - **subscribe**: Aggregates child store subscriptions. No initial notify—handler is only
30
+ * called when a child store emits.
31
+ *
32
+ * @param themes - ThemeMap for synthesizing fallback ThemeEntry
33
+ * @param stores - Array of theme stores or factory configs [factory, options?]
34
+ * @param options.defaultTheme - Fallback theme key when all stores return empty
35
+ * @returns AsyncThemeStore
36
+ */
37
+ export function composeThemeStores<
38
+ Themes extends ThemeMap,
39
+ A extends ThemeStoreFactory<Themes> = never,
40
+ B extends ThemeStoreFactory<Themes> = never,
41
+ C extends ThemeStoreFactory<Themes> = never,
42
+ D extends ThemeStoreFactory<Themes> = never,
43
+ E extends ThemeStoreFactory<Themes> = never,
44
+ F extends ThemeStoreFactory<Themes> = never,
45
+ G extends ThemeStoreFactory<Themes> = never,
46
+ H extends ThemeStoreFactory<Themes> = never
47
+ >(
48
+ themes: Themes,
49
+ stores: readonly [
50
+ store1: ComposeThemeStoreEntry<Themes, A>,
51
+ store2?: ComposeThemeStoreEntry<Themes, B>,
52
+ store3?: ComposeThemeStoreEntry<Themes, C>,
53
+ store4?: ComposeThemeStoreEntry<Themes, D>,
54
+ store5?: ComposeThemeStoreEntry<Themes, E>,
55
+ store6?: ComposeThemeStoreEntry<Themes, F>,
56
+ store7?: ComposeThemeStoreEntry<Themes, G>,
57
+ store8?: ComposeThemeStoreEntry<Themes, H>
58
+ ],
59
+ options?: ComposeThemeStoresOptions<Themes> | undefined
60
+ ): Required<AsyncThemeStore<Themes>> {
61
+ const { defaultTheme } = options ?? {}
62
+ const resolved = resolveStores(themes, stores)
63
+ const withRead = resolved.filter((s): s is StoreWithRead<Themes> => typeof s.read === 'function')
64
+
65
+ async function readFromStores(): Promise<ThemeEntry<Themes> | undefined> {
66
+ for (const store of withRead) {
67
+ const result = await Promise.resolve(store.read())
68
+ if (result !== undefined) return result
69
+ }
70
+ return defaultTheme !== undefined ? themeEntry(themes, defaultTheme) : undefined
71
+ }
72
+
73
+ const withSubscribe = resolved.filter(
74
+ (s): s is StoreWithSubscribe<Themes> => typeof s.subscribe === 'function'
75
+ )
76
+
77
+ function subscribe(handler: (theme: ThemeEntry<Themes> | undefined | null) => void): () => void {
78
+ let scheduled = false
79
+ let lastEmitted: keyof Themes | undefined
80
+
81
+ const scheduleNotify = () => {
82
+ if (scheduled) return
83
+ scheduled = true
84
+ queueMicrotask(async () => {
85
+ scheduled = false
86
+ const entry = await readFromStores()
87
+ const key = entry?.theme ?? undefined
88
+ if (key === lastEmitted) return
89
+ lastEmitted = key
90
+ handler(entry ?? undefined)
91
+ })
92
+ }
93
+
94
+ const unSubs = withSubscribe.map((s) => s.subscribe!((_result) => scheduleNotify()))
95
+
96
+ return () => {
97
+ for (const unSub of unSubs) {
98
+ unSub()
99
+ }
100
+ }
101
+ }
102
+
103
+ return {
104
+ read: readFromStores,
105
+ write(entry) {
106
+ return setThemeToStores(resolved, entry)
107
+ },
108
+ subscribe: withSubscribe.length > 0 ? subscribe : () => () => {}
109
+ }
110
+ }
111
+
112
+ type StoreWithRead<Themes extends ThemeMap> = RequiredPick<AsyncThemeStore<Themes>, 'read'>
113
+
114
+ type StoreWithSubscribe<Themes extends ThemeMap> = RequiredPick<
115
+ AsyncThemeStore<Themes>,
116
+ 'subscribe'
117
+ >
118
+
119
+ function resolveStores<Themes extends ThemeMap>(
120
+ themes: Themes,
121
+ stores: readonly [
122
+ store1: ComposeThemeStoreEntry<Themes, any>,
123
+ store2?: ComposeThemeStoreEntry<Themes, any>,
124
+ store3?: ComposeThemeStoreEntry<Themes, any>,
125
+ store4?: ComposeThemeStoreEntry<Themes, any>,
126
+ store5?: ComposeThemeStoreEntry<Themes, any>,
127
+ store6?: ComposeThemeStoreEntry<Themes, any>,
128
+ store7?: ComposeThemeStoreEntry<Themes, any>,
129
+ store8?: ComposeThemeStoreEntry<Themes, any>
130
+ ]
131
+ ): (ThemeStore<Themes> | AsyncThemeStore<Themes>)[] {
132
+ return stores.map((item) => {
133
+ if (Array.isArray(item)) {
134
+ const [factory, options] = item
135
+ return (factory as (t: Themes, o?: unknown) => ThemeStore<Themes>)(themes, options)
136
+ }
137
+ return item as ThemeStore<Themes> | AsyncThemeStore<Themes>
138
+ })
139
+ }
@@ -0,0 +1,27 @@
1
+ import type { ThemeEntry } from '../theme-entry.types.ts'
2
+ import type { ThemeMap } from '../theme-map.types.ts'
3
+
4
+ /**
5
+ * Applies theme to element by setting or removing a data attribute.
6
+ *
7
+ * @param element - Target element
8
+ * @param attributeName - Data attribute name (e.g. `data-theme`)
9
+ * @param entry - Theme entry to apply, or undefined to remove attribute
10
+ */
11
+ export function applyThemeToDataAttribute<Themes extends ThemeMap>(
12
+ element: Element,
13
+ attributeName: `data-${string}`,
14
+ entry: ThemeEntry<Themes> | undefined
15
+ ): void {
16
+ if (entry === undefined) {
17
+ element.removeAttribute(attributeName)
18
+ return
19
+ }
20
+ const val = entry.value
21
+ const attributeValue = Array.isArray(val) ? val[0] : val
22
+ if (attributeValue !== undefined && attributeValue !== '') {
23
+ element.setAttribute(attributeName, attributeValue)
24
+ } else {
25
+ element.removeAttribute(attributeName)
26
+ }
27
+ }
@@ -0,0 +1,23 @@
1
+ import { findKey } from 'type-plus'
2
+ import type { ThemeMap } from '../theme-map.types.ts'
3
+
4
+ /**
5
+ * Resolves theme key from data attribute value by matching against theme map.
6
+ *
7
+ * @param attrValue - Data attribute value (e.g. from getAttribute)
8
+ * @param themes - Record mapping theme keys to attribute values
9
+ * @returns Theme key if a match is found, otherwise undefined
10
+ */
11
+ export function resolveThemeFromDataAttribute<Theme extends string>(
12
+ themes: ThemeMap<Theme>,
13
+ attrValue: string | null
14
+ ): Theme | undefined {
15
+ if (attrValue === null || attrValue === '') return undefined
16
+ const theme = findKey(themes, (key) => {
17
+ const value = themes[key]
18
+ if (value === undefined) return false
19
+ const v = Array.isArray(value) ? value[0] : value
20
+ return v === attrValue
21
+ })
22
+ return theme as Theme | undefined
23
+ }
@@ -0,0 +1,10 @@
1
+ import type { ThemeEntry } from './theme-entry.types.ts'
2
+ import type { ThemeMap } from './theme-map.types.ts'
3
+
4
+ /** Creates ThemeEntry from theme map and theme key. */
5
+ export function themeEntry<Themes extends ThemeMap>(
6
+ themes: Themes,
7
+ theme: keyof Themes
8
+ ): ThemeEntry<Themes> {
9
+ return { theme, value: themes[theme] }
10
+ }
@@ -0,0 +1,11 @@
1
+ import type { ThemeMap } from './theme-map.types.ts'
2
+
3
+ /**
4
+ * Theme entry is a pair of theme key and theme value.
5
+ *
6
+ * It is the basic value persisted by the theme stores.
7
+ */
8
+ export interface ThemeEntry<Themes extends ThemeMap = ThemeMap> {
9
+ theme: keyof Themes
10
+ value: Themes[keyof Themes]
11
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Record mapping theme keys to their values.
3
+ * Each value can be a single string or readonly string[] (e.g. multiple CSS classes).
4
+ * Used by all ThemeStore factories via the themes option.
5
+ */
6
+ export type ThemeMap<Theme extends string = string> = Record<Theme, string | readonly string[]>
@@ -0,0 +1,24 @@
1
+ import type { ThemeEntry } from '../theme-entry.types.ts'
2
+ import type { ThemeMap } from '../theme-map.types.ts'
3
+
4
+ /**
5
+ * Async variant of `ThemeStore` where read and write may return promises.
6
+ * Use for remote persistence, polling, or WebSocket-based sync.
7
+ *
8
+ * Same optional methods as `ThemeStore`:
9
+ *
10
+ * - **read** – Can return `Promise<ThemeEntry | undefined>` for async sources
11
+ * - **write** – Can return `Promise<void>` for async persistence
12
+ * - **subscribe** – Same signature as sync; observes external changes
13
+ *
14
+ * @typeParam Themes - Map of theme keys to their value types (string or readonly string[])
15
+ */
16
+ export interface AsyncThemeStore<Themes extends ThemeMap = ThemeMap> {
17
+ read?:
18
+ | (() => ThemeEntry<Themes> | undefined | Promise<ThemeEntry<Themes> | undefined>)
19
+ | undefined
20
+ write?: ((entry: ThemeEntry<Themes> | undefined) => void | Promise<void>) | undefined
21
+ subscribe?:
22
+ | ((handler: (entry: ThemeEntry<Themes> | undefined | null) => void) => () => void)
23
+ | undefined
24
+ }
@@ -0,0 +1,62 @@
1
+ import type { Required } from 'type-plus'
2
+ import { observeAttributes } from '../../../attributes/observe-attribute.ts'
3
+ import { dummyThemeStore } from '../../../testing/theme/dummy-theme-store.ts'
4
+ import { applyThemeToClassName } from '../../class-name/apply-theme-to-class-name.ts'
5
+ import { resolveThemeFromClassName } from '../../class-name/resolve-theme-from-class-name.ts'
6
+ import { themeEntry } from '../../theme-entry.ts'
7
+ import type { ThemeMap } from '../../theme-map.types.ts'
8
+ import type { ThemeStore } from '../theme-store.types.ts'
9
+
10
+ /**
11
+ * Creates a theme store that reads and writes via element class names.
12
+ *
13
+ * @param themes - Record mapping theme keys to class name(s)
14
+ * @param options.element - Element to operate on (defaults to document.documentElement)
15
+ * @returns ThemeStore
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const themes = { current: 'theme-current', grayscale: 'theme-grayscale' }
20
+ * const store = classNameThemeStore(themes)
21
+ * store.read() // returns themeResult from element.className
22
+ * store.write(themeEntry(themes, 'grayscale'))
23
+ * store.subscribe((themeResult) => {})
24
+ * ```
25
+ */
26
+ export function classNameThemeStore<Themes extends ThemeMap>(
27
+ themes: Themes,
28
+ options?: { element?: Element | undefined }
29
+ ): Required<ThemeStore<Themes>> {
30
+ const element = options?.element ?? document?.documentElement
31
+
32
+ if (!element) return dummyThemeStore
33
+
34
+ return {
35
+ read() {
36
+ const theme = resolveThemeFromClassName(themes, element.className)
37
+ if (theme === undefined) return undefined
38
+ return themeEntry(themes, theme)
39
+ },
40
+ write(entry) {
41
+ applyThemeToClassName(themes, element, entry)
42
+ },
43
+ subscribe(handler) {
44
+ let lastEmitted: keyof Themes | undefined | null = null
45
+ const observer = observeAttributes(
46
+ {
47
+ class: (value) => {
48
+ const theme = value ? resolveThemeFromClassName(themes, value) : undefined
49
+ const entry = theme ? themeEntry(themes, theme) : undefined
50
+ const key = theme ?? undefined
51
+
52
+ if (lastEmitted === key) return
53
+ lastEmitted = key
54
+ handler(entry)
55
+ }
56
+ },
57
+ element
58
+ )
59
+ return () => observer.disconnect()
60
+ }
61
+ }
62
+ }