@kbach/browser 0.0.0-stage → 1.0.0-beta.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/AGENTS.md ADDED
@@ -0,0 +1,107 @@
1
+ # `@kbach/browser` — reference for AI assistants
2
+
3
+ Kbach from a CDN: one classic `<script>` for a plain HTML page, with no
4
+ bundler, no npm install and no React. This file covers `@kbach/browser`
5
+ only. For the cross-package overview, see the repo root's
6
+ [AGENTS.md](https://github.com/semsakadanupol/kbach/blob/main/AGENTS.md).
7
+
8
+ ```html
9
+ <script>window.kbachConfig = { darkMode: 'class', extend: { colors: { brand: '#ff6b35' } } };</script>
10
+ <script src="https://cdn.jsdelivr.net/npm/@kbach/browser"></script>
11
+ ```
12
+
13
+ `https://cdn.jsdelivr.net/npm/@kbach/browser` and
14
+ `https://unpkg.com/@kbach/browser` serve `dist/kbach.js` via package.json's
15
+ `jsdelivr`/`unpkg` fields.
16
+
17
+ ---
18
+
19
+ ## 1. What it does
20
+
21
+ `src/index.ts` (entry) → `startKbachCdn` (`src/runtime.ts`):
22
+
23
+ 1. Reads `window.kbachConfig` once (same shape as `kbach.config.js`),
24
+ resolves it with `@kbach/react`'s `resolveKbachConfig`, and applies it
25
+ before the engine loads, so dark-mode state and color variables are on
26
+ `<html>` from the start.
27
+ 2. Fetches `kbach_core_engine_bg.wasm` from next to its own
28
+ `<script src>` (`document.currentScript`), or from the script's
29
+ `data-wasm` attribute.
30
+ 3. Styles every `class` token on the page through `@kbach/react`'s `kb()`,
31
+ which injects rules into `<style data-kbach>` plus the base reset. Each
32
+ token is resolved at most once per config (`seen` set, one batched
33
+ `kb()` call per scan). Non-Kbach classes produce no rules.
34
+ 4. Watches `<html>` with a `MutationObserver` (added nodes plus `class`
35
+ attribute changes), started after the first full scan.
36
+ 5. Exposes `window.Kbach`: `ready`, `config(cfg)` (re-resolve plus a full
37
+ re-scan), `refresh()`, `setMode()`, `toggle()` (the last two are
38
+ `@kbach/react`'s dark-mode store).
39
+
40
+ **Mode-aware colors are mapped to CSS variables, not expanded**
41
+ (`src/cdnTheme.ts`, `toCdnTheme`). For a `{ light, dark }` pair, the
42
+ engine rewrites `bg-surface` to `bg-[#…] dark:bg-[#…]` and generates rules
43
+ only for that text, which `kb()` callers handle by using the returned
44
+ className. A CDN script can't rewrite the page's own `class` attributes, so
45
+ each hex pair becomes `rgb(var(--kb-color-<name>))`, and
46
+ `ensureColorVariablesInjected` supplies the light/dark values for the
47
+ configured strategy. Known limit: `bg-surface/50` (opacity on a mode-aware
48
+ color) renders at full opacity.
49
+
50
+ ## 2. How it's built — it bundles `@kbach/react`'s SOURCE
51
+
52
+ There is no second copy of any runtime logic here. `src/*.ts` imports
53
+ `../../react/src/{kb,theme,config,colorVariables,darkModeStore,wasmLoader}`
54
+ directly. None of those import React. They aren't public exports of
55
+ `@kbach/react`, and importing its built entry would pull in React
56
+ components. Consequences:
57
+
58
+ - `@kbach/react` is a `devDependency` (`"*"`, the workspace copy) only so
59
+ Turbo orders and cache-keys this build after react's. It is NOT a runtime
60
+ dependency: everything is bundled into `dist/kbach.js`.
61
+ - `wasmLoader.ts`'s `initKbachFrom(url)` is internal to `@kbach/react`
62
+ (not exported from its index) and exists for this package. Renaming or
63
+ removing it breaks this build, and the tests here catch that.
64
+ - **The `.wasm` and the bundled glue come from the `@kbach/core-engine`
65
+ that `react/src/wasmLoader.ts` resolves** (react's `dependencies`
66
+ version), never from this package. `scripts/copy-wasm.mjs` resolves the
67
+ `.wasm` from that same file on purpose, since a mismatched glue/binary
68
+ pair fails at load time.
69
+
70
+ `npm run build` = `tsup` (`tsup.config.ts`: IIFE, browser, minified,
71
+ `noExternal: [/.*/]`, no source map) → `scripts/copy-wasm.mjs` →
72
+ `scripts/verify-bundle.mjs`. The last step loads the BUILT
73
+ `dist/kbach.js` into jsdom with the real `.wasm` and fails the build
74
+ unless existing, added and changed classes are styled, the wasm URL is
75
+ correct, mode-aware colors use their variable, the page's `class` is
76
+ never rewritten, and `toggle()` flips `<html>`.
77
+
78
+ Tests (`npm run test`): `src/cdnTheme.test.ts` and `src/runtime.test.ts`,
79
+ which runs the real Node-target engine in place of the browser loader,
80
+ under jsdom with `@kbach/react`'s `test-setup.ts`.
81
+
82
+ Sizes (2026-11-07, gzipped): `kbach.js` ~7 KB, `.wasm` ~158 KB.
83
+
84
+ **Sandbox:** `apps/web-cdn` (`npm run dev -w web-cdn`, port 5174) is one
85
+ plain `index.html` and a dependency-free `serve.mjs`. `/` uses the LOCAL
86
+ `dist/` (rebuild with `npm run build -w @kbach/browser`, then reload), and
87
+ `/cdn` uses the published `@kbach/browser@beta` from jsDelivr. The page
88
+ covers utility classes, a config color, mode-aware colors with a toggle,
89
+ added elements and class changes. It must be served over http: `file://`
90
+ can't fetch the `.wasm`.
91
+
92
+ ## 3. Common failure modes
93
+
94
+ - **Nothing is styled.** Check the network tab for the `.wasm` request. A
95
+ 404 means `kbach_core_engine_bg.wasm` isn't next to `kbach.js` (when
96
+ self-hosting, copy both files, or set `data-wasm`). Opening the HTML as
97
+ `file://` fails too: browsers won't `fetch()` a local file, so serve the
98
+ folder (`npx serve`).
99
+ - **A custom color isn't applied.** `window.kbachConfig` was set AFTER
100
+ the `<script src>` tag. It's read once at load. Set it before, or call
101
+ `Kbach.config({...})` later.
102
+ - **`Kbach.toggle()` does nothing visible.** The default
103
+ `darkMode: 'media'` follows the OS only. Set `darkMode: 'class'` or
104
+ `'attribute'` in `window.kbachConfig`.
105
+ - **A brief unstyled flash on load.** Expected: classes resolve in the
106
+ browser after the WASM loads, like Tailwind's Play CDN. Use
107
+ `@kbach/react`'s build plugin for production.
package/README.md CHANGED
@@ -1,3 +1,72 @@
1
- # Temporary Holding Version
1
+ # @kbach/browser
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Kbach from a CDN: one `<script>` tag styles a plain HTML page with Kbach's
4
+ utility classes, the same vocabulary as [`@kbach/react`](https://www.npmjs.com/package/@kbach/react).
5
+ No build step, no bundler, no React.
6
+
7
+ > Beta — API may still change before 1.0.0.
8
+
9
+ ## Quick start
10
+
11
+ ```html
12
+ <!doctype html>
13
+ <html>
14
+ <head>
15
+ <script src="https://cdn.jsdelivr.net/npm/@kbach/browser"></script>
16
+ </head>
17
+ <body>
18
+ <div class="flex items-center gap-4 bg-blue-6 text-white p-4 rounded-lg">
19
+ Hello Kbach
20
+ </div>
21
+ </body>
22
+ </html>
23
+ ```
24
+
25
+ The script loads the Rust/WASM engine (about 7 KB of JS plus 158 KB of
26
+ WASM, gzipped), styles every `class` already on the page, and keeps
27
+ watching: elements added later and `class` changes are styled
28
+ automatically. Pin a version in production
29
+ (`https://cdn.jsdelivr.net/npm/@kbach/browser@1.0.0-beta.0`).
30
+ `https://unpkg.com/@kbach/browser` works too.
31
+
32
+ ## Config
33
+
34
+ Same shape as `kbach.config.js`. Set it before the
35
+ script tag:
36
+
37
+ ```html
38
+ <script>
39
+ window.kbachConfig = {
40
+ darkMode: 'class',
41
+ extend: { colors: { brand: '#ff6b35', surface: { light: '#f9fafb', dark: '#111827' } } },
42
+ };
43
+ </script>
44
+ <script src="https://cdn.jsdelivr.net/npm/@kbach/browser"></script>
45
+ ```
46
+
47
+ ## Runtime API
48
+
49
+ `window.Kbach` has:
50
+ - `Kbach.ready`: a promise that resolves once the page is styled
51
+ - `Kbach.toggle()` / `Kbach.setMode('light' | 'dark' | 'system')`: dark
52
+ mode, persisted (needs `darkMode: 'class'` or `'attribute'`; under the
53
+ default `'media'` the OS decides)
54
+ - `Kbach.config({...})`: replace the config and re-style the page
55
+ - `Kbach.refresh()`: re-scan the page (rarely needed)
56
+
57
+ ## Self-hosting
58
+
59
+ Serve `dist/kbach.js` and
60
+ `dist/kbach_core_engine_bg.wasm` from the same folder, or point at the
61
+ `.wasm` explicitly with `<script src="/kbach.js" data-wasm="/assets/kbach_core_engine_bg.wasm">`.
62
+
63
+ ## Prototypes vs production
64
+
65
+ `@kbach/browser` resolves classes in the browser, like Tailwind's Play CDN.
66
+ That makes it great for prototypes, but the page is unstyled for a moment
67
+ while the engine loads. For production apps, use [`@kbach/react`](https://www.npmjs.com/package/@kbach/react)'s
68
+ build plugin instead: zero runtime cost and no flash.
69
+
70
+ ## License
71
+
72
+ MIT
package/dist/kbach.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";(()=>{var _e={};function B(e,t){let n,r;try{let o=W(e,f.__wbindgen_malloc,f.__wbindgen_realloc),a=T,i=W(t,f.__wbindgen_malloc,f.__wbindgen_realloc),s=T,c=f.generate_css(o,a,i,s);return n=c[0],r=c[1],ge(c[0],c[1])}finally{f.__wbindgen_free(n,r,1)}}function ue(){return{__proto__:null,"./kbach_core_engine_bg.js":{__proto__:null,__wbindgen_init_externref_table:function(){let t=f.__wbindgen_externrefs,n=t.grow(4);t.set(0,void 0),t.set(n+0,void 0),t.set(n+1,null),t.set(n+2,!0),t.set(n+3,!1)}}}}function ge(e,t){return be(e>>>0,t)}var w=null;function C(){return(w===null||w.byteLength===0)&&(w=new Uint8Array(f.memory.buffer)),w}function W(e,t,n){if(n===void 0){let s=_.encode(e),c=t(s.length,1)>>>0;return C().subarray(c,c+s.length).set(s),T=s.length,c}let r=e.length,o=t(r,1)>>>0,a=C(),i=0;for(;i<r;i++){let s=e.charCodeAt(i);if(s>127)break;a[o+i]=s}if(i!==r){i!==0&&(e=e.slice(i)),o=n(o,r,r=i+e.length*3,1)>>>0;let s=C().subarray(o+i,o+r),c=_.encodeInto(e,s);i+=c.written,o=n(o,r,i,1)>>>0}return T=i,o}var x=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0});x.decode();var me=2146435072,S=0;function be(e,t){return S+=t,S>=me&&(x=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0}),x.decode(),S=t),x.decode(C().subarray(e,e+t))}var _=new TextEncoder;"encodeInto"in _||(_.encodeInto=function(e,t){let n=_.encode(e);return t.set(n),{read:e.length,written:n.length}});var T=0,pe,he,f;function ye(e,t){return he=e,f=e.exports,pe=t,w=null,f.__wbindgen_start(),f}async function we(e,t){if(typeof Response=="function"&&e instanceof Response){if(!e.ok)throw new Error(`failed to fetch Wasm: ${e.status} ${e.statusText} fetching '${e.url}'`);if(typeof WebAssembly.instantiateStreaming=="function")try{return await WebAssembly.instantiateStreaming(e,t)}catch(o){if(n(e.type)&&e.headers.get("Content-Type")!=="application/wasm")console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",o);else throw o}let r=await e.arrayBuffer();return await WebAssembly.instantiate(r,t)}else{let r=await WebAssembly.instantiate(e,t);return r instanceof WebAssembly.Instance?{instance:r,module:e}:r}function n(r){switch(r){case"basic":case"cors":case"default":return!0}return!1}}async function D(e){if(f!==void 0)return f;e!==void 0&&(Object.getPrototypeOf(e)===Object.prototype?{module_or_path:e}=e:console.warn("using deprecated parameters for the initialization function; pass a single object instead")),e===void 0&&(e=new URL("kbach_core_engine_bg.wasm",_e.url));let t=ue();(typeof e=="string"||typeof Request=="function"&&e instanceof Request||typeof URL=="function"&&e instanceof URL)&&(e=fetch(e));let{instance:n,module:r}=await we(await e,t);return ye(n,r)}var A=null,$=!1;function P(e){return A||(A=(e===void 0?D():D({module_or_path:e})).then(()=>{$=!0})),A}function U(e,t){if(!$)throw new Error("[Kbach] generateCss() was called before initKbach() resolved. Call `await initKbach()` once at startup (e.g. before rendering your app) before using kb().");return B(e,t)}var G={"amber-1":"#fffbeb","amber-10":"#78350f","amber-11":"#451a03","amber-12":"#291002","amber-2":"#fef3c7","amber-3":"#fde68a","amber-4":"#fcd34d","amber-5":"#fbbf24","amber-6":"#f59e0b","amber-7":"#d97706","amber-8":"#b45309","amber-9":"#92400e",black:"#000000","blue-1":"#eff6ff","blue-10":"#1e3a8a","blue-11":"#172554","blue-12":"#0d1633","blue-2":"#dbeafe","blue-3":"#bfdbfe","blue-4":"#93c5fd","blue-5":"#60a5fa","blue-6":"#3b82f6","blue-7":"#2563eb","blue-8":"#1d4ed8","blue-9":"#1e40af",bronze:"#cd7f32",coal:"#1c1c1c",copper:"#b87333",current:"currentColor","cyan-1":"#ecfeff","cyan-10":"#164e63","cyan-11":"#083344","cyan-12":"#041e28","cyan-2":"#cffafe","cyan-3":"#a5f3fc","cyan-4":"#67e8f9","cyan-5":"#22d3ee","cyan-6":"#06b6d4","cyan-7":"#0891b2","cyan-8":"#0e7490","cyan-9":"#155e75","emerald-1":"#ecfdf5","emerald-10":"#064e3b","emerald-11":"#022c22","emerald-12":"#011a14","emerald-2":"#d1fae5","emerald-3":"#a7f3d0","emerald-4":"#6ee7b7","emerald-5":"#34d399","emerald-6":"#10b981","emerald-7":"#059669","emerald-8":"#047857","emerald-9":"#065f46","fuchsia-1":"#fdf4ff","fuchsia-10":"#701a75","fuchsia-11":"#4a044e","fuchsia-12":"#2d022f","fuchsia-2":"#fae8ff","fuchsia-3":"#f5d0fe","fuchsia-4":"#f0abfc","fuchsia-5":"#e879f9","fuchsia-6":"#d946ef","fuchsia-7":"#c026d3","fuchsia-8":"#a21caf","fuchsia-9":"#86198f",gold:"#d4af37","gray-1":"#f9fafb","gray-10":"#111827","gray-11":"#030712","gray-12":"#020409","gray-2":"#f3f4f6","gray-3":"#e5e7eb","gray-4":"#d1d5db","gray-5":"#9ca3af","gray-6":"#6b7280","gray-7":"#4b5563","gray-8":"#374151","gray-9":"#1f2937","green-1":"#f0fdf4","green-10":"#14532d","green-11":"#052e16","green-12":"#031b0d","green-2":"#dcfce7","green-3":"#bbf7d0","green-4":"#86efac","green-5":"#4ade80","green-6":"#22c55e","green-7":"#16a34a","green-8":"#15803d","green-9":"#166534","indigo-1":"#eef2ff","indigo-10":"#312e81","indigo-11":"#1e1b4b","indigo-12":"#12102d","indigo-2":"#e0e7ff","indigo-3":"#c7d2fe","indigo-4":"#a5b4fc","indigo-5":"#818cf8","indigo-6":"#6366f1","indigo-7":"#4f46e5","indigo-8":"#4338ca","indigo-9":"#3730a3","lime-1":"#f7fee7","lime-10":"#365314","lime-11":"#1a2e05","lime-12":"#0f1b03","lime-2":"#ecfccb","lime-3":"#d9f99d","lime-4":"#bef264","lime-5":"#a3e635","lime-6":"#84cc16","lime-7":"#65a30d","lime-8":"#4d7c0f","lime-9":"#3f6212","neutral-1":"#fafafa","neutral-10":"#171717","neutral-11":"#0a0a0a","neutral-12":"#050505","neutral-2":"#f5f5f5","neutral-3":"#e5e5e5","neutral-4":"#d4d4d4","neutral-5":"#a3a3a3","neutral-6":"#737373","neutral-7":"#525252","neutral-8":"#404040","neutral-9":"#262626","orange-1":"#fff7ed","orange-10":"#7c2d12","orange-11":"#431407","orange-12":"#270c04","orange-2":"#ffedd5","orange-3":"#fed7aa","orange-4":"#fdba74","orange-5":"#fb923c","orange-6":"#f97316","orange-7":"#ea580c","orange-8":"#c2410c","orange-9":"#9a3412","pink-1":"#fdf2f8","pink-10":"#831843","pink-11":"#500724","pink-12":"#300415","pink-2":"#fce7f3","pink-3":"#fbcfe8","pink-4":"#f9a8d4","pink-5":"#f472b6","pink-6":"#ec4899","pink-7":"#db2777","pink-8":"#be185d","pink-9":"#9d174d","purple-1":"#faf5ff","purple-10":"#581c87","purple-11":"#3b0764","purple-12":"#23043c","purple-2":"#f3e8ff","purple-3":"#e9d5ff","purple-4":"#d8b4fe","purple-5":"#c084fc","purple-6":"#a855f7","purple-7":"#9333ea","purple-8":"#7e22ce","purple-9":"#6b21a8","red-1":"#fef2f2","red-10":"#7f1d1d","red-11":"#450a0a","red-12":"#280606","red-2":"#fee2e2","red-3":"#fecaca","red-4":"#fca5a5","red-5":"#f87171","red-6":"#ef4444","red-7":"#dc2626","red-8":"#b91c1c","red-9":"#991b1b","rose-1":"#fff1f2","rose-10":"#881337","rose-11":"#4c0519","rose-12":"#2d030e","rose-2":"#ffe4e6","rose-3":"#fecdd3","rose-4":"#fda4af","rose-5":"#fb7185","rose-6":"#f43f5e","rose-7":"#e11d48","rose-8":"#be123c","rose-9":"#9f1239",silver:"#c0c0c0","sky-1":"#f0f9ff","sky-10":"#0c4a6e","sky-11":"#082f49","sky-12":"#041b2b","sky-2":"#e0f2fe","sky-3":"#bae6fd","sky-4":"#7dd3fc","sky-5":"#38bdf8","sky-6":"#0ea5e9","sky-7":"#0284c7","sky-8":"#0369a1","sky-9":"#075985","slate-1":"#f8fafc","slate-10":"#0f172a","slate-11":"#020617","slate-12":"#01020a","slate-2":"#f1f5f9","slate-3":"#e2e8f0","slate-4":"#cbd5e1","slate-5":"#94a3b8","slate-6":"#64748b","slate-7":"#475569","slate-8":"#334155","slate-9":"#1e293b","stone-1":"#fafaf9","stone-10":"#1c1917","stone-11":"#0c0a09","stone-12":"#070605","stone-2":"#f5f5f4","stone-3":"#e7e5e4","stone-4":"#d6d3d1","stone-5":"#a8a29e","stone-6":"#78716c","stone-7":"#57534e","stone-8":"#44403c","stone-9":"#292524","teal-1":"#f0fdfa","teal-10":"#134e4a","teal-11":"#042f2e","teal-12":"#021c1b","teal-2":"#ccfbf1","teal-3":"#99f6e4","teal-4":"#5eead4","teal-5":"#2dd4bf","teal-6":"#14b8a6","teal-7":"#0d9488","teal-8":"#0f766e","teal-9":"#115e59",transparent:"transparent","violet-1":"#f5f3ff","violet-10":"#4c1d95","violet-11":"#2e1065","violet-12":"#1c0a3d","violet-2":"#ede9fe","violet-3":"#ddd6fe","violet-4":"#c4b5fd","violet-5":"#a78bfa","violet-6":"#8b5cf6","violet-7":"#7c3aed","violet-8":"#6d28d9","violet-9":"#5b21b6",white:"#ffffff","yellow-1":"#fefce8","yellow-10":"#713f12","yellow-11":"#422006","yellow-12":"#271304","yellow-2":"#fef9c3","yellow-3":"#fef08a","yellow-4":"#fde047","yellow-5":"#facc15","yellow-6":"#eab308","yellow-7":"#ca8a04","yellow-8":"#a16207","yellow-9":"#854d0e","zinc-1":"#fafafa","zinc-10":"#18181b","zinc-11":"#09090b","zinc-12":"#050506","zinc-2":"#f4f4f5","zinc-3":"#e4e4e7","zinc-4":"#d4d4d8","zinc-5":"#a1a1aa","zinc-6":"#71717a","zinc-7":"#52525b","zinc-8":"#3f3f46","zinc-9":"#27272a"};var v={colors:{...G,surface:{light:"#f9fafb",dark:"#111827"}},spacing:{0:0,px:1,"0.5":2,1:4,"1.5":6,2:8,"2.5":10,3:12,"3.5":14,4:16,5:20,6:24,7:28,8:32,9:36,10:40,11:44,12:48,14:56,16:64,20:80,24:96,28:112,32:128,36:144,40:160,44:176,48:192,52:208,56:224,60:240,64:256,72:288,80:320,96:384},screens:{sm:640,md:768,lg:1024,xl:1280,"2xl":1536},fontFamily:{sans:'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',serif:'ui-serif, Georgia, Cambria, "Times New Roman", Times, serif',mono:'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'},darkMode:"media",container:{}},E=v,N=JSON.stringify(v);function M(e){E=e,N=JSON.stringify(e)}function H(){return E}function J(){return N}var ke=new Set(["attribute","class","media"]);function V(){if(typeof document>"u")return;let e=getComputedStyle(document.documentElement).getPropertyValue("--kb-dark-mode").trim();!ke.has(e)||e===E.darkMode||M({...E,darkMode:e})}var j="kbach-reset",Ce=[{css:"*, *::before, *::after { box-sizing: border-box; border-width: 0; border-style: solid; border-color: currentColor; }"},{css:"body { margin: 0; padding: 0; }"},{css:"h1, h2, h3, h4, h5, h6 { margin: 0; font-size: inherit; font-weight: inherit; }",tags:["h1","h2","h3","h4","h5","h6"]},{css:"p { margin: 0; }",tags:["p"]},{css:"a { color: inherit; text-decoration: none; }",tags:["a"]},{css:"ul, ol { margin: 0; padding: 0; list-style: none; }",tags:["ul","ol"]},{css:"img, video, svg { display: block; max-width: 100%; }",tags:["img","video","svg"]},{css:"input:where(:not([type='checkbox']):not([type='radio'])), textarea { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; line-height: inherit; }",tags:["input","textarea"]},{css:":where(input[type='checkbox'], input[type='radio'], input[type='range']) { margin: 0; font: inherit; accent-color: currentColor; }",tags:["input"]},{css:"select { margin: 0; font: inherit; color: inherit; line-height: inherit; }",tags:["select"]},{css:"button { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; line-height: inherit; text-align: inherit; }",tags:["button"]},{css:"button, [role='button'] { cursor: pointer; }",tags:["button"]},{css:":disabled { cursor: default; }",tags:["button","input","select","textarea"]},{css:"textarea { resize: vertical; }",tags:["textarea"]},{css:"::placeholder { opacity: 1; }",tags:["input","textarea"]},{css:"input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-outer-spin-button { margin: 0; }",tags:["input"]},{css:"input[type='search']::-webkit-search-decoration, input[type='search']::-webkit-search-cancel-button { -webkit-appearance: none; }",tags:["input"]},{css:"fieldset { padding: 0; margin: 0; }",tags:["fieldset"]},{css:"table { border-collapse: collapse; border-spacing: 0; }",tags:["table"]}],Y=Ce.map(e=>e.css).join(`
2
+ `);var xe=!1;var h=null,K=[],I=new Map;function Te(){return h||(h=document.createElement("style"),h.setAttribute("data-kbach",""),document.head.appendChild(h),h)}function Ee(e){let t=0,n=K.length;for(;t<n;){let r=t+n>>>1;I.get(K[r])>e?n=r:t=r+1}return t}function ve(e,t){if(I.has(e))return;let n=Te().sheet;if(!n)return;let r=Ee(t);try{n.insertRule(e,r)}catch{return}K.splice(r,0,e),I.set(e,t)}var q=!1;function Me(){if(q||(q=!0,document.getElementById(j)))return;let e=document.createElement("style");e.id=j,e.textContent=Y,document.head.insertBefore(e,document.head.firstChild)}var Re=500,k=new Map,X=null;function Q(e){let t=J();t!==X&&(k.clear(),X=t);let n=k.get(e);if(n===void 0){let a=U(e,t);n=JSON.parse(a),k.size>=Re&&k.clear(),k.set(e,n)}let{className:r,rules:o}=n;if(!xe){Me();for(let{rule:a,order:i}of o)ve(a,i)}return r}function m(e){let t=/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(e.trim());if(!t)return null;let n=t[1];n.length===3&&(n=n.split("").map(o=>o+o).join(""));let r=parseInt(n,16);return[r>>16&255,r>>8&255,r&255]}function Se(e,t){switch(e){case"class":return`.dark { ${t} }`;case"media":return`@media (prefers-color-scheme: dark) { :root { ${t} } }`;default:return`[data-theme="dark"] { ${t} }`}}var Z="kbach-colors",ee=null;function te(e){if(typeof document>"u"||ee===e)return;ee=e;let t=[],n=[];for(let[o,a]of Object.entries(e.colors)){if(typeof a=="string")continue;let i=m(a.light),s=m(a.dark);!i||!s||(t.push(`--kb-color-${o}: ${i.join(",")};`),n.push(`--kb-color-${o}: ${s.join(",")};`))}let r=document.getElementById(Z);if(t.length===0){r?.remove();return}r||(r=document.createElement("style"),r.id=Z,document.head.insertBefore(r,document.head.firstChild)),r.textContent=`:root { ${t.join(" ")} }
3
+ ${Se(e.darkMode,n.join(" "))}`}var ne="kbach-theme",De=new Set;function Ae(){return typeof window>"u"||!window.matchMedia?!1:window.matchMedia("(prefers-color-scheme: dark)").matches}function re(){return typeof window>"u"?null:window.localStorage}function je(){let e=re();if(!e)return null;try{let t=e.getItem(ne);return t==="light"||t==="dark"||t==="system"?t:null}catch{return null}}function oe(e){return e==="system"?Ae():e==="dark"}function R(e){if(typeof document>"u")return;let t=document.documentElement,n=H().darkMode;t.classList.toggle("dark",n==="class"&&e),t.classList.toggle("light",n==="class"&&!e),n==="attribute"?t.setAttribute("data-theme",e?"dark":"light"):t.removeAttribute("data-theme")}V();var Ke=je();var O=Ke??"system",b=oe(O);R(b);if(typeof window<"u"&&window.matchMedia){let e=window.matchMedia("(prefers-color-scheme: dark)"),t=n=>{O==="system"&&(b=n.matches,R(b),ie())};e.addEventListener?e.addEventListener("change",t):e.addListener(t)}function ie(){for(let e of De)e()}function L(){R(b)}function F(e){O=e,b=oe(e),R(b);let t=re();if(t)try{t.setItem(ne,e)}catch{}ie()}function se(){F(b?"light":"dark")}function z(e,t){let n=m(e);if(!n)return e;let r=Number(t)/100,o=Number.isFinite(r)?Math.min(1,Math.max(0,r)):1;return`rgba(${n.join(",")},${o})`}function ae(e,t){if(typeof e!="string")return e;let n=e.indexOf("/"),r=n===-1?e:e.slice(0,n),o=n===-1?void 0:e.slice(n+1),a=t(r);return a===void 0?e:o===void 0?a:typeof a=="string"?z(a,o):{light:z(a.light,o),dark:z(a.dark,o)}}function Ie(e,t){let{dark:n,...r}=e,o={},a=i=>o[i]??t[i];for(let[i,s]of Object.entries(r))typeof s=="object"&&s!==null&&!("light"in s)&&!("dark"in s)||(o[i]=ae(s,a));if(n){let i={},s=c=>{let u=i[c]??t[c];return typeof u=="string"?u:void 0};for(let[c,u]of Object.entries(n)){let g=ae(u,s);i[c]=typeof g=="string"?g:g.dark}for(let[c,u]of Object.entries(i)){let g=o[c];g===void 0||typeof g!="string"||(o[c]={light:g,dark:u})}}return o}function ce(e){let t={...v,...e.theme};return e.extend?.colors&&(t={...t,colors:{...t.colors,...Ie(e.extend.colors,t.colors)}}),e.extend?.spacing&&(t={...t,spacing:{...t.spacing,...e.extend.spacing}}),e.extend?.screens&&(t={...t,screens:{...t.screens,...e.extend.screens}}),e.extend?.fontFamily&&(t={...t,fontFamily:{...t.fontFamily,...e.extend.fontFamily}}),e.extend?.container&&(t={...t,container:{...t.container,...e.extend.container}}),e.darkMode&&(t={...t,darkMode:e.darkMode}),t}function fe(e){let t=!1,n={};for(let[r,o]of Object.entries(e.colors))typeof o!="string"&&m(o.light)&&m(o.dark)?(n[r]=`rgb(var(--kb-color-${r}))`,t=!0):n[r]=o;return t?{...e,colors:n}:e}function Oe(e){let t=e.getAttribute("class");return t?t.split(/\s+/).filter(Boolean):[]}function le({document:e,wasmUrl:t,config:n}){let r=new Set,o=!1,a=l=>{if(!o)return;let d=[];for(let y of l)for(let p of Oe(y))r.has(p)||(r.add(p),d.push(p));d.length>0&&Q(d.join(" "))},i=l=>[l,...Array.from(l.querySelectorAll("[class]"))],s=()=>a(i(e.documentElement)),c=l=>{let d=ce(l??{});M(fe(d)),te(d),L()};c(n);let u=new e.defaultView.MutationObserver(l=>{let d=[];for(let y of l)if(y.type==="attributes")d.push(y.target);else for(let p of Array.from(y.addedNodes))p.nodeType===1&&d.push(...i(p));a(d)});return{ready:P(t).then(()=>{o=!0,s(),u.observe(e.documentElement,{subtree:!0,childList:!0,attributes:!0,attributeFilter:["class"]})}),config(l){c(l),r.clear(),s()},refresh:s,setMode:F,toggle:se}}var de=document.currentScript,Le=de?.dataset.wasm??new URL("kbach_core_engine_bg.wasm",de?.src||document.baseURI);window.Kbach=le({document,wasmUrl:Le,config:window.kbachConfig});})();
Binary file
package/package.json CHANGED
@@ -1,6 +1,46 @@
1
1
  {
2
2
  "name": "@kbach/browser",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0-beta.0",
4
+ "description": "Kbach from a CDN: one <script> tag styles a plain HTML page with Kbach's utility classes. No build step, no bundler, no React.",
5
+ "keywords": [
6
+ "css",
7
+ "cdn",
8
+ "wasm",
9
+ "tailwind",
10
+ "utility-first",
11
+ "styling"
12
+ ],
13
+ "license": "MIT",
14
+ "author": "semsakadanupol",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/semsakadanupol/kbach.git",
18
+ "directory": "packages/browser"
19
+ },
20
+ "homepage": "https://github.com/semsakadanupol/kbach#readme",
21
+ "bugs": {
22
+ "url": "https://github.com/semsakadanupol/kbach/issues"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "AGENTS.md"
30
+ ],
31
+ "main": "./dist/kbach.js",
32
+ "unpkg": "./dist/kbach.js",
33
+ "jsdelivr": "./dist/kbach.js",
34
+ "scripts": {
35
+ "build": "tsup && node scripts/copy-wasm.mjs && node scripts/verify-bundle.mjs",
36
+ "test": "vitest run",
37
+ "lint": "tsc --noEmit"
38
+ },
39
+ "devDependencies": {
40
+ "@kbach/react": "*",
41
+ "jsdom": "^30.0.1",
42
+ "tsup": "^8.0.0",
43
+ "typescript": "^6.0.0",
44
+ "vitest": "^1.0.0"
45
+ }
46
+ }