@k8ordo/color-scheme 0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Koki Sakano
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # @k8ordo/color-scheme
2
+
3
+ The colour-scheme axis of an application, owned. The visitor's preference —
4
+ light, dark, or nothing, which follows the system — lives in localStorage
5
+ through `@k8ordo/state`; one rule resolves it against `prefers-color-scheme`;
6
+ the result is the `dark` class on `<html>`, put there by an inline script
7
+ before the first paint and kept there after hydration. One provider does all
8
+ of that; a hook reads it. `@k8ordo/ui`'s tokens, and Tailwind's own dark
9
+ variant, read the class.
10
+
11
+ Like every [k8ordo](https://ordo.k8o.me) package it assumes React 19 and Server
12
+ Components, uses only what has reached Baseline newly available, and ships no
13
+ polyfills or legacy fallbacks.
14
+
15
+ - **Documentation**: https://ordo.k8o.me/color-scheme
16
+ - **Design guide**: [docs/GUIDE.md](docs/GUIDE.md) — shipped inside this package
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @k8ordo/color-scheme @k8ordo/state zod
22
+ # or
23
+ pnpm add @k8ordo/color-scheme @k8ordo/state zod
24
+ ```
25
+
26
+ ## Peer Dependencies
27
+
28
+ | Package | Version | Needed for |
29
+ | --------------- | ------- | ------------------------------------------ |
30
+ | `@k8ordo/state` | ≥0.2.0 | where the preference lives (localStorage) |
31
+ | `react` | ≥19.3.0 | the provider and the hook |
32
+ | `zod` | ^4.4.3 | the one-field schema `@k8ordo/state` reads |
33
+ | `typescript` | ≥7.0.2 | the shipped type declarations (optional) |
34
+
35
+ ## Quick Start
36
+
37
+ The provider goes in the root layout, inside `<body>`, around everything. It
38
+ renders the inline script itself, so nothing goes in `<head>`:
39
+
40
+ ```tsx
41
+ // routes/layout.tsx — a Server Component
42
+ import { ColorSchemeProvider } from '@k8ordo/color-scheme';
43
+
44
+ export default function RootLayout({ children }) {
45
+ return (
46
+ <html suppressHydrationWarning>
47
+ <body>
48
+ <ColorSchemeProvider>{children}</ColorSchemeProvider>
49
+ </body>
50
+ </html>
51
+ );
52
+ }
53
+ ```
54
+
55
+ A switcher reads and writes the preference:
56
+
57
+ ```tsx
58
+ 'use client';
59
+
60
+ import { useColorScheme } from '@k8ordo/color-scheme';
61
+
62
+ export function SchemeSwitcher() {
63
+ const { scheme, setPreference } = useColorScheme();
64
+ return (
65
+ <button
66
+ onClick={() => setPreference(scheme === 'dark' ? 'light' : 'dark')}
67
+ type="button"
68
+ >
69
+ {scheme === 'dark' ? 'light mode' : 'dark mode'}
70
+ </button>
71
+ );
72
+ }
73
+ ```
74
+
75
+ `scheme` is what is on screen; `preference` is what the visitor asked for
76
+ (`'system'` when nothing is stored); `setPreference('system')` clears it.
77
+ `<ColorSchemeProvider defaultPreference="dark">` starts every visitor dark
78
+ until they choose.
79
+
80
+ ## AI Agent Documentation
81
+
82
+ This package ships its documentation so an AI coding assistant reads the exact
83
+ installed version:
84
+
85
+ - `node_modules/@k8ordo/color-scheme/docs/GUIDE.md` — the design guide
86
+ - `node_modules/@k8ordo/color-scheme/docs/llms.txt` — the index
87
+
88
+ ## License
89
+
90
+ MIT
@@ -0,0 +1,3 @@
1
+ import { ColorScheme, ColorSchemePreference, colorSchemeState } from "./scheme.mjs";
2
+ import { ColorSchemeProvider, ColorSchemeProviderProps, UseColorScheme, useColorScheme } from "./provider.mjs";
3
+ export { type ColorScheme, type ColorSchemePreference, ColorSchemeProvider, type ColorSchemeProviderProps, type UseColorScheme, colorSchemeState, useColorScheme };
package/dist/index.mjs ADDED
@@ -0,0 +1,3 @@
1
+ import { colorSchemeState } from "./scheme.mjs";
2
+ import { ColorSchemeProvider, useColorScheme } from "./provider.mjs";
3
+ export { ColorSchemeProvider, colorSchemeState, useColorScheme };
@@ -0,0 +1,31 @@
1
+ import { ColorScheme, ColorSchemePreference } from "./scheme.mjs";
2
+ import { ReactNode } from "react";
3
+ //#region src/provider.d.ts
4
+ type UseColorScheme = {
5
+ /** What is on screen: the preference, or the system's answer. */
6
+ readonly scheme: ColorScheme;
7
+ /** What the visitor asked for; `'system'` when nothing is stored. */
8
+ readonly preference: ColorSchemePreference;
9
+ /** Stores a preference, or `'system'` to store none and follow it again. */
10
+ readonly setPreference: (preference: ColorSchemePreference) => void;
11
+ };
12
+ type ColorSchemeProviderProps = {
13
+ /**
14
+ * What applies while the visitor has chosen nothing. `'system'` follows
15
+ * `prefers-color-scheme`; a scheme starts every visitor there until they
16
+ * choose.
17
+ */
18
+ readonly defaultPreference?: ColorSchemePreference;
19
+ readonly children: ReactNode;
20
+ };
21
+ /**
22
+ * The one place the scheme is decided and written. Goes in the root
23
+ * layout, inside `<body>`, around everything: it renders the inline script
24
+ * that puts the class on `<html>` before the first paint, keeps the class
25
+ * in step afterwards, and hands `useColorScheme` what it reads.
26
+ */
27
+ declare function ColorSchemeProvider({ defaultPreference, children }: ColorSchemeProviderProps): ReactNode;
28
+ /** The visitor's colour scheme and a way to change it, from the provider above. */
29
+ declare function useColorScheme(): UseColorScheme;
30
+ //#endregion
31
+ export { ColorSchemeProvider, ColorSchemeProviderProps, UseColorScheme, useColorScheme };
@@ -0,0 +1,58 @@
1
+ "use client";
2
+ import { DARK_CLASS, DARK_QUERY, colorSchemeState, resolve, scriptFor } from "./scheme.mjs";
3
+ import { useAppState } from "@k8ordo/state";
4
+ import { createContext, use, useCallback, useEffect, useMemo, useSyncExternalStore } from "react";
5
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/provider.tsx
7
+ const ColorSchemeContext = createContext(null);
8
+ const subscribeSystem = (onChange) => {
9
+ const query = matchMedia(DARK_QUERY);
10
+ query.addEventListener("change", onChange);
11
+ return () => {
12
+ query.removeEventListener("change", onChange);
13
+ };
14
+ };
15
+ const readSystemDark = () => matchMedia(DARK_QUERY).matches;
16
+ const serverSystemDark = () => false;
17
+ const subscribeNever = () => () => {};
18
+ const useReadsStore = () => useSyncExternalStore(subscribeNever, () => true, () => false);
19
+ /**
20
+ * The one place the scheme is decided and written. Goes in the root
21
+ * layout, inside `<body>`, around everything: it renders the inline script
22
+ * that puts the class on `<html>` before the first paint, keeps the class
23
+ * in step afterwards, and hands `useColorScheme` what it reads.
24
+ */
25
+ function ColorSchemeProvider({ defaultPreference = "system", children }) {
26
+ const [{ preference }, update] = useAppState(colorSchemeState);
27
+ const systemDark = useSyncExternalStore(subscribeSystem, readSystemDark, serverSystemDark);
28
+ const scheme = resolve(preference, defaultPreference, systemDark);
29
+ const readsStore = useReadsStore();
30
+ useEffect(() => {
31
+ if (!readsStore) return;
32
+ document.documentElement.classList.toggle(DARK_CLASS, scheme === "dark");
33
+ }, [readsStore, scheme]);
34
+ const setPreference = useCallback((next) => {
35
+ update({ preference: next === "system" ? void 0 : next });
36
+ }, [update]);
37
+ const value = useMemo(() => ({
38
+ scheme,
39
+ preference: preference ?? "system",
40
+ setPreference
41
+ }), [
42
+ scheme,
43
+ preference,
44
+ setPreference
45
+ ]);
46
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("script", { children: scriptFor(defaultPreference) }), /* @__PURE__ */ jsx(ColorSchemeContext, {
47
+ value,
48
+ children
49
+ })] });
50
+ }
51
+ /** The visitor's colour scheme and a way to change it, from the provider above. */
52
+ function useColorScheme() {
53
+ const value = use(ColorSchemeContext);
54
+ if (value === null) throw new Error("useColorScheme needs <ColorSchemeProvider> above it — put one in the root layout, inside <body>");
55
+ return value;
56
+ }
57
+ //#endregion
58
+ export { ColorSchemeProvider, useColorScheme };
@@ -0,0 +1,36 @@
1
+ import * as z from "zod/mini";
2
+ //#region src/scheme.d.ts
3
+ type ColorScheme = 'light' | 'dark';
4
+ /** What the visitor asked for: a scheme, or to follow the system. */
5
+ type ColorSchemePreference = ColorScheme | 'system';
6
+ /**
7
+ * Where the preference lives: localStorage, under
8
+ * `k8ordo-state:color-scheme`. An absent `preference` is "nothing chosen",
9
+ * so a visitor who never chose is never pinned to what the system said on
10
+ * their first visit.
11
+ */
12
+ declare const colorSchemeState: import("@k8ordo/state").LocalState<z.ZodMiniObject<{
13
+ preference: z.ZodMiniOptional<z.ZodMiniEnum<{
14
+ dark: "dark";
15
+ light: "light";
16
+ }>>;
17
+ }, z.core.$strip>>;
18
+ /** The media query the system answers through. */
19
+ declare const DARK_QUERY = "(prefers-color-scheme: dark)";
20
+ /** The class `@k8ordo/ui` — and Tailwind's own dark variant — read on `<html>`. */
21
+ declare const DARK_CLASS = "dark";
22
+ /**
23
+ * The one rule: what the visitor chose wins, then what the provider was
24
+ * told to start from, and `system` — the default's default — asks the
25
+ * system.
26
+ */
27
+ declare const resolve: (preference: ColorScheme | undefined, defaultPreference: ColorSchemePreference, systemDark: boolean) => ColorScheme;
28
+ /**
29
+ * The inline script the provider renders before its children: the rule
30
+ * above, run before the first paint. The schema does not run there, so the
31
+ * stored value is checked by hand and anything else reads as nothing
32
+ * chosen — the same salvage the store applies.
33
+ */
34
+ declare const scriptFor: (defaultPreference: ColorSchemePreference) => string;
35
+ //#endregion
36
+ export { ColorScheme, ColorSchemePreference, DARK_CLASS, DARK_QUERY, colorSchemeState, resolve, scriptFor };
@@ -0,0 +1,31 @@
1
+ import { defineLocalState } from "@k8ordo/state";
2
+ import * as z from "zod/mini";
3
+ //#region src/scheme.ts
4
+ /**
5
+ * Where the preference lives: localStorage, under
6
+ * `k8ordo-state:color-scheme`. An absent `preference` is "nothing chosen",
7
+ * so a visitor who never chose is never pinned to what the system said on
8
+ * their first visit.
9
+ */
10
+ const colorSchemeState = defineLocalState("color-scheme", z.object({ preference: z.optional(z.enum(["light", "dark"])) }));
11
+ /** The media query the system answers through. */
12
+ const DARK_QUERY = "(prefers-color-scheme: dark)";
13
+ /** The class `@k8ordo/ui` — and Tailwind's own dark variant — read on `<html>`. */
14
+ const DARK_CLASS = "dark";
15
+ /**
16
+ * The one rule: what the visitor chose wins, then what the provider was
17
+ * told to start from, and `system` — the default's default — asks the
18
+ * system.
19
+ */
20
+ const resolve = (preference, defaultPreference, systemDark) => {
21
+ return preference ?? (defaultPreference === "system" ? void 0 : defaultPreference) ?? (systemDark ? "dark" : "light");
22
+ };
23
+ /**
24
+ * The inline script the provider renders before its children: the rule
25
+ * above, run before the first paint. The schema does not run there, so the
26
+ * stored value is checked by hand and anything else reads as nothing
27
+ * chosen — the same salvage the store applies.
28
+ */
29
+ const scriptFor = (defaultPreference) => `(()=>{const s=${colorSchemeState.inlineRead()};const v=s&&s.preference;const p=v==="dark"||v==="light"?v:${JSON.stringify(defaultPreference)};if(p==="dark"||(p!=="light"&&matchMedia(${JSON.stringify(DARK_QUERY)}).matches))document.documentElement.classList.add(${JSON.stringify(DARK_CLASS)})})()`;
30
+ //#endregion
31
+ export { DARK_CLASS, DARK_QUERY, colorSchemeState, resolve, scriptFor };
package/docs/GUIDE.md ADDED
@@ -0,0 +1,130 @@
1
+ # @k8ordo/color-scheme
2
+
3
+ The colour-scheme axis of an application, owned.
4
+
5
+ A visitor's colour scheme is three things that have to agree: what they
6
+ asked for, what the system says, and what is on screen. This package keeps
7
+ the first in localStorage (through `@k8ordo/state`), reads the second from
8
+ `prefers-color-scheme`, and resolves both into the third — the `dark` class
9
+ on `<html>`, which `@k8ordo/ui`'s tokens and Tailwind's own dark variant
10
+ read. One provider does all of it: it renders the inline script that puts
11
+ the class there before the first paint, keeps it right after hydration, and
12
+ hands a hook what it reads.
13
+
14
+ Like every k8ordo package it assumes React 19 and Server Components, uses
15
+ only what has reached Baseline newly available, and ships no polyfills or
16
+ legacy fallbacks.
17
+
18
+ ## What it does not own
19
+
20
+ - **The colours.** Which colour `dark` means is the stylesheet's business —
21
+ `@k8ordo/ui`'s tokens, or any other that reads the class.
22
+ - **Where the preference lives.** That is `@k8ordo/state`'s
23
+ `defineLocalState`; this package declares one and reads it, so the key
24
+ and the JSON envelope are never spelled here.
25
+ - **A guess on the server.** No cookie, no header: a server render shows
26
+ the provider's default, and the inline script is what makes the first
27
+ paint right.
28
+
29
+ ## The shape of it
30
+
31
+ ```
32
+ routes/layout.tsx <body><ColorSchemeProvider>{children}</ColorSchemeProvider></body>
33
+ │ renders <script> first → class on <html> before the first paint
34
+ │ reads the store + the system → keeps the class in step
35
+ components useColorScheme() → { scheme, preference, setPreference }
36
+ ```
37
+
38
+ ## Setup
39
+
40
+ The provider goes in the root layout — a Server Component — inside `<body>`
41
+ and around everything, so the script it renders comes before anything the
42
+ page paints:
43
+
44
+ ```tsx
45
+ // routes/layout.tsx
46
+ import { ColorSchemeProvider } from '@k8ordo/color-scheme';
47
+ import type { ReactNode } from 'react';
48
+
49
+ export default function RootLayout({ children }: { children: ReactNode }) {
50
+ return (
51
+ <html lang="en" suppressHydrationWarning>
52
+ <body>
53
+ <ColorSchemeProvider>{children}</ColorSchemeProvider>
54
+ </body>
55
+ </html>
56
+ );
57
+ }
58
+ ```
59
+
60
+ `suppressHydrationWarning` on `<html>`, because the script adds a class the
61
+ server did not render — that difference is the point, not a mistake.
62
+
63
+ Nothing else is required: the first paint is right, and a visitor who
64
+ never chooses follows the system, in either direction, for as long as the
65
+ page is open. To start every visitor somewhere other than the system, say
66
+ so once: `<ColorSchemeProvider defaultPreference="dark">`.
67
+
68
+ ## Reading and changing it
69
+
70
+ ```tsx
71
+ 'use client';
72
+
73
+ import { useColorScheme } from '@k8ordo/color-scheme';
74
+
75
+ export function SchemeSwitcher() {
76
+ const { scheme, preference, setPreference } = useColorScheme();
77
+ return (
78
+ <select
79
+ onChange={(event) =>
80
+ setPreference(event.currentTarget.value as typeof preference)
81
+ }
82
+ value={preference}
83
+ >
84
+ <option value="system">system ({scheme})</option>
85
+ <option value="light">light</option>
86
+ <option value="dark">dark</option>
87
+ </select>
88
+ );
89
+ }
90
+ ```
91
+
92
+ | Member | What it is |
93
+ | --------------- | ------------------------------------------------------------------------- |
94
+ | `scheme` | `'light' \| 'dark'` — what is on screen. |
95
+ | `preference` | `'light' \| 'dark' \| 'system'` — what the visitor asked for. |
96
+ | `setPreference` | Stores a preference; `'system'` stores none and follows the system again. |
97
+
98
+ A toggle is `setPreference(scheme === 'dark' ? 'light' : 'dark')`. The hook
99
+ reads the provider and touches nothing else, so a switcher and a preview
100
+ agree because there is one provider deciding.
101
+
102
+ `colorSchemeState` is the `defineLocalState` definition itself, exported for
103
+ anything that wants the row without the hook — `useAppState(colorSchemeState)`,
104
+ or `colorSchemeState.storageKey` in a test.
105
+
106
+ ## What it guarantees
107
+
108
+ - **No flash.** The script runs before the first paint and reads the same
109
+ row the provider writes; a dark page loads dark.
110
+ - **Nothing chosen follows the default, and the default follows the
111
+ system.** A visitor who never chose is never pinned to what the system
112
+ said on their first visit, and a row an older version wrote, or one that
113
+ is not JSON, reads as nothing chosen.
114
+ - **The server renders the default**, and the provider corrects itself on
115
+ hydration without touching the document: the render that hydrates reads
116
+ the server's guesses and writes nothing, and the render that follows reads
117
+ the store and writes what the script already put there. Until then the
118
+ HTML shows the default — a switcher's icon, say — which is the one thing
119
+ no server can know; a component that must not show it even for a moment
120
+ says so with React's `use(browser())` under a `<Suspense>`.
121
+ - **Tabs agree.** The preference propagates across tabs through the
122
+ `storage` event, as any `@k8ordo/state` local state does.
123
+
124
+ ## Testing
125
+
126
+ Under a browser, `localStorage.clear()`, remove the class from
127
+ `document.documentElement`, and `resetStateRegistry()` from `@k8ordo/state`
128
+ between tests; render the hook under `<ColorSchemeProvider>` (a `wrapper`).
129
+ A client-only mount also runs the script the provider renders — a script
130
+ element React creates executes — which is idempotent with the effect.
package/docs/llms.txt ADDED
@@ -0,0 +1,37 @@
1
+ # @k8ordo/color-scheme
2
+
3
+ > The colour-scheme axis of an application, owned. The visitor's preference
4
+ > — light, dark, or nothing, which follows the system — lives in
5
+ > localStorage through `@k8ordo/state`; one rule resolves it against
6
+ > `prefers-color-scheme`; the result is the `dark` class on `<html>` (what
7
+ > `@k8ordo/ui` and Tailwind's dark variant read). `<ColorSchemeProvider>` in
8
+ > the root layout renders the inline script that puts the class on before
9
+ > the first paint and keeps it in step afterwards; `useColorScheme()` reads
10
+ > it. The server renders the default.
11
+ >
12
+ > Like every k8ordo package it assumes React 19 and Server Components, uses
13
+ > only what has reached Baseline newly available, and ships no polyfills or
14
+ > legacy fallbacks.
15
+
16
+ When installed via npm, these docs live under `node_modules/@k8ordo/color-scheme/docs/`.
17
+
18
+ ## Guide
19
+
20
+ - [Design Guide](GUIDE.md): what the package owns and does not (the class,
21
+ not the colours; the rule, not the storage), placing the provider in the
22
+ root layout, reading and changing the preference with the hook, what it
23
+ guarantees, and testing.
24
+
25
+ ## Entry point
26
+
27
+ - `@k8ordo/color-scheme` — `<ColorSchemeProvider defaultPreference?>`
28
+ (client; goes inside `<body>` around everything) renders the inline
29
+ `<script>` that puts `dark` on `<html>` before the first paint from the
30
+ stored preference, the default (`'system'` unless told otherwise), or the
31
+ system, and keeps the class in step. `useColorScheme()` returns
32
+ `{ scheme, preference, setPreference }`: `scheme` is `'light' | 'dark'`
33
+ on screen, `preference` is `'light' | 'dark' | 'system'`, and
34
+ `setPreference('system')` clears the stored row. `colorSchemeState` is the
35
+ `@k8ordo/state` local definition itself (`k8ordo-state:color-scheme`).
36
+ Types: `ColorScheme`, `ColorSchemePreference`, `ColorSchemeProviderProps`,
37
+ `UseColorScheme`.
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "@k8ordo/color-scheme",
3
+ "version": "0.1.0",
4
+ "description": "The colour-scheme axis of an application, owned: the visitor's light / dark / system preference, kept in localStorage, resolved against the system, put on <html> before the first paint and kept there.",
5
+ "keywords": [
6
+ "color-scheme",
7
+ "dark-mode",
8
+ "prefers-color-scheme",
9
+ "react",
10
+ "server-components",
11
+ "theme"
12
+ ],
13
+ "homepage": "https://ordo.k8o.me",
14
+ "bugs": {
15
+ "url": "https://github.com/k35o/k8ordo/issues"
16
+ },
17
+ "license": "MIT",
18
+ "author": "k8o <kosakanoki@gmail.com>",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/k35o/k8ordo.git"
22
+ },
23
+ "files": [
24
+ "dist/**",
25
+ "docs/**"
26
+ ],
27
+ "type": "module",
28
+ "sideEffects": false,
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.mts",
32
+ "default": "./dist/index.mjs"
33
+ },
34
+ "./package.json": "./package.json"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public",
38
+ "registry": "https://registry.npmjs.org"
39
+ },
40
+ "devDependencies": {
41
+ "@arethetypeswrong/cli": "0.18.5",
42
+ "@k8ordo/state": "^0.2.0",
43
+ "@types/react": "19.3.0",
44
+ "@types/react-dom": "19.3.0",
45
+ "@vitejs/plugin-react": "6.1.1",
46
+ "@vitest/browser-playwright": "5.0.0",
47
+ "publint": "0.3.24",
48
+ "react": "19.3.0",
49
+ "react-dom": "19.3.0",
50
+ "typescript": "7.0.2",
51
+ "vite": "8.2.2",
52
+ "vite-plus": "0.3.0",
53
+ "vitest": "5.0.0",
54
+ "vitest-browser-react": "2.3.0",
55
+ "zod": "4.5.4"
56
+ },
57
+ "peerDependencies": {
58
+ "@k8ordo/state": "^0.2.0",
59
+ "@types/react": ">=19.3.0",
60
+ "react": ">=19.3.0",
61
+ "typescript": ">=7.0.2",
62
+ "zod": "^4.4.3"
63
+ },
64
+ "peerDependenciesMeta": {
65
+ "@types/react": {
66
+ "optional": true
67
+ },
68
+ "typescript": {
69
+ "optional": true
70
+ }
71
+ },
72
+ "scripts": {
73
+ "build": "vp pack",
74
+ "test": "vp test",
75
+ "typecheck": "tsc --noEmit",
76
+ "check": "vp check",
77
+ "check:write": "vp check --fix",
78
+ "check:package": "publint && attw --pack . --profile esm-only"
79
+ }
80
+ }