@just-web/toolkits 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/dist/attributes/get-attribute.cjs +1 -1
  2. package/dist/attributes/get-attribute.cjs.map +1 -1
  3. package/dist/attributes/get-attribute.d.cts +2 -2
  4. package/dist/attributes/get-attribute.d.cts.map +1 -1
  5. package/dist/attributes/get-attribute.d.mts +2 -2
  6. package/dist/attributes/get-attribute.d.mts.map +1 -1
  7. package/dist/attributes/get-attribute.mjs +1 -1
  8. package/dist/attributes/get-attribute.mjs.map +1 -1
  9. package/dist/attributes/get-data-attribute.cjs +1 -1
  10. package/dist/attributes/get-data-attribute.cjs.map +1 -1
  11. package/dist/attributes/get-data-attribute.d.cts +2 -2
  12. package/dist/attributes/get-data-attribute.d.cts.map +1 -1
  13. package/dist/attributes/get-data-attribute.d.mts +2 -2
  14. package/dist/attributes/get-data-attribute.d.mts.map +1 -1
  15. package/dist/attributes/get-data-attribute.mjs +1 -1
  16. package/dist/attributes/get-data-attribute.mjs.map +1 -1
  17. package/dist/attributes/observe-attribute.cjs +1 -1
  18. package/dist/attributes/observe-attribute.cjs.map +1 -1
  19. package/dist/attributes/observe-attribute.d.cts +2 -2
  20. package/dist/attributes/observe-attribute.d.cts.map +1 -1
  21. package/dist/attributes/observe-attribute.d.mts +2 -2
  22. package/dist/attributes/observe-attribute.d.mts.map +1 -1
  23. package/dist/attributes/observe-attribute.mjs +1 -1
  24. package/dist/attributes/observe-attribute.mjs.map +1 -1
  25. package/dist/attributes/observe-data-attribute.cjs +1 -1
  26. package/dist/attributes/observe-data-attribute.cjs.map +1 -1
  27. package/dist/attributes/observe-data-attribute.d.cts +2 -2
  28. package/dist/attributes/observe-data-attribute.d.cts.map +1 -1
  29. package/dist/attributes/observe-data-attribute.d.mts +2 -2
  30. package/dist/attributes/observe-data-attribute.d.mts.map +1 -1
  31. package/dist/attributes/observe-data-attribute.mjs +1 -1
  32. package/dist/attributes/observe-data-attribute.mjs.map +1 -1
  33. package/dist/color-scheme/color-scheme.types.d.cts +11 -0
  34. package/dist/color-scheme/color-scheme.types.d.cts.map +1 -0
  35. package/dist/color-scheme/color-scheme.types.d.mts +11 -0
  36. package/dist/color-scheme/color-scheme.types.d.mts.map +1 -0
  37. package/dist/color-scheme/get-prefers-color-scheme.cjs +3 -1
  38. package/dist/color-scheme/get-prefers-color-scheme.cjs.map +1 -1
  39. package/dist/color-scheme/get-prefers-color-scheme.d.cts +7 -2
  40. package/dist/color-scheme/get-prefers-color-scheme.d.cts.map +1 -1
  41. package/dist/color-scheme/get-prefers-color-scheme.d.mts +7 -2
  42. package/dist/color-scheme/get-prefers-color-scheme.d.mts.map +1 -1
  43. package/dist/color-scheme/get-prefers-color-scheme.mjs +3 -1
  44. package/dist/color-scheme/get-prefers-color-scheme.mjs.map +1 -1
  45. package/dist/color-scheme/observe-prefers-color-scheme.cjs.map +1 -1
  46. package/dist/color-scheme/observe-prefers-color-scheme.d.cts +4 -1
  47. package/dist/color-scheme/observe-prefers-color-scheme.d.cts.map +1 -1
  48. package/dist/color-scheme/observe-prefers-color-scheme.d.mts +4 -1
  49. package/dist/color-scheme/observe-prefers-color-scheme.d.mts.map +1 -1
  50. package/dist/color-scheme/observe-prefers-color-scheme.mjs.map +1 -1
  51. package/dist/index.d.cts +2 -1
  52. package/dist/index.d.mts +2 -1
  53. package/dist/react/hooks/use-attribute.cjs +7 -7
  54. package/dist/react/hooks/use-attribute.cjs.map +1 -1
  55. package/dist/react/hooks/use-attribute.d.cts +4 -4
  56. package/dist/react/hooks/use-attribute.d.mts +4 -4
  57. package/dist/react/hooks/use-attribute.mjs +7 -7
  58. package/dist/react/hooks/use-attribute.mjs.map +1 -1
  59. package/dist/react/hooks/use-theme-by-class-name.cjs +1 -1
  60. package/dist/react/hooks/use-theme-by-class-name.cjs.map +1 -1
  61. package/dist/react/hooks/use-theme-by-class-name.d.cts +2 -2
  62. package/dist/react/hooks/use-theme-by-class-name.d.mts +2 -2
  63. package/dist/react/hooks/use-theme-by-class-name.mjs +1 -1
  64. package/dist/react/hooks/use-theme-by-class-name.mjs.map +1 -1
  65. package/dist/react/hooks/use-theme-by-data-attribute.cjs +1 -1
  66. package/dist/react/hooks/use-theme-by-data-attribute.cjs.map +1 -1
  67. package/dist/react/hooks/use-theme-by-data-attribute.d.cts +2 -2
  68. package/dist/react/hooks/use-theme-by-data-attribute.d.mts +2 -2
  69. package/dist/react/hooks/use-theme-by-data-attribute.mjs +1 -1
  70. package/dist/react/hooks/use-theme-by-data-attribute.mjs.map +1 -1
  71. package/dist/react/theme/create-theme-hook.cjs.map +1 -1
  72. package/dist/react/theme/create-theme-hook.mjs.map +1 -1
  73. package/dist/theme/_utils/parse-stored-theme.cjs +1 -1
  74. package/dist/theme/_utils/parse-stored-theme.cjs.map +1 -1
  75. package/dist/theme/_utils/parse-stored-theme.mjs +1 -1
  76. package/dist/theme/_utils/parse-stored-theme.mjs.map +1 -1
  77. package/dist/theme/_utils/set-theme-to-stores.cjs +1 -1
  78. package/dist/theme/_utils/set-theme-to-stores.cjs.map +1 -1
  79. package/dist/theme/_utils/set-theme-to-stores.mjs +1 -1
  80. package/dist/theme/_utils/set-theme-to-stores.mjs.map +1 -1
  81. package/dist/theme/class-name/parse-class-name.cjs +1 -1
  82. package/dist/theme/class-name/parse-class-name.cjs.map +1 -1
  83. package/dist/theme/class-name/parse-class-name.d.cts +2 -2
  84. package/dist/theme/class-name/parse-class-name.d.cts.map +1 -1
  85. package/dist/theme/class-name/parse-class-name.d.mts +2 -2
  86. package/dist/theme/class-name/parse-class-name.d.mts.map +1 -1
  87. package/dist/theme/class-name/parse-class-name.mjs +1 -1
  88. package/dist/theme/class-name/parse-class-name.mjs.map +1 -1
  89. package/dist/theme/class-name/read-class-name.cjs +1 -1
  90. package/dist/theme/class-name/read-class-name.cjs.map +1 -1
  91. package/dist/theme/class-name/read-class-name.d.cts +2 -2
  92. package/dist/theme/class-name/read-class-name.d.cts.map +1 -1
  93. package/dist/theme/class-name/read-class-name.d.mts +2 -2
  94. package/dist/theme/class-name/read-class-name.d.mts.map +1 -1
  95. package/dist/theme/class-name/read-class-name.mjs +1 -1
  96. package/dist/theme/class-name/read-class-name.mjs.map +1 -1
  97. package/dist/theme/class-name/stringify-class-name.cjs +4 -4
  98. package/dist/theme/class-name/stringify-class-name.cjs.map +1 -1
  99. package/dist/theme/class-name/stringify-class-name.d.cts +3 -3
  100. package/dist/theme/class-name/stringify-class-name.d.cts.map +1 -1
  101. package/dist/theme/class-name/stringify-class-name.d.mts +3 -3
  102. package/dist/theme/class-name/stringify-class-name.d.mts.map +1 -1
  103. package/dist/theme/class-name/stringify-class-name.mjs +4 -4
  104. package/dist/theme/class-name/stringify-class-name.mjs.map +1 -1
  105. package/dist/theme/class-name/subscribe-class-name.cjs +1 -1
  106. package/dist/theme/class-name/subscribe-class-name.cjs.map +1 -1
  107. package/dist/theme/class-name/subscribe-class-name.d.cts +2 -2
  108. package/dist/theme/class-name/subscribe-class-name.d.cts.map +1 -1
  109. package/dist/theme/class-name/subscribe-class-name.d.mts +2 -2
  110. package/dist/theme/class-name/subscribe-class-name.d.mts.map +1 -1
  111. package/dist/theme/class-name/subscribe-class-name.mjs +1 -1
  112. package/dist/theme/class-name/subscribe-class-name.mjs.map +1 -1
  113. package/dist/theme/class-name/write-class-name.cjs +2 -2
  114. package/dist/theme/class-name/write-class-name.cjs.map +1 -1
  115. package/dist/theme/class-name/write-class-name.d.cts +4 -4
  116. package/dist/theme/class-name/write-class-name.d.cts.map +1 -1
  117. package/dist/theme/class-name/write-class-name.d.mts +4 -4
  118. package/dist/theme/class-name/write-class-name.d.mts.map +1 -1
  119. package/dist/theme/class-name/write-class-name.mjs +2 -2
  120. package/dist/theme/class-name/write-class-name.mjs.map +1 -1
  121. package/dist/theme/compose-theme-stores.cjs.map +1 -1
  122. package/dist/theme/compose-theme-stores.mjs.map +1 -1
  123. package/dist/theme/cookie/write-cookie-theme.cjs +2 -2
  124. package/dist/theme/cookie/write-cookie-theme.cjs.map +1 -1
  125. package/dist/theme/cookie/write-cookie-theme.d.cts +2 -2
  126. package/dist/theme/cookie/write-cookie-theme.d.cts.map +1 -1
  127. package/dist/theme/cookie/write-cookie-theme.d.mts +2 -2
  128. package/dist/theme/cookie/write-cookie-theme.d.mts.map +1 -1
  129. package/dist/theme/cookie/write-cookie-theme.mjs +2 -2
  130. package/dist/theme/cookie/write-cookie-theme.mjs.map +1 -1
  131. package/dist/theme/data-attribute/parse-data-attribute.cjs +1 -1
  132. package/dist/theme/data-attribute/parse-data-attribute.cjs.map +1 -1
  133. package/dist/theme/data-attribute/parse-data-attribute.d.cts +2 -2
  134. package/dist/theme/data-attribute/parse-data-attribute.d.mts +2 -2
  135. package/dist/theme/data-attribute/parse-data-attribute.mjs +1 -1
  136. package/dist/theme/data-attribute/parse-data-attribute.mjs.map +1 -1
  137. package/dist/theme/data-attribute/read-data-attribute.cjs +1 -1
  138. package/dist/theme/data-attribute/read-data-attribute.cjs.map +1 -1
  139. package/dist/theme/data-attribute/read-data-attribute.d.cts +2 -2
  140. package/dist/theme/data-attribute/read-data-attribute.d.cts.map +1 -1
  141. package/dist/theme/data-attribute/read-data-attribute.d.mts +2 -2
  142. package/dist/theme/data-attribute/read-data-attribute.d.mts.map +1 -1
  143. package/dist/theme/data-attribute/read-data-attribute.mjs +1 -1
  144. package/dist/theme/data-attribute/read-data-attribute.mjs.map +1 -1
  145. package/dist/theme/data-attribute/stringify-data-attribute.cjs +4 -4
  146. package/dist/theme/data-attribute/stringify-data-attribute.cjs.map +1 -1
  147. package/dist/theme/data-attribute/stringify-data-attribute.d.cts +3 -3
  148. package/dist/theme/data-attribute/stringify-data-attribute.d.cts.map +1 -1
  149. package/dist/theme/data-attribute/stringify-data-attribute.d.mts +3 -3
  150. package/dist/theme/data-attribute/stringify-data-attribute.d.mts.map +1 -1
  151. package/dist/theme/data-attribute/stringify-data-attribute.mjs +4 -4
  152. package/dist/theme/data-attribute/stringify-data-attribute.mjs.map +1 -1
  153. package/dist/theme/data-attribute/subscribe-data-attribute.cjs +1 -1
  154. package/dist/theme/data-attribute/subscribe-data-attribute.cjs.map +1 -1
  155. package/dist/theme/data-attribute/subscribe-data-attribute.d.cts +2 -2
  156. package/dist/theme/data-attribute/subscribe-data-attribute.d.cts.map +1 -1
  157. package/dist/theme/data-attribute/subscribe-data-attribute.d.mts +2 -2
  158. package/dist/theme/data-attribute/subscribe-data-attribute.d.mts.map +1 -1
  159. package/dist/theme/data-attribute/subscribe-data-attribute.mjs +1 -1
  160. package/dist/theme/data-attribute/subscribe-data-attribute.mjs.map +1 -1
  161. package/dist/theme/data-attribute/write-data-attribute.cjs +3 -3
  162. package/dist/theme/data-attribute/write-data-attribute.cjs.map +1 -1
  163. package/dist/theme/data-attribute/write-data-attribute.d.cts +4 -4
  164. package/dist/theme/data-attribute/write-data-attribute.d.cts.map +1 -1
  165. package/dist/theme/data-attribute/write-data-attribute.d.mts +4 -4
  166. package/dist/theme/data-attribute/write-data-attribute.d.mts.map +1 -1
  167. package/dist/theme/data-attribute/write-data-attribute.mjs +3 -3
  168. package/dist/theme/data-attribute/write-data-attribute.mjs.map +1 -1
  169. package/dist/theme/local-storage/write-local-storage.cjs +1 -1
  170. package/dist/theme/local-storage/write-local-storage.cjs.map +1 -1
  171. package/dist/theme/local-storage/write-local-storage.d.cts +2 -2
  172. package/dist/theme/local-storage/write-local-storage.d.mts +2 -2
  173. package/dist/theme/local-storage/write-local-storage.mjs +1 -1
  174. package/dist/theme/local-storage/write-local-storage.mjs.map +1 -1
  175. package/dist/theme/session-storage/write-session-storage.cjs +1 -1
  176. package/dist/theme/session-storage/write-session-storage.cjs.map +1 -1
  177. package/dist/theme/session-storage/write-session-storage.d.cts +2 -2
  178. package/dist/theme/session-storage/write-session-storage.d.mts +2 -2
  179. package/dist/theme/session-storage/write-session-storage.mjs +1 -1
  180. package/dist/theme/session-storage/write-session-storage.mjs.map +1 -1
  181. package/dist/theme/theme-entry.types.d.cts +4 -2
  182. package/dist/theme/theme-entry.types.d.cts.map +1 -1
  183. package/dist/theme/theme-entry.types.d.mts +4 -2
  184. package/dist/theme/theme-entry.types.d.mts.map +1 -1
  185. package/dist/theme/theme-store/async-theme-store.types.d.cts +1 -1
  186. package/dist/theme/theme-store/async-theme-store.types.d.cts.map +1 -1
  187. package/dist/theme/theme-store/async-theme-store.types.d.mts +1 -1
  188. package/dist/theme/theme-store/async-theme-store.types.d.mts.map +1 -1
  189. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.cjs +1 -1
  190. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.cjs.map +1 -1
  191. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.d.cts +2 -2
  192. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.d.mts +2 -2
  193. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.mjs +1 -1
  194. package/dist/theme/theme-store/class-name-theme-store/class-name-theme-store.mjs.map +1 -1
  195. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.cjs +1 -1
  196. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.cjs.map +1 -1
  197. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.d.cts +3 -3
  198. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.d.mts +3 -3
  199. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.mjs +1 -1
  200. package/dist/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.mjs.map +1 -1
  201. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.cjs +1 -1
  202. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.cjs.map +1 -1
  203. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.d.cts +1 -1
  204. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.d.mts +1 -1
  205. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.mjs +1 -1
  206. package/dist/theme/theme-store/in-memory-theme-store/in-memory-theme-store.mjs.map +1 -1
  207. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.d.cts +1 -1
  208. package/dist/theme/theme-store/local-storage-theme-store/local-storage-theme-store.d.mts +1 -1
  209. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.d.cts +1 -1
  210. package/dist/theme/theme-store/session-storage-theme-store/session-storage-theme-store.d.mts +1 -1
  211. package/dist/theme/theme-store/theme-store.types.d.cts +1 -1
  212. package/dist/theme/theme-store/theme-store.types.d.cts.map +1 -1
  213. package/dist/theme/theme-store/theme-store.types.d.mts +1 -1
  214. package/dist/theme/theme-store/theme-store.types.d.mts.map +1 -1
  215. package/dist/theme/web-storage/write-web-storage.cjs +3 -3
  216. package/dist/theme/web-storage/write-web-storage.cjs.map +1 -1
  217. package/dist/theme/web-storage/write-web-storage.d.cts +2 -2
  218. package/dist/theme/web-storage/write-web-storage.d.mts +2 -2
  219. package/dist/theme/web-storage/write-web-storage.mjs +3 -3
  220. package/dist/theme/web-storage/write-web-storage.mjs.map +1 -1
  221. package/dist/utils/append-id.cjs +2 -2
  222. package/dist/utils/append-id.cjs.map +1 -1
  223. package/dist/utils/append-id.d.cts +3 -3
  224. package/dist/utils/append-id.d.mts +3 -3
  225. package/dist/utils/append-id.mjs +2 -2
  226. package/dist/utils/append-id.mjs.map +1 -1
  227. package/package.json +1 -1
  228. package/src/attributes/get-attribute.ts +5 -2
  229. package/src/attributes/get-data-attribute.ts +5 -2
  230. package/src/attributes/observe-attribute.ts +2 -2
  231. package/src/attributes/observe-data-attribute.ts +2 -2
  232. package/src/color-scheme/color-scheme.types.ts +7 -0
  233. package/src/color-scheme/get-prefers-color-scheme.ts +6 -4
  234. package/src/color-scheme/observe-prefers-color-scheme.ts +3 -1
  235. package/src/index.ts +1 -0
  236. package/src/react/hooks/use-attribute.ts +11 -11
  237. package/src/react/hooks/use-theme-by-class-name.ts +2 -2
  238. package/src/react/hooks/use-theme-by-data-attribute.ts +2 -2
  239. package/src/react/theme/create-theme-hook.ts +4 -6
  240. package/src/theme/_utils/parse-stored-theme.ts +2 -2
  241. package/src/theme/_utils/set-theme-to-stores.ts +3 -3
  242. package/src/theme/class-name/parse-class-name.ts +2 -2
  243. package/src/theme/class-name/read-class-name.ts +2 -2
  244. package/src/theme/class-name/stringify-class-name.ts +6 -6
  245. package/src/theme/class-name/subscribe-class-name.ts +2 -2
  246. package/src/theme/class-name/write-class-name.ts +4 -4
  247. package/src/theme/compose-theme-stores.ts +1 -1
  248. package/src/theme/cookie/write-cookie-theme.ts +3 -3
  249. package/src/theme/data-attribute/parse-data-attribute.ts +2 -2
  250. package/src/theme/data-attribute/read-data-attribute.ts +2 -2
  251. package/src/theme/data-attribute/stringify-data-attribute.ts +6 -6
  252. package/src/theme/data-attribute/subscribe-data-attribute.ts +2 -2
  253. package/src/theme/data-attribute/write-data-attribute.ts +5 -5
  254. package/src/theme/local-storage/write-local-storage.ts +2 -2
  255. package/src/theme/session-storage/write-session-storage.ts +2 -2
  256. package/src/theme/theme-entry.types.ts +5 -3
  257. package/src/theme/theme-store/async-theme-store.types.ts +1 -3
  258. package/src/theme/theme-store/class-name-theme-store/class-name-theme-store.ts +2 -2
  259. package/src/theme/theme-store/data-attribute-theme-store/data-attribute-theme-store.ts +2 -2
  260. package/src/theme/theme-store/in-memory-theme-store/in-memory-theme-store.ts +1 -1
  261. package/src/theme/theme-store/theme-store.types.ts +1 -3
  262. package/src/theme/web-storage/write-web-storage.ts +5 -5
  263. package/src/utils/append-id.ts +3 -3
@@ -1,7 +1,7 @@
1
1
 
2
2
  //#region src/theme/web-storage/write-web-storage.ts
3
3
  function defaultStringify(_themes, _existing, entry) {
4
- return entry === void 0 ? "" : JSON.stringify(entry);
4
+ return entry == null ? "" : JSON.stringify(entry);
5
5
  }
6
6
  /**
7
7
  * Writes a theme entry to a web storage (localStorage or sessionStorage).
@@ -10,7 +10,7 @@ function defaultStringify(_themes, _existing, entry) {
10
10
  *
11
11
  * @param themes - Record mapping theme keys to values (used by stringify)
12
12
  * @param storageKey - Storage key to write to
13
- * @param entry - Theme entry to write, or undefined to remove
13
+ * @param entry - Theme entry to write, or null/undefined to remove
14
14
  * @param options.storage - Storage object (localStorage or sessionStorage)
15
15
  * @param options.stringify - Custom serializer (default: JSON.stringify)
16
16
  * @param options.onError - Optional callback invoked when setItem/removeItem throws
@@ -18,7 +18,7 @@ function defaultStringify(_themes, _existing, entry) {
18
18
  function writeWebStorage(themes, storageKey, entry, options) {
19
19
  const { storage, stringify = defaultStringify, onError } = options;
20
20
  try {
21
- if (entry === void 0) storage.removeItem(storageKey);
21
+ if (entry == null) storage.removeItem(storageKey);
22
22
  else {
23
23
  const value = stringify(themes, storage.getItem(storageKey) ?? void 0, entry);
24
24
  storage.setItem(storageKey, value);
@@ -1 +1 @@
1
- {"version":3,"file":"write-web-storage.cjs","names":[],"sources":["../../../src/theme/web-storage/write-web-storage.ts"],"sourcesContent":["import type { StringifyStoredTheme, ThemeEntry } from '../theme-entry.types.ts'\nimport type { ThemeMap } from '../theme-map.types.ts'\n\nfunction defaultStringify<Themes extends ThemeMap>(\n\t_themes: Themes,\n\t_existing: string | undefined,\n\tentry: ThemeEntry<Themes> | undefined\n): string {\n\treturn entry === undefined ? '' : JSON.stringify(entry)\n}\n\n/**\n * Writes a theme entry to a web storage (localStorage or sessionStorage).\n *\n * Performs setItem/removeItem only. Does not notify subscribers; the store must call notify() after this.\n *\n * @param themes - Record mapping theme keys to values (used by stringify)\n * @param storageKey - Storage key to write to\n * @param entry - Theme entry to write, or undefined to remove\n * @param options.storage - Storage object (localStorage or sessionStorage)\n * @param options.stringify - Custom serializer (default: JSON.stringify)\n * @param options.onError - Optional callback invoked when setItem/removeItem throws\n */\nexport function writeWebStorage<Themes extends ThemeMap>(\n\tthemes: Themes,\n\tstorageKey: string,\n\tentry: ThemeEntry<Themes> | undefined,\n\toptions: {\n\t\tstorage: Storage\n\t\tstringify?: StringifyStoredTheme<Themes> | undefined\n\t\tonError?: ((error: unknown) => void) | undefined\n\t}\n): void {\n\tconst { storage, stringify = defaultStringify, onError } = options\n\ttry {\n\t\tif (entry === undefined) {\n\t\t\tstorage.removeItem(storageKey)\n\t\t} else {\n\t\t\tconst existing = storage.getItem(storageKey) ?? undefined\n\t\t\tconst value = stringify(themes, existing, entry)\n\t\t\tstorage.setItem(storageKey, value)\n\t\t}\n\t} catch (error) {\n\t\tonError?.(error)\n\t}\n}\n"],"mappings":";;AAGA,SAAS,iBACR,SACA,WACA,OACS;AACT,QAAO,UAAU,SAAY,KAAK,KAAK,UAAU,MAAM;;;;;;;;;;;;;;AAexD,SAAgB,gBACf,QACA,YACA,OACA,SAKO;CACP,MAAM,EAAE,SAAS,YAAY,kBAAkB,YAAY;AAC3D,KAAI;AACH,MAAI,UAAU,OACb,SAAQ,WAAW,WAAW;OACxB;GAEN,MAAM,QAAQ,UAAU,QADP,QAAQ,QAAQ,WAAW,IAAI,QACN,MAAM;AAChD,WAAQ,QAAQ,YAAY,MAAM;;UAE3B,OAAO;AACf,YAAU,MAAM"}
1
+ {"version":3,"file":"write-web-storage.cjs","names":[],"sources":["../../../src/theme/web-storage/write-web-storage.ts"],"sourcesContent":["import type { StringifyStoredTheme, ThemeEntry } from '../theme-entry.types.ts'\nimport type { ThemeMap } from '../theme-map.types.ts'\n\nfunction defaultStringify<Themes extends ThemeMap>(\n\t_themes: Themes,\n\t_existing: string | undefined,\n\tentry: ThemeEntry<Themes> | null | undefined\n): string {\n\treturn entry == null ? '' : JSON.stringify(entry)\n}\n\n/**\n * Writes a theme entry to a web storage (localStorage or sessionStorage).\n *\n * Performs setItem/removeItem only. Does not notify subscribers; the store must call notify() after this.\n *\n * @param themes - Record mapping theme keys to values (used by stringify)\n * @param storageKey - Storage key to write to\n * @param entry - Theme entry to write, or null/undefined to remove\n * @param options.storage - Storage object (localStorage or sessionStorage)\n * @param options.stringify - Custom serializer (default: JSON.stringify)\n * @param options.onError - Optional callback invoked when setItem/removeItem throws\n */\nexport function writeWebStorage<Themes extends ThemeMap>(\n\tthemes: Themes,\n\tstorageKey: string,\n\tentry: ThemeEntry<Themes> | null | undefined,\n\toptions: {\n\t\tstorage: Storage\n\t\tstringify?: StringifyStoredTheme<Themes> | undefined\n\t\tonError?: ((error: unknown) => void) | undefined\n\t}\n): void {\n\tconst { storage, stringify = defaultStringify, onError } = options\n\ttry {\n\t\tif (entry == null) {\n\t\t\tstorage.removeItem(storageKey)\n\t\t} else {\n\t\t\tconst existing = storage.getItem(storageKey) ?? undefined\n\t\t\tconst value = stringify(themes, existing, entry)\n\t\t\tstorage.setItem(storageKey, value)\n\t\t}\n\t} catch (error) {\n\t\tonError?.(error)\n\t}\n}\n"],"mappings":";;AAGA,SAAS,iBACR,SACA,WACA,OACS;AACT,QAAO,SAAS,OAAO,KAAK,KAAK,UAAU,MAAM;;;;;;;;;;;;;;AAelD,SAAgB,gBACf,QACA,YACA,OACA,SAKO;CACP,MAAM,EAAE,SAAS,YAAY,kBAAkB,YAAY;AAC3D,KAAI;AACH,MAAI,SAAS,KACZ,SAAQ,WAAW,WAAW;OACxB;GAEN,MAAM,QAAQ,UAAU,QADP,QAAQ,QAAQ,WAAW,IAAI,QACN,MAAM;AAChD,WAAQ,QAAQ,YAAY,MAAM;;UAE3B,OAAO;AACf,YAAU,MAAM"}
@@ -10,12 +10,12 @@ import { StringifyStoredTheme, ThemeEntry } from "../theme-entry.types.cjs";
10
10
  *
11
11
  * @param themes - Record mapping theme keys to values (used by stringify)
12
12
  * @param storageKey - Storage key to write to
13
- * @param entry - Theme entry to write, or undefined to remove
13
+ * @param entry - Theme entry to write, or null/undefined to remove
14
14
  * @param options.storage - Storage object (localStorage or sessionStorage)
15
15
  * @param options.stringify - Custom serializer (default: JSON.stringify)
16
16
  * @param options.onError - Optional callback invoked when setItem/removeItem throws
17
17
  */
18
- declare function writeWebStorage<Themes extends ThemeMap>(themes: Themes, storageKey: string, entry: ThemeEntry<Themes> | undefined, options: {
18
+ declare function writeWebStorage<Themes extends ThemeMap>(themes: Themes, storageKey: string, entry: ThemeEntry<Themes> | null | undefined, options: {
19
19
  storage: Storage;
20
20
  stringify?: StringifyStoredTheme<Themes> | undefined;
21
21
  onError?: ((error: unknown) => void) | undefined;
@@ -10,12 +10,12 @@ import { StringifyStoredTheme, ThemeEntry } from "../theme-entry.types.mjs";
10
10
  *
11
11
  * @param themes - Record mapping theme keys to values (used by stringify)
12
12
  * @param storageKey - Storage key to write to
13
- * @param entry - Theme entry to write, or undefined to remove
13
+ * @param entry - Theme entry to write, or null/undefined to remove
14
14
  * @param options.storage - Storage object (localStorage or sessionStorage)
15
15
  * @param options.stringify - Custom serializer (default: JSON.stringify)
16
16
  * @param options.onError - Optional callback invoked when setItem/removeItem throws
17
17
  */
18
- declare function writeWebStorage<Themes extends ThemeMap>(themes: Themes, storageKey: string, entry: ThemeEntry<Themes> | undefined, options: {
18
+ declare function writeWebStorage<Themes extends ThemeMap>(themes: Themes, storageKey: string, entry: ThemeEntry<Themes> | null | undefined, options: {
19
19
  storage: Storage;
20
20
  stringify?: StringifyStoredTheme<Themes> | undefined;
21
21
  onError?: ((error: unknown) => void) | undefined;
@@ -1,6 +1,6 @@
1
1
  //#region src/theme/web-storage/write-web-storage.ts
2
2
  function defaultStringify(_themes, _existing, entry) {
3
- return entry === void 0 ? "" : JSON.stringify(entry);
3
+ return entry == null ? "" : JSON.stringify(entry);
4
4
  }
5
5
  /**
6
6
  * Writes a theme entry to a web storage (localStorage or sessionStorage).
@@ -9,7 +9,7 @@ function defaultStringify(_themes, _existing, entry) {
9
9
  *
10
10
  * @param themes - Record mapping theme keys to values (used by stringify)
11
11
  * @param storageKey - Storage key to write to
12
- * @param entry - Theme entry to write, or undefined to remove
12
+ * @param entry - Theme entry to write, or null/undefined to remove
13
13
  * @param options.storage - Storage object (localStorage or sessionStorage)
14
14
  * @param options.stringify - Custom serializer (default: JSON.stringify)
15
15
  * @param options.onError - Optional callback invoked when setItem/removeItem throws
@@ -17,7 +17,7 @@ function defaultStringify(_themes, _existing, entry) {
17
17
  function writeWebStorage(themes, storageKey, entry, options) {
18
18
  const { storage, stringify = defaultStringify, onError } = options;
19
19
  try {
20
- if (entry === void 0) storage.removeItem(storageKey);
20
+ if (entry == null) storage.removeItem(storageKey);
21
21
  else {
22
22
  const value = stringify(themes, storage.getItem(storageKey) ?? void 0, entry);
23
23
  storage.setItem(storageKey, value);
@@ -1 +1 @@
1
- {"version":3,"file":"write-web-storage.mjs","names":[],"sources":["../../../src/theme/web-storage/write-web-storage.ts"],"sourcesContent":["import type { StringifyStoredTheme, ThemeEntry } from '../theme-entry.types.ts'\nimport type { ThemeMap } from '../theme-map.types.ts'\n\nfunction defaultStringify<Themes extends ThemeMap>(\n\t_themes: Themes,\n\t_existing: string | undefined,\n\tentry: ThemeEntry<Themes> | undefined\n): string {\n\treturn entry === undefined ? '' : JSON.stringify(entry)\n}\n\n/**\n * Writes a theme entry to a web storage (localStorage or sessionStorage).\n *\n * Performs setItem/removeItem only. Does not notify subscribers; the store must call notify() after this.\n *\n * @param themes - Record mapping theme keys to values (used by stringify)\n * @param storageKey - Storage key to write to\n * @param entry - Theme entry to write, or undefined to remove\n * @param options.storage - Storage object (localStorage or sessionStorage)\n * @param options.stringify - Custom serializer (default: JSON.stringify)\n * @param options.onError - Optional callback invoked when setItem/removeItem throws\n */\nexport function writeWebStorage<Themes extends ThemeMap>(\n\tthemes: Themes,\n\tstorageKey: string,\n\tentry: ThemeEntry<Themes> | undefined,\n\toptions: {\n\t\tstorage: Storage\n\t\tstringify?: StringifyStoredTheme<Themes> | undefined\n\t\tonError?: ((error: unknown) => void) | undefined\n\t}\n): void {\n\tconst { storage, stringify = defaultStringify, onError } = options\n\ttry {\n\t\tif (entry === undefined) {\n\t\t\tstorage.removeItem(storageKey)\n\t\t} else {\n\t\t\tconst existing = storage.getItem(storageKey) ?? undefined\n\t\t\tconst value = stringify(themes, existing, entry)\n\t\t\tstorage.setItem(storageKey, value)\n\t\t}\n\t} catch (error) {\n\t\tonError?.(error)\n\t}\n}\n"],"mappings":";AAGA,SAAS,iBACR,SACA,WACA,OACS;AACT,QAAO,UAAU,SAAY,KAAK,KAAK,UAAU,MAAM;;;;;;;;;;;;;;AAexD,SAAgB,gBACf,QACA,YACA,OACA,SAKO;CACP,MAAM,EAAE,SAAS,YAAY,kBAAkB,YAAY;AAC3D,KAAI;AACH,MAAI,UAAU,OACb,SAAQ,WAAW,WAAW;OACxB;GAEN,MAAM,QAAQ,UAAU,QADP,QAAQ,QAAQ,WAAW,IAAI,QACN,MAAM;AAChD,WAAQ,QAAQ,YAAY,MAAM;;UAE3B,OAAO;AACf,YAAU,MAAM"}
1
+ {"version":3,"file":"write-web-storage.mjs","names":[],"sources":["../../../src/theme/web-storage/write-web-storage.ts"],"sourcesContent":["import type { StringifyStoredTheme, ThemeEntry } from '../theme-entry.types.ts'\nimport type { ThemeMap } from '../theme-map.types.ts'\n\nfunction defaultStringify<Themes extends ThemeMap>(\n\t_themes: Themes,\n\t_existing: string | undefined,\n\tentry: ThemeEntry<Themes> | null | undefined\n): string {\n\treturn entry == null ? '' : JSON.stringify(entry)\n}\n\n/**\n * Writes a theme entry to a web storage (localStorage or sessionStorage).\n *\n * Performs setItem/removeItem only. Does not notify subscribers; the store must call notify() after this.\n *\n * @param themes - Record mapping theme keys to values (used by stringify)\n * @param storageKey - Storage key to write to\n * @param entry - Theme entry to write, or null/undefined to remove\n * @param options.storage - Storage object (localStorage or sessionStorage)\n * @param options.stringify - Custom serializer (default: JSON.stringify)\n * @param options.onError - Optional callback invoked when setItem/removeItem throws\n */\nexport function writeWebStorage<Themes extends ThemeMap>(\n\tthemes: Themes,\n\tstorageKey: string,\n\tentry: ThemeEntry<Themes> | null | undefined,\n\toptions: {\n\t\tstorage: Storage\n\t\tstringify?: StringifyStoredTheme<Themes> | undefined\n\t\tonError?: ((error: unknown) => void) | undefined\n\t}\n): void {\n\tconst { storage, stringify = defaultStringify, onError } = options\n\ttry {\n\t\tif (entry == null) {\n\t\t\tstorage.removeItem(storageKey)\n\t\t} else {\n\t\t\tconst existing = storage.getItem(storageKey) ?? undefined\n\t\t\tconst value = stringify(themes, existing, entry)\n\t\t\tstorage.setItem(storageKey, value)\n\t\t}\n\t} catch (error) {\n\t\tonError?.(error)\n\t}\n}\n"],"mappings":";AAGA,SAAS,iBACR,SACA,WACA,OACS;AACT,QAAO,SAAS,OAAO,KAAK,KAAK,UAAU,MAAM;;;;;;;;;;;;;;AAelD,SAAgB,gBACf,QACA,YACA,OACA,SAKO;CACP,MAAM,EAAE,SAAS,YAAY,kBAAkB,YAAY;AAC3D,KAAI;AACH,MAAI,SAAS,KACZ,SAAQ,WAAW,WAAW;OACxB;GAEN,MAAM,QAAQ,UAAU,QADP,QAAQ,QAAQ,WAAW,IAAI,QACN,MAAM;AAChD,WAAQ,QAAQ,YAAY,MAAM;;UAE3B,OAAO;AACf,YAAU,MAAM"}
@@ -3,9 +3,9 @@
3
3
  /**
4
4
  * Appends a suffix to an ID if the ID is defined.
5
5
  *
6
- * @param id - The ID to append the suffix to.
6
+ * @param id - The ID to append the suffix to (accepts null e.g. from getAttribute).
7
7
  * @param suffix - The suffix to append to the ID.
8
- * @returns The ID with the suffix appended, or undefined if the ID is undefined.
8
+ * @returns The ID with the suffix appended, or undefined if the ID is null/undefined.
9
9
  */
10
10
  function appendId(id, suffix) {
11
11
  return id ? `${id}-${suffix}` : void 0;
@@ -1 +1 @@
1
- {"version":3,"file":"append-id.cjs","names":[],"sources":["../../src/utils/append-id.ts"],"sourcesContent":["/**\n * Appends a suffix to an ID if the ID is defined.\n *\n * @param id - The ID to append the suffix to.\n * @param suffix - The suffix to append to the ID.\n * @returns The ID with the suffix appended, or undefined if the ID is undefined.\n */\nexport function appendId(id: string | undefined, suffix: string): string | undefined {\n\treturn id ? `${id}-${suffix}` : undefined\n}\n"],"mappings":";;;;;;;;;AAOA,SAAgB,SAAS,IAAwB,QAAoC;AACpF,QAAO,KAAK,GAAG,GAAG,GAAG,WAAW"}
1
+ {"version":3,"file":"append-id.cjs","names":[],"sources":["../../src/utils/append-id.ts"],"sourcesContent":["/**\n * Appends a suffix to an ID if the ID is defined.\n *\n * @param id - The ID to append the suffix to (accepts null e.g. from getAttribute).\n * @param suffix - The suffix to append to the ID.\n * @returns The ID with the suffix appended, or undefined if the ID is null/undefined.\n */\nexport function appendId(id: string | null | undefined, suffix: string): string | undefined {\n\treturn id ? `${id}-${suffix}` : undefined\n}\n"],"mappings":";;;;;;;;;AAOA,SAAgB,SAAS,IAA+B,QAAoC;AAC3F,QAAO,KAAK,GAAG,GAAG,GAAG,WAAW"}
@@ -2,11 +2,11 @@
2
2
  /**
3
3
  * Appends a suffix to an ID if the ID is defined.
4
4
  *
5
- * @param id - The ID to append the suffix to.
5
+ * @param id - The ID to append the suffix to (accepts null e.g. from getAttribute).
6
6
  * @param suffix - The suffix to append to the ID.
7
- * @returns The ID with the suffix appended, or undefined if the ID is undefined.
7
+ * @returns The ID with the suffix appended, or undefined if the ID is null/undefined.
8
8
  */
9
- declare function appendId(id: string | undefined, suffix: string): string | undefined;
9
+ declare function appendId(id: string | null | undefined, suffix: string): string | undefined;
10
10
  //#endregion
11
11
  export { appendId };
12
12
  //# sourceMappingURL=append-id.d.cts.map
@@ -2,11 +2,11 @@
2
2
  /**
3
3
  * Appends a suffix to an ID if the ID is defined.
4
4
  *
5
- * @param id - The ID to append the suffix to.
5
+ * @param id - The ID to append the suffix to (accepts null e.g. from getAttribute).
6
6
  * @param suffix - The suffix to append to the ID.
7
- * @returns The ID with the suffix appended, or undefined if the ID is undefined.
7
+ * @returns The ID with the suffix appended, or undefined if the ID is null/undefined.
8
8
  */
9
- declare function appendId(id: string | undefined, suffix: string): string | undefined;
9
+ declare function appendId(id: string | null | undefined, suffix: string): string | undefined;
10
10
  //#endregion
11
11
  export { appendId };
12
12
  //# sourceMappingURL=append-id.d.mts.map
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * Appends a suffix to an ID if the ID is defined.
4
4
  *
5
- * @param id - The ID to append the suffix to.
5
+ * @param id - The ID to append the suffix to (accepts null e.g. from getAttribute).
6
6
  * @param suffix - The suffix to append to the ID.
7
- * @returns The ID with the suffix appended, or undefined if the ID is undefined.
7
+ * @returns The ID with the suffix appended, or undefined if the ID is null/undefined.
8
8
  */
9
9
  function appendId(id, suffix) {
10
10
  return id ? `${id}-${suffix}` : void 0;
@@ -1 +1 @@
1
- {"version":3,"file":"append-id.mjs","names":[],"sources":["../../src/utils/append-id.ts"],"sourcesContent":["/**\n * Appends a suffix to an ID if the ID is defined.\n *\n * @param id - The ID to append the suffix to.\n * @param suffix - The suffix to append to the ID.\n * @returns The ID with the suffix appended, or undefined if the ID is undefined.\n */\nexport function appendId(id: string | undefined, suffix: string): string | undefined {\n\treturn id ? `${id}-${suffix}` : undefined\n}\n"],"mappings":";;;;;;;;AAOA,SAAgB,SAAS,IAAwB,QAAoC;AACpF,QAAO,KAAK,GAAG,GAAG,GAAG,WAAW"}
1
+ {"version":3,"file":"append-id.mjs","names":[],"sources":["../../src/utils/append-id.ts"],"sourcesContent":["/**\n * Appends a suffix to an ID if the ID is defined.\n *\n * @param id - The ID to append the suffix to (accepts null e.g. from getAttribute).\n * @param suffix - The suffix to append to the ID.\n * @returns The ID with the suffix appended, or undefined if the ID is null/undefined.\n */\nexport function appendId(id: string | null | undefined, suffix: string): string | undefined {\n\treturn id ? `${id}-${suffix}` : undefined\n}\n"],"mappings":";;;;;;;;AAOA,SAAgB,SAAS,IAA+B,QAAoC;AAC3F,QAAO,KAAK,GAAG,GAAG,GAAG,WAAW"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@just-web/toolkits",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Toolkits for web applications",
5
5
  "homepage": "https://github.com/justland/just-web-foundation/tree/main/libs/toolkits",
6
6
  "repository": {
@@ -2,7 +2,7 @@
2
2
  * Gets the value of an attribute from an element.
3
3
  *
4
4
  * @param qualifiedName - The name of the attribute to get
5
- * @param element - The element to get the attribute from. Defaults to `document.documentElement`
5
+ * @param element - The element to get the attribute from (accepts null e.g. from refs). Defaults to `document.documentElement`
6
6
  * @returns The attribute value cast to type T, or null if the attribute doesn't exist
7
7
  *
8
8
  * @example
@@ -14,7 +14,10 @@
14
14
  * const testId = getAttribute('data-testid', element)
15
15
  * ```
16
16
  */
17
- export function getAttribute<T extends string>(qualifiedName: T, element?: Element | undefined) {
17
+ export function getAttribute<T extends string>(
18
+ qualifiedName: T,
19
+ element?: Element | null | undefined
20
+ ) {
18
21
  element = element ?? globalThis.document.documentElement
19
22
  return element.getAttribute(qualifiedName)
20
23
  }
@@ -5,11 +5,14 @@ import { getAttribute } from './get-attribute.ts'
5
5
  * Gets the value of a data attribute from an element.
6
6
  *
7
7
  * @param qualifiedName - The name of the data attribute to get
8
- * @param element - The element to get the data attribute from. Defaults to `document.documentElement`
8
+ * @param element - The element to get the data attribute from (accepts null e.g. from refs). Defaults to `document.documentElement`
9
9
  * @returns The data attribute value, or null if the attribute doesn't exist
10
10
  *
11
11
  * @example
12
12
  */
13
- export function getDataAttribute(qualifiedName: DataAttribute, element?: Element | undefined) {
13
+ export function getDataAttribute(
14
+ qualifiedName: DataAttribute,
15
+ element?: Element | null | undefined
16
+ ) {
14
17
  return getAttribute(qualifiedName, element)
15
18
  }
@@ -2,7 +2,7 @@
2
2
  * Observes attributes changes on an element and calls corresponding handlers.
3
3
  *
4
4
  * @param handlers - An object mapping attribute names to handler functions.
5
- * @param element - The element to observe. Defaults to `document.documentElement`.
5
+ * @param element - The element to observe (accepts null e.g. from refs). Defaults to `document.documentElement`.
6
6
  * @returns {MutationObserver} The observer instance, which can be used to disconnect the observer.
7
7
  *
8
8
  * @example
@@ -18,7 +18,7 @@
18
18
  */
19
19
  export function observeAttributes<T extends string>(
20
20
  handlers: Record<string, (value: T | null) => void>,
21
- element?: Element | undefined
21
+ element?: Element | null | undefined
22
22
  ) {
23
23
  element = element ?? globalThis.document.documentElement
24
24
  const observer = new MutationObserver((mutations) => {
@@ -5,7 +5,7 @@ import { observeAttributes } from './observe-attribute.ts'
5
5
  *
6
6
  * @param options - Configuration options
7
7
  * @param options.handlers - An object mapping `data-*` attribute names to handler functions.
8
- * @param options.element - The element to observe. Defaults to `document.documentElement`
8
+ * @param options.element - The element to observe (accepts null e.g. from refs). Defaults to `document.documentElement`
9
9
  * @returns {MutationObserver} The observer instance, which can be used to disconnect the observer
10
10
  *
11
11
  * @example
@@ -23,7 +23,7 @@ import { observeAttributes } from './observe-attribute.ts'
23
23
  */
24
24
  export function observeDataAttributes<T extends string, K extends `data-${string}`>(
25
25
  handlers: Record<K, (value: T | null) => void>,
26
- element?: Element | undefined
26
+ element?: Element | null | undefined
27
27
  ) {
28
28
  return observeAttributes(handlers, element)
29
29
  }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The color scheme of the system.
3
+ *
4
+ * Per {@link https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme Media Queries Level 5 § prefers-color-scheme},
5
+ * these are the only valid values exposed by the `prefers-color-scheme` media feature.
6
+ */
7
+ export type ColorScheme = 'light' | 'dark'
@@ -1,17 +1,19 @@
1
+ import type { ColorScheme } from './color-scheme.types.ts'
2
+
1
3
  /**
2
4
  * Gets the current preferred color scheme.
3
5
  * It can only be 'light' or 'dark'.
4
6
  *
5
- * Even if the browser preference is 'auto'/'device', it will return 'light' or 'dark'.
7
+ * Per {@link https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme Media Queries Level 5},
8
+ * the `prefers-color-scheme` media feature has only two valid values. Even if the browser
9
+ * preference is 'auto'/'device', it will return 'light' or 'dark'.
6
10
  *
7
11
  * When `matchMedia` is unavailable (e.g. SSR), returns `defaultColorScheme`.
8
12
  *
9
13
  * @param defaultColorScheme - Fallback when `matchMedia` is unavailable (default: `'light'`)
10
14
  * @returns 'light' or 'dark'
11
15
  */
12
- export function getPrefersColorScheme(
13
- defaultColorScheme: 'light' | 'dark' = 'light'
14
- ): 'light' | 'dark' {
16
+ export function getPrefersColorScheme(defaultColorScheme: ColorScheme = 'light'): ColorScheme {
15
17
  if (typeof matchMedia === 'undefined') return defaultColorScheme
16
18
  return matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark'
17
19
  }
@@ -1,3 +1,5 @@
1
+ import type { ColorScheme } from './color-scheme.types.ts'
2
+
1
3
  /**
2
4
  * Observes system color scheme preference changes and calls handlers when they occur.
3
5
  *
@@ -13,7 +15,7 @@
13
15
  * cleanup()
14
16
  * ```
15
17
  */
16
- export function observePrefersColorScheme(handler: (value: 'light' | 'dark') => void) {
18
+ export function observePrefersColorScheme(handler: (value: ColorScheme) => void) {
17
19
  const m = globalThis.matchMedia('(prefers-color-scheme: light)')
18
20
  const listener = (event: MediaQueryListEvent) => {
19
21
  handler(event.matches ? 'light' : 'dark')
package/src/index.ts CHANGED
@@ -9,6 +9,7 @@ export * from './class-name/class-name-props.ts'
9
9
  export * from './class-name/clsx.ts'
10
10
  export * from './class-name/just-class-name.ts'
11
11
  export * from './class-name/resolve-class-name.ts'
12
+ export * from './color-scheme/color-scheme.types.ts'
12
13
  export * from './color-scheme/get-prefers-color-scheme.ts'
13
14
  export * from './color-scheme/observe-prefers-color-scheme.ts'
14
15
  export * from './style/css-properties.ts'
@@ -6,36 +6,36 @@ import { observeAttributes } from '../../attributes/observe-attribute.ts'
6
6
  * and a setter to update it. Stays in sync when the attribute changes (e.g. from elsewhere).
7
7
  *
8
8
  * @param attributeName - The attribute to observe (e.g. `'class'`, `'data-theme'`).
9
- * @param element - The element to observe. Defaults to `document.documentElement` when omitted.
10
- * @returns Tuple of [value, setValue]. Pass null to setValue to remove the attribute.
9
+ * @param element - The element to observe (accepts null e.g. from refs). Defaults to `document.documentElement` when omitted.
10
+ * @returns Tuple of [value, setValue]. Pass null or undefined to setValue to remove the attribute.
11
11
  *
12
12
  * @example
13
13
  * ```tsx
14
14
  * const [className, setClassName] = useAttribute('class')
15
15
  * const [theme, setTheme] = useAttribute('data-theme', myElement)
16
16
  * setTheme('dark')
17
- * setClassName(null) // removes class attribute
17
+ * setClassName(undefined) // removes class attribute
18
18
  * ```
19
19
  */
20
20
  export function useAttribute(
21
21
  attributeName: string,
22
- element: Element | undefined = typeof document !== 'undefined'
22
+ element: Element | null | undefined = typeof document !== 'undefined'
23
23
  ? document.documentElement
24
24
  : undefined
25
- ): [string | null, (value: string | null) => void] {
26
- const [value, setValueState] = useState<string | null>(
27
- () => element?.getAttribute(attributeName) ?? null
25
+ ): [string | undefined, (value: string | null | undefined) => void] {
26
+ const [value, setValueState] = useState<string | undefined>(
27
+ () => element?.getAttribute(attributeName) ?? undefined
28
28
  )
29
29
 
30
30
  useEffect(() => {
31
31
  if (!element) return
32
32
 
33
- setValueState(element.getAttribute(attributeName))
33
+ setValueState(element.getAttribute(attributeName) ?? undefined)
34
34
 
35
35
  const observer = observeAttributes(
36
36
  {
37
37
  [attributeName]: (next) => {
38
- setValueState(next)
38
+ setValueState(next ?? undefined)
39
39
  }
40
40
  },
41
41
  element
@@ -44,9 +44,9 @@ export function useAttribute(
44
44
  }, [element, attributeName])
45
45
 
46
46
  const setValue = useCallback(
47
- (next: string | null) => {
47
+ (next: string | null | undefined) => {
48
48
  if (!element) return
49
- if (next === null) {
49
+ if (next == null) {
50
50
  element.removeAttribute(attributeName)
51
51
  } else {
52
52
  element.setAttribute(attributeName, next)
@@ -11,7 +11,7 @@ import { classNameThemeStore } from '../../theme/theme-store/class-name-theme-st
11
11
  *
12
12
  * @param themes - Record mapping theme keys to their class name values
13
13
  * @param options.theme - Fallback theme key when no matching class is found
14
- * @param options.element - Element to read/set theme on (defaults to document.documentElement)
14
+ * @param options.element - Element to read/set theme on (accepts null e.g. from refs). Defaults to document.documentElement.
15
15
  * @returns Tuple of [currentTheme, setTheme]
16
16
  *
17
17
  * @example
@@ -32,7 +32,7 @@ export function useThemeByClassName<Themes extends ThemeMap>(
32
32
  themes: Themes,
33
33
  options?: {
34
34
  defaultTheme?: keyof Themes | undefined
35
- element?: Element | undefined
35
+ element?: Element | null | undefined
36
36
  }
37
37
  ): [keyof Themes | undefined, (theme: keyof Themes) => void] {
38
38
  const element =
@@ -12,7 +12,7 @@ import { dataAttributeThemeStore } from '../../theme/theme-store/data-attribute-
12
12
  * @param themes - Record mapping theme keys to their data attribute values
13
13
  * @param options.attributeName - Data attribute name (e.g. `data-theme`)
14
14
  * @param options.defaultTheme - Fallback theme key when no matching attribute value is found
15
- * @param options.element - Element to read/set theme on (defaults to document.documentElement)
15
+ * @param options.element - Element to read/set theme on (accepts null e.g. from refs). Defaults to document.documentElement.
16
16
  * @returns Tuple of [currentTheme, setTheme]
17
17
  *
18
18
  * @example
@@ -37,7 +37,7 @@ export function useThemeByDataAttribute<Themes extends ThemeMap>(
37
37
  options: {
38
38
  attributeName: `data-${string}`
39
39
  defaultTheme?: keyof Themes | undefined
40
- element?: Element | undefined
40
+ element?: Element | null | undefined
41
41
  }
42
42
  ): [keyof Themes | undefined, (theme: keyof Themes) => void] {
43
43
  const element =
@@ -101,16 +101,14 @@ function createSharedChannel<Themes extends ThemeMap>(
101
101
  }
102
102
  }
103
103
 
104
- const handleStoreUpdate = (entry: ThemeEntry<Themes> | undefined | null) => {
104
+ const handleStoreUpdate = (entry: ThemeEntry<Themes> | undefined) => {
105
105
  notify(entry?.theme ?? defaultTheme)
106
106
  }
107
107
 
108
108
  // Initial read to populate lastTheme (compose store subscribe has no initial notify)
109
- void Promise.resolve(composedStore.read()).then(
110
- (entry: ThemeEntry<Themes> | undefined | null) => {
111
- notify(entry?.theme ?? defaultTheme)
112
- }
113
- )
109
+ void Promise.resolve(composedStore.read()).then((entry: ThemeEntry<Themes> | undefined) => {
110
+ notify(entry?.theme ?? defaultTheme)
111
+ })
114
112
 
115
113
  let unobserve: () => void = composedStore.subscribe(handleStoreUpdate)
116
114
  let isSubscribedToStore = true
@@ -38,12 +38,12 @@ function getCanonicalShapeAndComparable(v: ThemeMapValue): {
38
38
  * returns { theme, value: stored.value }. Else returns undefined.
39
39
  *
40
40
  * @param themes - Record of valid theme keys and values (required for validation)
41
- * @param value - Raw string from localStorage/sessionStorage/cookie
41
+ * @param value - Raw string from localStorage/sessionStorage/cookie (accepts null)
42
42
  * @returns ThemeEntry when valid, otherwise undefined
43
43
  */
44
44
  export function parseStoredTheme<Themes extends ThemeMap>(
45
45
  themes: Themes | undefined,
46
- value: string | undefined
46
+ value: string | null | undefined
47
47
  ): ThemeEntry<Themes> | undefined {
48
48
  const parsed = tryParseJSON<{ theme: string; value?: unknown }>(value ?? null)
49
49
  if (!parsed?.theme || typeof parsed.theme !== 'string') return undefined
@@ -4,18 +4,18 @@ import type { AsyncThemeStore } from '../theme-store/async-theme-store.types.ts'
4
4
  import type { ThemeStore } from '../theme-store/theme-store.types.ts'
5
5
 
6
6
  type StoreWithWrite<Themes extends ThemeMap> = (ThemeStore<Themes> | AsyncThemeStore<Themes>) & {
7
- write: (entry: ThemeEntry<Themes> | undefined) => void | Promise<void>
7
+ write: (entry: ThemeEntry<Themes> | null | undefined) => void | Promise<void>
8
8
  }
9
9
 
10
10
  /**
11
11
  * Writes theme entry to all stores that have a write method.
12
12
  *
13
13
  * @param stores - Array of theme stores
14
- * @param entry - Theme entry to write, or undefined to clear
14
+ * @param entry - Theme entry to write, or null/undefined to clear
15
15
  */
16
16
  export async function setThemeToStores<Themes extends ThemeMap>(
17
17
  stores: (ThemeStore<Themes> | AsyncThemeStore<Themes>)[],
18
- entry: ThemeEntry<Themes> | undefined
18
+ entry: ThemeEntry<Themes> | null | undefined
19
19
  ): Promise<void> {
20
20
  const withWrite = stores.filter((s): s is StoreWithWrite<Themes> => typeof s.write === 'function')
21
21
 
@@ -12,12 +12,12 @@ import type { ThemeMap } from '../theme-map.types.ts'
12
12
  * Arrays in theme map use first value for matching.
13
13
  *
14
14
  * @param themes - Record mapping theme keys to class name(s)
15
- * @param className - Raw class attribute value (e.g. from element.className)
15
+ * @param className - Raw class attribute value (e.g. from element.className; accepts null)
16
16
  * @returns ThemeEntry if a match is found, otherwise undefined
17
17
  */
18
18
  export function parseClassName<Themes extends ThemeMap>(
19
19
  themes: Themes,
20
- className: string | undefined
20
+ className: string | null | undefined
21
21
  ): ThemeEntry<Themes> | undefined {
22
22
  const cls = className ?? ''
23
23
  const theme = findKey(themes, (key) => {
@@ -6,14 +6,14 @@ import { parseClassName } from './parse-class-name.ts'
6
6
  * Reads a theme entry from the class attribute on an element.
7
7
  *
8
8
  * @param themes - Record mapping theme keys to class name(s)
9
- * @param options.element - Element to read from (defaults to document.documentElement)
9
+ * @param options.element - Element to read from (accepts null e.g. from refs). Defaults to document.documentElement.
10
10
  * @param options.parse - Custom parser (default: parseClassName)
11
11
  * @returns ThemeEntry if found, undefined otherwise. Returns undefined when element is not available (e.g. SSR).
12
12
  */
13
13
  export function readClassName<Themes extends ThemeMap>(
14
14
  themes: Themes,
15
15
  options?:
16
- | { element?: Element | undefined; parse?: ParseStoredTheme<Themes> | undefined }
16
+ | { element?: Element | null | undefined; parse?: ParseStoredTheme<Themes> | undefined }
17
17
  | undefined
18
18
  ): ThemeEntry<Themes> | undefined {
19
19
  const element = options?.element ?? document?.documentElement
@@ -10,23 +10,23 @@ import type { ThemeMap } from '../theme-map.types.ts'
10
10
  * (unlike stringifyDataAttribute which uses first only).
11
11
  *
12
12
  * @param themes - Record mapping theme keys to class names (used to identify theme tokens)
13
- * @param existing - Current class attribute value string
14
- * @param entry - Theme entry to stringify, or undefined to clear theme (keeps non-theme classes)
13
+ * @param existing - Current class attribute value string (accepts null)
14
+ * @param entry - Theme entry to stringify, or null/undefined to clear theme (keeps non-theme classes)
15
15
  * @returns Class attribute value string
16
16
  */
17
17
  export function stringifyClassName<Themes extends ThemeMap>(
18
18
  themes: Themes,
19
- existing: string | undefined,
20
- entry: ThemeEntry<Themes> | undefined
19
+ existing: string | null | undefined,
20
+ entry: ThemeEntry<Themes> | null | undefined
21
21
  ): string {
22
22
  const allThemeClasses = Object.values(themes).flatMap((v) => {
23
23
  const resolved = resolveThemeMapValue(v)
24
24
  return Array.isArray(resolved) ? [...resolved] : [resolved]
25
25
  })
26
- const existingClasses = existing?.trim() ? existing.trim().split(/\s+/) : []
26
+ const existingClasses = (existing ?? '')?.trim() ? (existing ?? '').trim().split(/\s+/) : []
27
27
  const withoutThemes = existingClasses.filter((c) => !allThemeClasses.includes(c))
28
28
  const activeClasses =
29
- entry !== undefined
29
+ entry != null
30
30
  ? (() => {
31
31
  const resolved = resolveThemeMapValue(entry.value)
32
32
  return Array.isArray(resolved) ? [...resolved] : [resolved]
@@ -8,7 +8,7 @@ import { parseClassName } from './parse-class-name.ts'
8
8
  *
9
9
  * @param themes - Record mapping theme keys to class name(s)
10
10
  * @param handler - Callback invoked when the class attribute changes
11
- * @param options.element - Element to observe (defaults to document.documentElement)
11
+ * @param options.element - Element to observe (accepts null e.g. from refs). Defaults to document.documentElement.
12
12
  * @param options.parse - Custom parser (default: parseClassName)
13
13
  * @returns Unsubscribe function. Returns a no-op function when element is not available (e.g. SSR).
14
14
  */
@@ -16,7 +16,7 @@ export function subscribeClassName<Themes extends ThemeMap>(
16
16
  themes: Themes,
17
17
  handler: (entry: ThemeEntry<Themes> | undefined) => void,
18
18
  options?:
19
- | { element?: Element | undefined; parse?: ParseStoredTheme<Themes> | undefined }
19
+ | { element?: Element | null | undefined; parse?: ParseStoredTheme<Themes> | undefined }
20
20
  | undefined
21
21
  ): () => void {
22
22
  const element = options?.element ?? document?.documentElement
@@ -6,15 +6,15 @@ import { stringifyClassName } from './stringify-class-name.ts'
6
6
  * Writes a theme entry to the class attribute on an element.
7
7
  *
8
8
  * @param themes - Record mapping theme keys to class name(s)
9
- * @param entry - Theme entry to write, or undefined to remove the theme
10
- * @param options.element - Element to write to (defaults to document.documentElement)
9
+ * @param entry - Theme entry to write, or null/undefined to remove the theme
10
+ * @param options.element - Element to write to (accepts null e.g. from refs). Defaults to document.documentElement.
11
11
  * @param options.stringify - Custom serializer (default: stringifyClassName)
12
12
  */
13
13
  export function writeClassName<Themes extends ThemeMap>(
14
14
  themes: Themes,
15
- entry: ThemeEntry<Themes> | undefined,
15
+ entry: ThemeEntry<Themes> | null | undefined,
16
16
  options?:
17
- | { element?: Element | undefined; stringify?: StringifyStoredTheme<Themes> | undefined }
17
+ | { element?: Element | null | undefined; stringify?: StringifyStoredTheme<Themes> | undefined }
18
18
  | undefined
19
19
  ): void {
20
20
  const element = options?.element ?? document?.documentElement
@@ -74,7 +74,7 @@ export function composeThemeStores<
74
74
  (s): s is StoreWithSubscribe<Themes> => typeof s.subscribe === 'function'
75
75
  )
76
76
 
77
- function subscribe(handler: (theme: ThemeEntry<Themes> | undefined | null) => void): () => void {
77
+ function subscribe(handler: (theme: ThemeEntry<Themes> | undefined) => void): () => void {
78
78
  let scheduled = false
79
79
  let lastEmitted: keyof Themes | undefined
80
80
 
@@ -16,12 +16,12 @@ export interface WriteCookieThemeOptions<_Themes extends ThemeMap = ThemeMap> {
16
16
  * Performs cookie set/delete only. Does not notify subscribers; the store must call notify() after this.
17
17
  *
18
18
  * @param themes - Record mapping theme keys to values (used for type validation)
19
- * @param entry - Theme entry to write, or undefined to remove
19
+ * @param entry - Theme entry to write, or null/undefined to remove
20
20
  * @param options - Cookie options
21
21
  */
22
22
  export function writeCookieTheme<Themes extends ThemeMap>(
23
23
  _themes: Themes,
24
- entry: ThemeEntry<Themes> | undefined,
24
+ entry: ThemeEntry<Themes> | null | undefined,
25
25
  options: WriteCookieThemeOptions<Themes>
26
26
  ): void {
27
27
  const { cookieName, path = '/', maxAge, sameSite, secure } = options
@@ -30,7 +30,7 @@ export function writeCookieTheme<Themes extends ThemeMap>(
30
30
  return
31
31
  }
32
32
 
33
- if (entry === undefined) {
33
+ if (entry == null) {
34
34
  deleteCookie(cookieName, path)
35
35
  return
36
36
  }