@kbach/react 0.6.8 → 0.6.10
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/README.md +31 -12
- package/dist/{chunk-ELFAWQI6.mjs → chunk-CJRRPENF.mjs} +28 -2
- package/dist/{chunk-HOWJ3U6D.mjs → chunk-RWQKYQWN.mjs} +1 -1
- package/dist/index.d.mts +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +51 -18
- package/dist/index.mjs +12 -1
- package/dist/jsx-dev-runtime.js +42 -18
- package/dist/jsx-dev-runtime.mjs +2 -2
- package/dist/jsx-runtime.js +42 -18
- package/dist/jsx-runtime.mjs +2 -2
- package/dist/vite-plugin.js +48 -11
- package/dist/vite-plugin.mjs +48 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ npm install @kbach/react
|
|
|
20
20
|
|
|
21
21
|
## Setup
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Step 1 is always required. After that, pick **one** of the two setups below — Quick if you just want styles working, Static CSS if you're shipping to production and/or using SSR.
|
|
24
24
|
|
|
25
25
|
### 1. Configure the JSX runtime
|
|
26
26
|
|
|
@@ -39,25 +39,30 @@ Don't also pass `jsxImportSource` to `@vitejs/plugin-react` (or any other bundle
|
|
|
39
39
|
|
|
40
40
|
Alternatives if you can't use tsconfig: a per-file `/** @jsxImportSource @kbach/react */` pragma comment, or `['@babel/preset-react', { runtime: 'automatic', importSource: '@kbach/react' }]` in `babel.config.js` for non-Vite/non-SWC toolchains.
|
|
41
41
|
|
|
42
|
-
###
|
|
42
|
+
### 2a. Quick setup (runtime CSS injection)
|
|
43
|
+
|
|
44
|
+
No build plugin, works with any bundler (Vite, webpack, Turbopack, Metro-for-web, …). Styles are generated and injected into a `<style>` tag by client-side JS the moment `ThemeProvider` mounts.
|
|
43
45
|
|
|
44
46
|
```jsx
|
|
45
|
-
import { ThemeProvider } from '@kbach/react';
|
|
47
|
+
import { ThemeProvider, KbachReset } from '@kbach/react';
|
|
46
48
|
|
|
47
49
|
export default function Root() {
|
|
48
50
|
return (
|
|
49
51
|
<ThemeProvider defaultMode="system">
|
|
52
|
+
<KbachReset />
|
|
50
53
|
<App />
|
|
51
54
|
</ThemeProvider>
|
|
52
55
|
);
|
|
53
56
|
}
|
|
54
57
|
```
|
|
55
58
|
|
|
56
|
-
|
|
59
|
+
`<KbachReset />` renders Kbach's base browser-default reset (borderless buttons/inputs, visible checkboxes/radios, no arrow-less `<select>`, etc. — see [CSS resets](#css-resets) below) as a plain `<style>` tag instead of waiting for the client-side injector. On a plain client-rendered app (Vite CSR, Create React App, …) it's a nice-to-have, since the injector already runs before first paint there. **On SSR** (Next.js, React Router framework mode, Remix, …) it's the difference between correct styling from the first byte and a flash of raw browser defaults (default button border, unstyled `<select>`, serif-ish fonts, …) that only clears up once hydration finishes — the server has no JS to run the injector, but it *can* render this component, since it's just JSX. Put it as high in `<head>` as your framework allows; the snippet above (right after `ThemeProvider` opens) works everywhere `<head>` isn't directly reachable.
|
|
60
|
+
|
|
61
|
+
That's the whole setup. `@kbach/react` ships its own `"use client"` directive, so both of these work directly in a Next.js App Router Server Component tree — no manual client wrapper needed.
|
|
57
62
|
|
|
58
|
-
###
|
|
63
|
+
### 2b. Static CSS setup (recommended for production / SSR, Vite only)
|
|
59
64
|
|
|
60
|
-
|
|
65
|
+
Zero runtime cost: a Vite plugin scans your source at build time and writes real CSS into a file you import, so styles ship as an ordinary stylesheet with no client-side generation step at all — nothing to flash-of-unstyled before. This is the better choice for any Vite-based SSR framework (React Router, SvelteKit-style meta-frameworks, etc.) over the Quick setup + `<KbachReset />` combination, since it covers *every* class, not just the base reset.
|
|
61
66
|
|
|
62
67
|
```ts
|
|
63
68
|
// vite.config.ts
|
|
@@ -88,15 +93,26 @@ Then create an empty `kbach.css` anywhere in your project and import it once in
|
|
|
88
93
|
import './kbach.css';
|
|
89
94
|
```
|
|
90
95
|
|
|
96
|
+
```jsx
|
|
97
|
+
import { ThemeProvider } from '@kbach/react';
|
|
98
|
+
// No <KbachReset /> needed — kbach.css already includes the same base reset.
|
|
99
|
+
|
|
100
|
+
export default function Root() {
|
|
101
|
+
return (
|
|
102
|
+
<ThemeProvider defaultMode="system">
|
|
103
|
+
<App />
|
|
104
|
+
</ThemeProvider>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
91
109
|
The plugin scans your source files and writes generated CSS between the `/* kbach:start */`/`/* kbach:end */` markers. Importing `kbach.css` automatically disables runtime injection — all styles come from the file instead. On HMR, only the changed file is rescanned; unchanged tokens reuse cached CSS.
|
|
92
110
|
|
|
93
111
|
While it's scanning, the plugin also indexes every `.css`/`.scss`/`.sass`/`.less` file under the same directories and warns in the terminal (not the browser console) for any class that's neither a real Kbach utility nor defined anywhere in those stylesheets — likely a typo. A class you've defined yourself elsewhere (CSS Modules, styled-components, a third-party component's class) is recognized as soon as something in the project literally has a `.that-class-name` rule, so it won't get flagged just because Kbach itself doesn't know it. Each warning includes a `file:line:column` location that most terminals (VS Code's integrated terminal included) turn into a clickable link straight to that class.
|
|
94
112
|
|
|
95
113
|
## Next.js
|
|
96
114
|
|
|
97
|
-
Step 1 (tsconfig `jsxImportSource`)
|
|
98
|
-
|
|
99
|
-
The "Optional: static CSS" step above doesn't apply — Next.js builds with webpack/Turbopack, not Vite, so there's currently no static-CSS build step for it. Styles fall back to runtime injection, which means the same first-paint FOUC note above applies: expect a brief flash of unstyled content before hydration completes. This is a known limitation, not a per-project bug to work around.
|
|
115
|
+
Step 1 (tsconfig `jsxImportSource`) above applies as-is — Next.js's SWC compiler reads `jsxImportSource` from `tsconfig.json` the same way Vite does. For step 2, use **Quick setup (2a)** — Next.js builds with webpack/Turbopack, not Vite, so the static-CSS plugin (2b) doesn't apply. Render `<KbachReset />` once in the root App Router `layout.tsx` (inside `<head>`, or right after `<ThemeProvider>` opens) so Server Component HTML gets the base reset without waiting on hydration; `@kbach/react` shipping its own `"use client"` directive means both `ThemeProvider` and `KbachReset` work directly in that Server Component tree with no wrapper of your own needed.
|
|
100
116
|
|
|
101
117
|
## React Router
|
|
102
118
|
|
|
@@ -117,6 +133,8 @@ export default defineConfig({
|
|
|
117
133
|
|
|
118
134
|
If you do add `kbach()` to `vite.config.ts` and it detects both plugins active together, it prints a warning explaining exactly this at dev-server startup — so this doesn't have to be a silent, hard-to-diagnose blank page.
|
|
119
135
|
|
|
136
|
+
Framework mode is SSR, so skipping the static-CSS plugin (2b) in favor of Quick setup (2a) means the initial server-rendered HTML has no CSS at all until hydration — render `<KbachReset />` in `root.tsx`'s `<Layout>` (inside `<head>`, alongside `<Links />`/`<Meta />`) if you go that route, otherwise expect raw browser defaults (e.g. the native `<button>` border) on first paint.
|
|
137
|
+
|
|
120
138
|
**Library mode (client-only, `createBrowserRouter`/`<BrowserRouter>`):** no SSR involved — set it up exactly like any other Vite + React app. Since there's no meta-framework Vite plugin providing JSX handling here, you likely do need `@vitejs/plugin-react({ jsxImportSource: '@kbach/react' })` (or rely on tsconfig alone — Vite's default esbuild-based transform reads it too).
|
|
121
139
|
|
|
122
140
|
## API
|
|
@@ -355,7 +373,7 @@ Opacity modifier: `bg-blue-6/50` (50% alpha), `bg-blue-6/[0.15]` (arbitrary)
|
|
|
355
373
|
|
|
356
374
|
## CSS resets
|
|
357
375
|
|
|
358
|
-
Included automatically in `kbach.css
|
|
376
|
+
Included automatically in `kbach.css`, in runtime injection, and in `<KbachReset />` (all three render the exact same rules — pick whichever setup you're using from [Setup](#setup) above):
|
|
359
377
|
|
|
360
378
|
- `*, *::before, *::after { box-sizing: border-box; border-width: 0; border-style: solid; border-color: currentColor }` — border utilities like `border-2 border-gray-4` work on any element without also needing `border-solid`
|
|
361
379
|
- `body { margin: 0; padding: 0 }`
|
|
@@ -363,8 +381,9 @@ Included automatically in `kbach.css` and in runtime injection:
|
|
|
363
381
|
- `p`, `ul`, `ol` — margin and list style cleared
|
|
364
382
|
- `a` — color and underline cleared (inherit from parent)
|
|
365
383
|
- `img`, `video`, `svg` — `display: block; max-width: 100%`
|
|
366
|
-
- `
|
|
367
|
-
- `
|
|
384
|
+
- `button`, text-like `input`s (not checkbox/radio), `textarea` — appearance, border, padding cleared, so `border-`/`bg-`/`rounded-`/`p-` utilities fully restyle them
|
|
385
|
+
- `input[type=checkbox]`, `input[type=radio]`, `select` — native rendering kept (stripping it would hide the checkmark/dot/dropdown-arrow with nothing to replace them); only typography and spacing are normalized, and checkbox/radio/range get `accent-color: currentColor` so they still pick up your text color instead of the OS default blue
|
|
386
|
+
- `fieldset`, `table` — padding/margin/border-collapse cleared
|
|
368
387
|
|
|
369
388
|
## Configuration
|
|
370
389
|
|
|
@@ -2549,6 +2549,7 @@ function findOuterColon(s) {
|
|
|
2549
2549
|
}
|
|
2550
2550
|
|
|
2551
2551
|
// src/core/reset.ts
|
|
2552
|
+
var RESET_STYLE_ID = "kbach-reset";
|
|
2552
2553
|
var BASE_RESET = [
|
|
2553
2554
|
// border-style: solid means border-N utilities show a visible border without an extra border-solid class.
|
|
2554
2555
|
// border-width: 0 keeps all elements borderless by default.
|
|
@@ -2559,8 +2560,31 @@ var BASE_RESET = [
|
|
|
2559
2560
|
"a { color: inherit; text-decoration: none; }",
|
|
2560
2561
|
"ul, ol { margin: 0; padding: 0; list-style: none; }",
|
|
2561
2562
|
"img, video, svg { display: block; max-width: 100%; }",
|
|
2562
|
-
|
|
2563
|
+
// appearance: none is deliberately NOT applied to checkbox/radio/select below —
|
|
2564
|
+
// stripping it hides their native checkmark/arrow with nothing rendered in its
|
|
2565
|
+
// place, leaving an invisible checkbox or an arrow-less <select> that looks like
|
|
2566
|
+
// plain text. Text-like inputs, textarea, and button don't have that problem
|
|
2567
|
+
// (their native chrome is just a skin around content utilities can fully
|
|
2568
|
+
// restyle), so they keep the blank-canvas treatment.
|
|
2569
|
+
"input: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; }",
|
|
2570
|
+
// Native checkbox/radio/range still get typography + spacing normalized, and
|
|
2571
|
+
// accent-color re-themes their native indicator to the current text color
|
|
2572
|
+
// instead of the browser/OS default blue, so they stay on-brand without
|
|
2573
|
+
// needing to be rebuilt from scratch.
|
|
2574
|
+
"input[type='checkbox'], input[type='radio'], input[type='range'] { margin: 0; font: inherit; accent-color: currentColor; }",
|
|
2575
|
+
// select keeps its native chrome (border/background/arrow are all part of the
|
|
2576
|
+
// same OS-drawn widget that appearance: none would blank out) — only typography
|
|
2577
|
+
// and spacing are normalized so it still matches surrounding text.
|
|
2578
|
+
"select { margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
2563
2579
|
"button { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; line-height: inherit; text-align: inherit; }",
|
|
2580
|
+
"button, [role='button'] { cursor: pointer; }",
|
|
2581
|
+
":disabled { cursor: default; }",
|
|
2582
|
+
"textarea { resize: vertical; }",
|
|
2583
|
+
// Firefox renders placeholders at ~54% opacity by default; every other browser uses 1 —
|
|
2584
|
+
// normalize to 1 so placeholder color is consistent and fully controlled by the placeholder: modifier.
|
|
2585
|
+
"::placeholder { opacity: 1; }",
|
|
2586
|
+
"input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-outer-spin-button { margin: 0; }",
|
|
2587
|
+
"input[type='search']::-webkit-search-decoration, input[type='search']::-webkit-search-cancel-button { -webkit-appearance: none; }",
|
|
2564
2588
|
"fieldset { padding: 0; margin: 0; }",
|
|
2565
2589
|
"table { border-collapse: collapse; border-spacing: 0; }"
|
|
2566
2590
|
].join("\n");
|
|
@@ -2729,7 +2753,7 @@ function getGlobalStyleEl() {
|
|
|
2729
2753
|
}
|
|
2730
2754
|
function injectGlobalStyles(theme) {
|
|
2731
2755
|
if (typeof document === "undefined" || isRuntimeCSSDisabled()) return;
|
|
2732
|
-
const rules = [BASE_RESET];
|
|
2756
|
+
const rules = document.getElementById(RESET_STYLE_ID) ? [] : [BASE_RESET];
|
|
2733
2757
|
const ff = theme.fontFamily;
|
|
2734
2758
|
if (ff?.sans && ff.sans !== "System") {
|
|
2735
2759
|
const family = Array.isArray(ff.sans) ? ff.sans.join(", ") : ff.sans;
|
|
@@ -3697,6 +3721,8 @@ InteractiveWrapper.displayName = "Kbach.InteractiveWrapper";
|
|
|
3697
3721
|
export {
|
|
3698
3722
|
defaultColors,
|
|
3699
3723
|
defaultTheme,
|
|
3724
|
+
RESET_STYLE_ID,
|
|
3725
|
+
BASE_RESET,
|
|
3700
3726
|
isWeb,
|
|
3701
3727
|
isNative,
|
|
3702
3728
|
setResolveTarget,
|
package/dist/index.d.mts
CHANGED
|
@@ -271,6 +271,24 @@ interface ThemeProviderProps {
|
|
|
271
271
|
}
|
|
272
272
|
declare function ThemeProvider({ children, defaultMode, colorScheme, windowWidth: windowWidthProp, config: configOverride, disablePersistence, }: ThemeProviderProps): React__default.JSX.Element;
|
|
273
273
|
|
|
274
|
+
/**
|
|
275
|
+
* Renders Kbach's base browser-default reset (borderless button/input,
|
|
276
|
+
* visible checkbox/radio, no arrow-less <select>, etc.) as a plain <style>
|
|
277
|
+
* tag so it's part of the page's initial HTML.
|
|
278
|
+
*
|
|
279
|
+
* Runtime-only setups (no Vite plugin / static kbach.css) otherwise only get
|
|
280
|
+
* this reset once ThemeProvider's client-side effect runs — fine for a plain
|
|
281
|
+
* CSR app, but under SSR the server has no JS to run it, so the first paint
|
|
282
|
+
* ships with raw browser defaults (e.g. the native button border) until
|
|
283
|
+
* hydration catches up. Render this once, as high in <head> as your
|
|
284
|
+
* framework allows, to close that gap.
|
|
285
|
+
*
|
|
286
|
+
* Not needed if you're on the static-CSS setup — kbach.css already includes
|
|
287
|
+
* the same reset, and the runtime injector detects this tag and skips adding
|
|
288
|
+
* it a second time either way.
|
|
289
|
+
*/
|
|
290
|
+
declare function KbachReset(): React.JSX.Element | null;
|
|
291
|
+
|
|
274
292
|
interface StyledProps {
|
|
275
293
|
/** Additional utility classes applied on top of the base classes */
|
|
276
294
|
kb?: string;
|
|
@@ -417,4 +435,4 @@ declare function kb(classString: string, isDark?: boolean): StyleValue | string;
|
|
|
417
435
|
*/
|
|
418
436
|
declare function cx(...classes: Array<string | false | null | undefined>): string;
|
|
419
437
|
|
|
420
|
-
export { type ColorScale, type ColorsAPI, type FrameworkConfig, type InteractionState, InteractiveWrapper, type InteractiveWrapperProps, type ParsedClass, type PluginAPI, type ResolvedConfig, type ResolvedStyle, type StyleValue, type StyledProps, type ThemeConfig, ThemeContext, type ThemeContextValue, type ThemeMode, ThemeProvider, type ThemeProviderProps, buildConfig, clearCache, cx, defaultColors, defaultTheme, disableRuntimeCSS, flatten, getConfig, initConfig, kb, normalizeClassString, parseClass, parseClasses, resolve, setResolveTarget, splitClassTokens, styled, updateConfig, useBreakpoint, useColors, useGlobalDarkMode, useIsDark, useResolvedStyle, useResponsive, useStyles, useTheme, wrapColors };
|
|
438
|
+
export { type ColorScale, type ColorsAPI, type FrameworkConfig, type InteractionState, InteractiveWrapper, type InteractiveWrapperProps, KbachReset, type ParsedClass, type PluginAPI, type ResolvedConfig, type ResolvedStyle, type StyleValue, type StyledProps, type ThemeConfig, ThemeContext, type ThemeContextValue, type ThemeMode, ThemeProvider, type ThemeProviderProps, buildConfig, clearCache, cx, defaultColors, defaultTheme, disableRuntimeCSS, flatten, getConfig, initConfig, kb, normalizeClassString, parseClass, parseClasses, resolve, setResolveTarget, splitClassTokens, styled, updateConfig, useBreakpoint, useColors, useGlobalDarkMode, useIsDark, useResolvedStyle, useResponsive, useStyles, useTheme, wrapColors };
|
package/dist/index.d.ts
CHANGED
|
@@ -271,6 +271,24 @@ interface ThemeProviderProps {
|
|
|
271
271
|
}
|
|
272
272
|
declare function ThemeProvider({ children, defaultMode, colorScheme, windowWidth: windowWidthProp, config: configOverride, disablePersistence, }: ThemeProviderProps): React__default.JSX.Element;
|
|
273
273
|
|
|
274
|
+
/**
|
|
275
|
+
* Renders Kbach's base browser-default reset (borderless button/input,
|
|
276
|
+
* visible checkbox/radio, no arrow-less <select>, etc.) as a plain <style>
|
|
277
|
+
* tag so it's part of the page's initial HTML.
|
|
278
|
+
*
|
|
279
|
+
* Runtime-only setups (no Vite plugin / static kbach.css) otherwise only get
|
|
280
|
+
* this reset once ThemeProvider's client-side effect runs — fine for a plain
|
|
281
|
+
* CSR app, but under SSR the server has no JS to run it, so the first paint
|
|
282
|
+
* ships with raw browser defaults (e.g. the native button border) until
|
|
283
|
+
* hydration catches up. Render this once, as high in <head> as your
|
|
284
|
+
* framework allows, to close that gap.
|
|
285
|
+
*
|
|
286
|
+
* Not needed if you're on the static-CSS setup — kbach.css already includes
|
|
287
|
+
* the same reset, and the runtime injector detects this tag and skips adding
|
|
288
|
+
* it a second time either way.
|
|
289
|
+
*/
|
|
290
|
+
declare function KbachReset(): React.JSX.Element | null;
|
|
291
|
+
|
|
274
292
|
interface StyledProps {
|
|
275
293
|
/** Additional utility classes applied on top of the base classes */
|
|
276
294
|
kb?: string;
|
|
@@ -417,4 +435,4 @@ declare function kb(classString: string, isDark?: boolean): StyleValue | string;
|
|
|
417
435
|
*/
|
|
418
436
|
declare function cx(...classes: Array<string | false | null | undefined>): string;
|
|
419
437
|
|
|
420
|
-
export { type ColorScale, type ColorsAPI, type FrameworkConfig, type InteractionState, InteractiveWrapper, type InteractiveWrapperProps, type ParsedClass, type PluginAPI, type ResolvedConfig, type ResolvedStyle, type StyleValue, type StyledProps, type ThemeConfig, ThemeContext, type ThemeContextValue, type ThemeMode, ThemeProvider, type ThemeProviderProps, buildConfig, clearCache, cx, defaultColors, defaultTheme, disableRuntimeCSS, flatten, getConfig, initConfig, kb, normalizeClassString, parseClass, parseClasses, resolve, setResolveTarget, splitClassTokens, styled, updateConfig, useBreakpoint, useColors, useGlobalDarkMode, useIsDark, useResolvedStyle, useResponsive, useStyles, useTheme, wrapColors };
|
|
438
|
+
export { type ColorScale, type ColorsAPI, type FrameworkConfig, type InteractionState, InteractiveWrapper, type InteractiveWrapperProps, KbachReset, type ParsedClass, type PluginAPI, type ResolvedConfig, type ResolvedStyle, type StyleValue, type StyledProps, type ThemeConfig, ThemeContext, type ThemeContextValue, type ThemeMode, ThemeProvider, type ThemeProviderProps, buildConfig, clearCache, cx, defaultColors, defaultTheme, disableRuntimeCSS, flatten, getConfig, initConfig, kb, normalizeClassString, parseClass, parseClasses, resolve, setResolveTarget, splitClassTokens, styled, updateConfig, useBreakpoint, useColors, useGlobalDarkMode, useIsDark, useResolvedStyle, useResponsive, useStyles, useTheme, wrapColors };
|
package/dist/index.js
CHANGED
|
@@ -32,6 +32,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
32
32
|
var index_exports = {};
|
|
33
33
|
__export(index_exports, {
|
|
34
34
|
InteractiveWrapper: () => InteractiveWrapper,
|
|
35
|
+
KbachReset: () => KbachReset,
|
|
35
36
|
ThemeContext: () => ThemeContext,
|
|
36
37
|
ThemeProvider: () => ThemeProvider,
|
|
37
38
|
buildConfig: () => buildConfig,
|
|
@@ -627,6 +628,47 @@ var defaultTheme = {
|
|
|
627
628
|
}
|
|
628
629
|
};
|
|
629
630
|
|
|
631
|
+
// src/core/reset.ts
|
|
632
|
+
var RESET_STYLE_ID = "kbach-reset";
|
|
633
|
+
var BASE_RESET = [
|
|
634
|
+
// border-style: solid means border-N utilities show a visible border without an extra border-solid class.
|
|
635
|
+
// border-width: 0 keeps all elements borderless by default.
|
|
636
|
+
"*, *::before, *::after { box-sizing: border-box; border-width: 0; border-style: solid; border-color: currentColor; }",
|
|
637
|
+
"body { margin: 0; padding: 0; }",
|
|
638
|
+
"h1, h2, h3, h4, h5, h6 { margin: 0; font-size: inherit; font-weight: inherit; }",
|
|
639
|
+
"p { margin: 0; }",
|
|
640
|
+
"a { color: inherit; text-decoration: none; }",
|
|
641
|
+
"ul, ol { margin: 0; padding: 0; list-style: none; }",
|
|
642
|
+
"img, video, svg { display: block; max-width: 100%; }",
|
|
643
|
+
// appearance: none is deliberately NOT applied to checkbox/radio/select below —
|
|
644
|
+
// stripping it hides their native checkmark/arrow with nothing rendered in its
|
|
645
|
+
// place, leaving an invisible checkbox or an arrow-less <select> that looks like
|
|
646
|
+
// plain text. Text-like inputs, textarea, and button don't have that problem
|
|
647
|
+
// (their native chrome is just a skin around content utilities can fully
|
|
648
|
+
// restyle), so they keep the blank-canvas treatment.
|
|
649
|
+
"input: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; }",
|
|
650
|
+
// Native checkbox/radio/range still get typography + spacing normalized, and
|
|
651
|
+
// accent-color re-themes their native indicator to the current text color
|
|
652
|
+
// instead of the browser/OS default blue, so they stay on-brand without
|
|
653
|
+
// needing to be rebuilt from scratch.
|
|
654
|
+
"input[type='checkbox'], input[type='radio'], input[type='range'] { margin: 0; font: inherit; accent-color: currentColor; }",
|
|
655
|
+
// select keeps its native chrome (border/background/arrow are all part of the
|
|
656
|
+
// same OS-drawn widget that appearance: none would blank out) — only typography
|
|
657
|
+
// and spacing are normalized so it still matches surrounding text.
|
|
658
|
+
"select { margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
659
|
+
"button { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; line-height: inherit; text-align: inherit; }",
|
|
660
|
+
"button, [role='button'] { cursor: pointer; }",
|
|
661
|
+
":disabled { cursor: default; }",
|
|
662
|
+
"textarea { resize: vertical; }",
|
|
663
|
+
// Firefox renders placeholders at ~54% opacity by default; every other browser uses 1 —
|
|
664
|
+
// normalize to 1 so placeholder color is consistent and fully controlled by the placeholder: modifier.
|
|
665
|
+
"::placeholder { opacity: 1; }",
|
|
666
|
+
"input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-outer-spin-button { margin: 0; }",
|
|
667
|
+
"input[type='search']::-webkit-search-decoration, input[type='search']::-webkit-search-cancel-button { -webkit-appearance: none; }",
|
|
668
|
+
"fieldset { padding: 0; margin: 0; }",
|
|
669
|
+
"table { border-collapse: collapse; border-spacing: 0; }"
|
|
670
|
+
].join("\n");
|
|
671
|
+
|
|
630
672
|
// src/core/cache.ts
|
|
631
673
|
var LRUCache = class {
|
|
632
674
|
constructor(capacity = 1e4, onEvict) {
|
|
@@ -2667,23 +2709,6 @@ function findOuterColon(s) {
|
|
|
2667
2709
|
return -1;
|
|
2668
2710
|
}
|
|
2669
2711
|
|
|
2670
|
-
// src/core/reset.ts
|
|
2671
|
-
var BASE_RESET = [
|
|
2672
|
-
// border-style: solid means border-N utilities show a visible border without an extra border-solid class.
|
|
2673
|
-
// border-width: 0 keeps all elements borderless by default.
|
|
2674
|
-
"*, *::before, *::after { box-sizing: border-box; border-width: 0; border-style: solid; border-color: currentColor; }",
|
|
2675
|
-
"body { margin: 0; padding: 0; }",
|
|
2676
|
-
"h1, h2, h3, h4, h5, h6 { margin: 0; font-size: inherit; font-weight: inherit; }",
|
|
2677
|
-
"p { margin: 0; }",
|
|
2678
|
-
"a { color: inherit; text-decoration: none; }",
|
|
2679
|
-
"ul, ol { margin: 0; padding: 0; list-style: none; }",
|
|
2680
|
-
"img, video, svg { display: block; max-width: 100%; }",
|
|
2681
|
-
"input, textarea, select { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
2682
|
-
"button { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; line-height: inherit; text-align: inherit; }",
|
|
2683
|
-
"fieldset { padding: 0; margin: 0; }",
|
|
2684
|
-
"table { border-collapse: collapse; border-spacing: 0; }"
|
|
2685
|
-
].join("\n");
|
|
2686
|
-
|
|
2687
2712
|
// src/core/responsiveStore.ts
|
|
2688
2713
|
var RESPONSIVE_KEY = "__kbach_responsive__";
|
|
2689
2714
|
function getStore() {
|
|
@@ -2807,7 +2832,7 @@ function getGlobalStyleEl() {
|
|
|
2807
2832
|
}
|
|
2808
2833
|
function injectGlobalStyles(theme) {
|
|
2809
2834
|
if (typeof document === "undefined" || isRuntimeCSSDisabled()) return;
|
|
2810
|
-
const rules = [BASE_RESET];
|
|
2835
|
+
const rules = document.getElementById(RESET_STYLE_ID) ? [] : [BASE_RESET];
|
|
2811
2836
|
const ff = theme.fontFamily;
|
|
2812
2837
|
if (ff?.sans && ff.sans !== "System") {
|
|
2813
2838
|
const family = Array.isArray(ff.sans) ? ff.sans.join(", ") : ff.sans;
|
|
@@ -3501,6 +3526,13 @@ function ThemeProvider({
|
|
|
3501
3526
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeContext.Provider, { value: contextValue, children });
|
|
3502
3527
|
}
|
|
3503
3528
|
|
|
3529
|
+
// src/KbachReset.tsx
|
|
3530
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
3531
|
+
function KbachReset() {
|
|
3532
|
+
if (isNative) return null;
|
|
3533
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { id: RESET_STYLE_ID, children: BASE_RESET });
|
|
3534
|
+
}
|
|
3535
|
+
|
|
3504
3536
|
// src/styled.tsx
|
|
3505
3537
|
var import_react5 = __toESM(require("react"));
|
|
3506
3538
|
|
|
@@ -4134,6 +4166,7 @@ function cx(...classes) {
|
|
|
4134
4166
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4135
4167
|
0 && (module.exports = {
|
|
4136
4168
|
InteractiveWrapper,
|
|
4169
|
+
KbachReset,
|
|
4137
4170
|
ThemeContext,
|
|
4138
4171
|
ThemeProvider,
|
|
4139
4172
|
buildConfig,
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import {
|
|
3
|
+
BASE_RESET,
|
|
3
4
|
EMPTY_BREAKPOINTS,
|
|
4
5
|
InteractiveWrapper,
|
|
6
|
+
RESET_STYLE_ID,
|
|
5
7
|
buildConfig,
|
|
6
8
|
chain,
|
|
7
9
|
clearCache,
|
|
@@ -15,6 +17,7 @@ import {
|
|
|
15
17
|
hasInteractiveBuckets,
|
|
16
18
|
hasResponsiveBuckets,
|
|
17
19
|
initConfig,
|
|
20
|
+
isNative,
|
|
18
21
|
isWeb,
|
|
19
22
|
normalizeClassString,
|
|
20
23
|
onConfigChange,
|
|
@@ -36,7 +39,7 @@ import {
|
|
|
36
39
|
useGlobalDarkMode,
|
|
37
40
|
useGlobalWidth,
|
|
38
41
|
useSyncExternalStore
|
|
39
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-CJRRPENF.mjs";
|
|
40
43
|
|
|
41
44
|
// src/context.tsx
|
|
42
45
|
import { createContext, useContext } from "react";
|
|
@@ -262,6 +265,13 @@ function ThemeProvider({
|
|
|
262
265
|
return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: contextValue, children });
|
|
263
266
|
}
|
|
264
267
|
|
|
268
|
+
// src/KbachReset.tsx
|
|
269
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
270
|
+
function KbachReset() {
|
|
271
|
+
if (isNative) return null;
|
|
272
|
+
return /* @__PURE__ */ jsx2("style", { id: RESET_STYLE_ID, children: BASE_RESET });
|
|
273
|
+
}
|
|
274
|
+
|
|
265
275
|
// src/styled.tsx
|
|
266
276
|
import React2, {
|
|
267
277
|
forwardRef,
|
|
@@ -425,6 +435,7 @@ function cx(...classes) {
|
|
|
425
435
|
}
|
|
426
436
|
export {
|
|
427
437
|
InteractiveWrapper,
|
|
438
|
+
KbachReset,
|
|
428
439
|
ThemeContext,
|
|
429
440
|
ThemeProvider,
|
|
430
441
|
buildConfig,
|
package/dist/jsx-dev-runtime.js
CHANGED
|
@@ -583,6 +583,47 @@ var defaultTheme = {
|
|
|
583
583
|
}
|
|
584
584
|
};
|
|
585
585
|
|
|
586
|
+
// src/core/reset.ts
|
|
587
|
+
var RESET_STYLE_ID = "kbach-reset";
|
|
588
|
+
var BASE_RESET = [
|
|
589
|
+
// border-style: solid means border-N utilities show a visible border without an extra border-solid class.
|
|
590
|
+
// border-width: 0 keeps all elements borderless by default.
|
|
591
|
+
"*, *::before, *::after { box-sizing: border-box; border-width: 0; border-style: solid; border-color: currentColor; }",
|
|
592
|
+
"body { margin: 0; padding: 0; }",
|
|
593
|
+
"h1, h2, h3, h4, h5, h6 { margin: 0; font-size: inherit; font-weight: inherit; }",
|
|
594
|
+
"p { margin: 0; }",
|
|
595
|
+
"a { color: inherit; text-decoration: none; }",
|
|
596
|
+
"ul, ol { margin: 0; padding: 0; list-style: none; }",
|
|
597
|
+
"img, video, svg { display: block; max-width: 100%; }",
|
|
598
|
+
// appearance: none is deliberately NOT applied to checkbox/radio/select below —
|
|
599
|
+
// stripping it hides their native checkmark/arrow with nothing rendered in its
|
|
600
|
+
// place, leaving an invisible checkbox or an arrow-less <select> that looks like
|
|
601
|
+
// plain text. Text-like inputs, textarea, and button don't have that problem
|
|
602
|
+
// (their native chrome is just a skin around content utilities can fully
|
|
603
|
+
// restyle), so they keep the blank-canvas treatment.
|
|
604
|
+
"input: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; }",
|
|
605
|
+
// Native checkbox/radio/range still get typography + spacing normalized, and
|
|
606
|
+
// accent-color re-themes their native indicator to the current text color
|
|
607
|
+
// instead of the browser/OS default blue, so they stay on-brand without
|
|
608
|
+
// needing to be rebuilt from scratch.
|
|
609
|
+
"input[type='checkbox'], input[type='radio'], input[type='range'] { margin: 0; font: inherit; accent-color: currentColor; }",
|
|
610
|
+
// select keeps its native chrome (border/background/arrow are all part of the
|
|
611
|
+
// same OS-drawn widget that appearance: none would blank out) — only typography
|
|
612
|
+
// and spacing are normalized so it still matches surrounding text.
|
|
613
|
+
"select { margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
614
|
+
"button { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; line-height: inherit; text-align: inherit; }",
|
|
615
|
+
"button, [role='button'] { cursor: pointer; }",
|
|
616
|
+
":disabled { cursor: default; }",
|
|
617
|
+
"textarea { resize: vertical; }",
|
|
618
|
+
// Firefox renders placeholders at ~54% opacity by default; every other browser uses 1 —
|
|
619
|
+
// normalize to 1 so placeholder color is consistent and fully controlled by the placeholder: modifier.
|
|
620
|
+
"::placeholder { opacity: 1; }",
|
|
621
|
+
"input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-outer-spin-button { margin: 0; }",
|
|
622
|
+
"input[type='search']::-webkit-search-decoration, input[type='search']::-webkit-search-cancel-button { -webkit-appearance: none; }",
|
|
623
|
+
"fieldset { padding: 0; margin: 0; }",
|
|
624
|
+
"table { border-collapse: collapse; border-spacing: 0; }"
|
|
625
|
+
].join("\n");
|
|
626
|
+
|
|
586
627
|
// src/core/cache.ts
|
|
587
628
|
var LRUCache = class {
|
|
588
629
|
constructor(capacity = 1e4, onEvict) {
|
|
@@ -2626,23 +2667,6 @@ function findOuterColon(s) {
|
|
|
2626
2667
|
return -1;
|
|
2627
2668
|
}
|
|
2628
2669
|
|
|
2629
|
-
// src/core/reset.ts
|
|
2630
|
-
var BASE_RESET = [
|
|
2631
|
-
// border-style: solid means border-N utilities show a visible border without an extra border-solid class.
|
|
2632
|
-
// border-width: 0 keeps all elements borderless by default.
|
|
2633
|
-
"*, *::before, *::after { box-sizing: border-box; border-width: 0; border-style: solid; border-color: currentColor; }",
|
|
2634
|
-
"body { margin: 0; padding: 0; }",
|
|
2635
|
-
"h1, h2, h3, h4, h5, h6 { margin: 0; font-size: inherit; font-weight: inherit; }",
|
|
2636
|
-
"p { margin: 0; }",
|
|
2637
|
-
"a { color: inherit; text-decoration: none; }",
|
|
2638
|
-
"ul, ol { margin: 0; padding: 0; list-style: none; }",
|
|
2639
|
-
"img, video, svg { display: block; max-width: 100%; }",
|
|
2640
|
-
"input, textarea, select { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
2641
|
-
"button { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; line-height: inherit; text-align: inherit; }",
|
|
2642
|
-
"fieldset { padding: 0; margin: 0; }",
|
|
2643
|
-
"table { border-collapse: collapse; border-spacing: 0; }"
|
|
2644
|
-
].join("\n");
|
|
2645
|
-
|
|
2646
2670
|
// src/core/responsiveStore.ts
|
|
2647
2671
|
var RESPONSIVE_KEY = "__kbach_responsive__";
|
|
2648
2672
|
function getStore() {
|
|
@@ -2751,7 +2775,7 @@ function getGlobalStyleEl() {
|
|
|
2751
2775
|
}
|
|
2752
2776
|
function injectGlobalStyles(theme) {
|
|
2753
2777
|
if (typeof document === "undefined" || isRuntimeCSSDisabled()) return;
|
|
2754
|
-
const rules = [BASE_RESET];
|
|
2778
|
+
const rules = document.getElementById(RESET_STYLE_ID) ? [] : [BASE_RESET];
|
|
2755
2779
|
const ff = theme.fontFamily;
|
|
2756
2780
|
if (ff?.sans && ff.sans !== "System") {
|
|
2757
2781
|
const family = Array.isArray(ff.sans) ? ff.sans.join(", ") : ff.sans;
|
package/dist/jsx-dev-runtime.mjs
CHANGED
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
Fragment,
|
|
4
4
|
jsx,
|
|
5
5
|
jsxs
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-RWQKYQWN.mjs";
|
|
7
|
+
import "./chunk-CJRRPENF.mjs";
|
|
8
8
|
|
|
9
9
|
// src/jsx-dev-runtime.tsx
|
|
10
10
|
function jsxDEV(type, props, key, isStaticChildren, source, self) {
|
package/dist/jsx-runtime.js
CHANGED
|
@@ -584,6 +584,47 @@ var defaultTheme = {
|
|
|
584
584
|
}
|
|
585
585
|
};
|
|
586
586
|
|
|
587
|
+
// src/core/reset.ts
|
|
588
|
+
var RESET_STYLE_ID = "kbach-reset";
|
|
589
|
+
var BASE_RESET = [
|
|
590
|
+
// border-style: solid means border-N utilities show a visible border without an extra border-solid class.
|
|
591
|
+
// border-width: 0 keeps all elements borderless by default.
|
|
592
|
+
"*, *::before, *::after { box-sizing: border-box; border-width: 0; border-style: solid; border-color: currentColor; }",
|
|
593
|
+
"body { margin: 0; padding: 0; }",
|
|
594
|
+
"h1, h2, h3, h4, h5, h6 { margin: 0; font-size: inherit; font-weight: inherit; }",
|
|
595
|
+
"p { margin: 0; }",
|
|
596
|
+
"a { color: inherit; text-decoration: none; }",
|
|
597
|
+
"ul, ol { margin: 0; padding: 0; list-style: none; }",
|
|
598
|
+
"img, video, svg { display: block; max-width: 100%; }",
|
|
599
|
+
// appearance: none is deliberately NOT applied to checkbox/radio/select below —
|
|
600
|
+
// stripping it hides their native checkmark/arrow with nothing rendered in its
|
|
601
|
+
// place, leaving an invisible checkbox or an arrow-less <select> that looks like
|
|
602
|
+
// plain text. Text-like inputs, textarea, and button don't have that problem
|
|
603
|
+
// (their native chrome is just a skin around content utilities can fully
|
|
604
|
+
// restyle), so they keep the blank-canvas treatment.
|
|
605
|
+
"input: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; }",
|
|
606
|
+
// Native checkbox/radio/range still get typography + spacing normalized, and
|
|
607
|
+
// accent-color re-themes their native indicator to the current text color
|
|
608
|
+
// instead of the browser/OS default blue, so they stay on-brand without
|
|
609
|
+
// needing to be rebuilt from scratch.
|
|
610
|
+
"input[type='checkbox'], input[type='radio'], input[type='range'] { margin: 0; font: inherit; accent-color: currentColor; }",
|
|
611
|
+
// select keeps its native chrome (border/background/arrow are all part of the
|
|
612
|
+
// same OS-drawn widget that appearance: none would blank out) — only typography
|
|
613
|
+
// and spacing are normalized so it still matches surrounding text.
|
|
614
|
+
"select { margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
615
|
+
"button { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; line-height: inherit; text-align: inherit; }",
|
|
616
|
+
"button, [role='button'] { cursor: pointer; }",
|
|
617
|
+
":disabled { cursor: default; }",
|
|
618
|
+
"textarea { resize: vertical; }",
|
|
619
|
+
// Firefox renders placeholders at ~54% opacity by default; every other browser uses 1 —
|
|
620
|
+
// normalize to 1 so placeholder color is consistent and fully controlled by the placeholder: modifier.
|
|
621
|
+
"::placeholder { opacity: 1; }",
|
|
622
|
+
"input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-outer-spin-button { margin: 0; }",
|
|
623
|
+
"input[type='search']::-webkit-search-decoration, input[type='search']::-webkit-search-cancel-button { -webkit-appearance: none; }",
|
|
624
|
+
"fieldset { padding: 0; margin: 0; }",
|
|
625
|
+
"table { border-collapse: collapse; border-spacing: 0; }"
|
|
626
|
+
].join("\n");
|
|
627
|
+
|
|
587
628
|
// src/core/cache.ts
|
|
588
629
|
var LRUCache = class {
|
|
589
630
|
constructor(capacity = 1e4, onEvict) {
|
|
@@ -2627,23 +2668,6 @@ function findOuterColon(s) {
|
|
|
2627
2668
|
return -1;
|
|
2628
2669
|
}
|
|
2629
2670
|
|
|
2630
|
-
// src/core/reset.ts
|
|
2631
|
-
var BASE_RESET = [
|
|
2632
|
-
// border-style: solid means border-N utilities show a visible border without an extra border-solid class.
|
|
2633
|
-
// border-width: 0 keeps all elements borderless by default.
|
|
2634
|
-
"*, *::before, *::after { box-sizing: border-box; border-width: 0; border-style: solid; border-color: currentColor; }",
|
|
2635
|
-
"body { margin: 0; padding: 0; }",
|
|
2636
|
-
"h1, h2, h3, h4, h5, h6 { margin: 0; font-size: inherit; font-weight: inherit; }",
|
|
2637
|
-
"p { margin: 0; }",
|
|
2638
|
-
"a { color: inherit; text-decoration: none; }",
|
|
2639
|
-
"ul, ol { margin: 0; padding: 0; list-style: none; }",
|
|
2640
|
-
"img, video, svg { display: block; max-width: 100%; }",
|
|
2641
|
-
"input, textarea, select { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
2642
|
-
"button { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; line-height: inherit; text-align: inherit; }",
|
|
2643
|
-
"fieldset { padding: 0; margin: 0; }",
|
|
2644
|
-
"table { border-collapse: collapse; border-spacing: 0; }"
|
|
2645
|
-
].join("\n");
|
|
2646
|
-
|
|
2647
2671
|
// src/core/responsiveStore.ts
|
|
2648
2672
|
var RESPONSIVE_KEY = "__kbach_responsive__";
|
|
2649
2673
|
function getStore() {
|
|
@@ -2752,7 +2776,7 @@ function getGlobalStyleEl() {
|
|
|
2752
2776
|
}
|
|
2753
2777
|
function injectGlobalStyles(theme) {
|
|
2754
2778
|
if (typeof document === "undefined" || isRuntimeCSSDisabled()) return;
|
|
2755
|
-
const rules = [BASE_RESET];
|
|
2779
|
+
const rules = document.getElementById(RESET_STYLE_ID) ? [] : [BASE_RESET];
|
|
2756
2780
|
const ff = theme.fontFamily;
|
|
2757
2781
|
if (ff?.sans && ff.sans !== "System") {
|
|
2758
2782
|
const family = Array.isArray(ff.sans) ? ff.sans.join(", ") : ff.sans;
|
package/dist/jsx-runtime.mjs
CHANGED
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
_invalidateDefaultFontCache,
|
|
5
5
|
jsx,
|
|
6
6
|
jsxs
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-RWQKYQWN.mjs";
|
|
8
8
|
import {
|
|
9
9
|
registerWebElement
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-CJRRPENF.mjs";
|
|
11
11
|
export {
|
|
12
12
|
Fragment,
|
|
13
13
|
_invalidateDefaultFontCache,
|
package/dist/vite-plugin.js
CHANGED
|
@@ -60,6 +60,7 @@ function escapeCSSSelector(cls) {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
// src/core/reset.ts
|
|
63
|
+
var RESET_STYLE_ID = "kbach-reset";
|
|
63
64
|
var BASE_RESET = [
|
|
64
65
|
// border-style: solid means border-N utilities show a visible border without an extra border-solid class.
|
|
65
66
|
// border-width: 0 keeps all elements borderless by default.
|
|
@@ -70,8 +71,31 @@ var BASE_RESET = [
|
|
|
70
71
|
"a { color: inherit; text-decoration: none; }",
|
|
71
72
|
"ul, ol { margin: 0; padding: 0; list-style: none; }",
|
|
72
73
|
"img, video, svg { display: block; max-width: 100%; }",
|
|
73
|
-
|
|
74
|
+
// appearance: none is deliberately NOT applied to checkbox/radio/select below —
|
|
75
|
+
// stripping it hides their native checkmark/arrow with nothing rendered in its
|
|
76
|
+
// place, leaving an invisible checkbox or an arrow-less <select> that looks like
|
|
77
|
+
// plain text. Text-like inputs, textarea, and button don't have that problem
|
|
78
|
+
// (their native chrome is just a skin around content utilities can fully
|
|
79
|
+
// restyle), so they keep the blank-canvas treatment.
|
|
80
|
+
"input: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; }",
|
|
81
|
+
// Native checkbox/radio/range still get typography + spacing normalized, and
|
|
82
|
+
// accent-color re-themes their native indicator to the current text color
|
|
83
|
+
// instead of the browser/OS default blue, so they stay on-brand without
|
|
84
|
+
// needing to be rebuilt from scratch.
|
|
85
|
+
"input[type='checkbox'], input[type='radio'], input[type='range'] { margin: 0; font: inherit; accent-color: currentColor; }",
|
|
86
|
+
// select keeps its native chrome (border/background/arrow are all part of the
|
|
87
|
+
// same OS-drawn widget that appearance: none would blank out) — only typography
|
|
88
|
+
// and spacing are normalized so it still matches surrounding text.
|
|
89
|
+
"select { margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
74
90
|
"button { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; line-height: inherit; text-align: inherit; }",
|
|
91
|
+
"button, [role='button'] { cursor: pointer; }",
|
|
92
|
+
":disabled { cursor: default; }",
|
|
93
|
+
"textarea { resize: vertical; }",
|
|
94
|
+
// Firefox renders placeholders at ~54% opacity by default; every other browser uses 1 —
|
|
95
|
+
// normalize to 1 so placeholder color is consistent and fully controlled by the placeholder: modifier.
|
|
96
|
+
"::placeholder { opacity: 1; }",
|
|
97
|
+
"input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-outer-spin-button { margin: 0; }",
|
|
98
|
+
"input[type='search']::-webkit-search-decoration, input[type='search']::-webkit-search-cancel-button { -webkit-appearance: none; }",
|
|
75
99
|
"fieldset { padding: 0; margin: 0; }",
|
|
76
100
|
"table { border-collapse: collapse; border-spacing: 0; }"
|
|
77
101
|
].join("\n");
|
|
@@ -2654,7 +2678,7 @@ function getGlobalStyleEl() {
|
|
|
2654
2678
|
}
|
|
2655
2679
|
function injectGlobalStyles(theme) {
|
|
2656
2680
|
if (typeof document === "undefined" || isRuntimeCSSDisabled()) return;
|
|
2657
|
-
const rules = [BASE_RESET];
|
|
2681
|
+
const rules = document.getElementById(RESET_STYLE_ID) ? [] : [BASE_RESET];
|
|
2658
2682
|
const ff = theme.fontFamily;
|
|
2659
2683
|
if (ff?.sans && ff.sans !== "System") {
|
|
2660
2684
|
const family = Array.isArray(ff.sans) ? ff.sans.join(", ") : ff.sans;
|
|
@@ -3202,6 +3226,23 @@ function toNumericScreens(screens) {
|
|
|
3202
3226
|
}
|
|
3203
3227
|
return out;
|
|
3204
3228
|
}
|
|
3229
|
+
function pushTemplateLiteralBody(body, into) {
|
|
3230
|
+
const interpolationRe = /\$\{(?:[^{}]|\{[^{}]*\})*\}/g;
|
|
3231
|
+
let im;
|
|
3232
|
+
while ((im = interpolationRe.exec(body)) !== null) {
|
|
3233
|
+
pushClassLikeStrings(im[0], into);
|
|
3234
|
+
}
|
|
3235
|
+
pushTokens(body.replace(/\$\{(?:[^{}]|\{[^{}]*\})*\}/g, " "), into);
|
|
3236
|
+
}
|
|
3237
|
+
function pushClassLikeStrings(text, into) {
|
|
3238
|
+
const quotedStringRe = /(["'`])((?:(?!\1).)*)\1/g;
|
|
3239
|
+
let qm;
|
|
3240
|
+
while ((qm = quotedStringRe.exec(text)) !== null) {
|
|
3241
|
+
const [, quote, content] = qm;
|
|
3242
|
+
if (quote === "`") pushTemplateLiteralBody(content, into);
|
|
3243
|
+
else pushTokens(content, into);
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3205
3246
|
function extractClassStrings(code) {
|
|
3206
3247
|
const found = /* @__PURE__ */ new Set();
|
|
3207
3248
|
const simpleRe = /(?:className|kb)=["']([^"']+)["']/g;
|
|
@@ -3222,8 +3263,7 @@ function extractClassStrings(code) {
|
|
|
3222
3263
|
i++;
|
|
3223
3264
|
}
|
|
3224
3265
|
jsxExprRe.lastIndex = i + 1;
|
|
3225
|
-
|
|
3226
|
-
for (const s of innerStrings) pushTokens(s.slice(1, -1), found);
|
|
3266
|
+
pushClassLikeStrings(block, found);
|
|
3227
3267
|
}
|
|
3228
3268
|
const clsxCallRe = /(?:clsx|cn|classnames|cx)\(/g;
|
|
3229
3269
|
while ((m = clsxCallRe.exec(code)) !== null) {
|
|
@@ -3240,19 +3280,16 @@ function extractClassStrings(code) {
|
|
|
3240
3280
|
i++;
|
|
3241
3281
|
}
|
|
3242
3282
|
clsxCallRe.lastIndex = i + 1;
|
|
3243
|
-
|
|
3244
|
-
for (const s of innerStrings) pushTokens(s.slice(1, -1), found);
|
|
3283
|
+
pushClassLikeStrings(block, found);
|
|
3245
3284
|
}
|
|
3246
3285
|
const templateRe = /`([^`]{1,2000})`/g;
|
|
3247
|
-
while ((m = templateRe.exec(code)) !== null)
|
|
3248
|
-
const staticParts = m[1].replace(/\$\{(?:[^{}]|\{[^{}]*\})*\}/g, " ");
|
|
3249
|
-
pushTokens(staticParts, found);
|
|
3250
|
-
}
|
|
3286
|
+
while ((m = templateRe.exec(code)) !== null) pushTemplateLiteralBody(m[1], found);
|
|
3251
3287
|
return [...found];
|
|
3252
3288
|
}
|
|
3289
|
+
var NOT_A_CLASS_NAME_RE = /[${}]|-$|^[?:&|!"'=<>]+$/;
|
|
3253
3290
|
function pushTokens(str, into) {
|
|
3254
3291
|
for (const tok of splitClassTokens(str)) {
|
|
3255
|
-
if (tok) into.add(tok);
|
|
3292
|
+
if (tok && !NOT_A_CLASS_NAME_RE.test(tok)) into.add(tok);
|
|
3256
3293
|
}
|
|
3257
3294
|
}
|
|
3258
3295
|
function scanDir(dir, onFile) {
|
package/dist/vite-plugin.mjs
CHANGED
|
@@ -26,6 +26,7 @@ function escapeCSSSelector(cls) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
// src/core/reset.ts
|
|
29
|
+
var RESET_STYLE_ID = "kbach-reset";
|
|
29
30
|
var BASE_RESET = [
|
|
30
31
|
// border-style: solid means border-N utilities show a visible border without an extra border-solid class.
|
|
31
32
|
// border-width: 0 keeps all elements borderless by default.
|
|
@@ -36,8 +37,31 @@ var BASE_RESET = [
|
|
|
36
37
|
"a { color: inherit; text-decoration: none; }",
|
|
37
38
|
"ul, ol { margin: 0; padding: 0; list-style: none; }",
|
|
38
39
|
"img, video, svg { display: block; max-width: 100%; }",
|
|
39
|
-
|
|
40
|
+
// appearance: none is deliberately NOT applied to checkbox/radio/select below —
|
|
41
|
+
// stripping it hides their native checkmark/arrow with nothing rendered in its
|
|
42
|
+
// place, leaving an invisible checkbox or an arrow-less <select> that looks like
|
|
43
|
+
// plain text. Text-like inputs, textarea, and button don't have that problem
|
|
44
|
+
// (their native chrome is just a skin around content utilities can fully
|
|
45
|
+
// restyle), so they keep the blank-canvas treatment.
|
|
46
|
+
"input: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; }",
|
|
47
|
+
// Native checkbox/radio/range still get typography + spacing normalized, and
|
|
48
|
+
// accent-color re-themes their native indicator to the current text color
|
|
49
|
+
// instead of the browser/OS default blue, so they stay on-brand without
|
|
50
|
+
// needing to be rebuilt from scratch.
|
|
51
|
+
"input[type='checkbox'], input[type='radio'], input[type='range'] { margin: 0; font: inherit; accent-color: currentColor; }",
|
|
52
|
+
// select keeps its native chrome (border/background/arrow are all part of the
|
|
53
|
+
// same OS-drawn widget that appearance: none would blank out) — only typography
|
|
54
|
+
// and spacing are normalized so it still matches surrounding text.
|
|
55
|
+
"select { margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
40
56
|
"button { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; line-height: inherit; text-align: inherit; }",
|
|
57
|
+
"button, [role='button'] { cursor: pointer; }",
|
|
58
|
+
":disabled { cursor: default; }",
|
|
59
|
+
"textarea { resize: vertical; }",
|
|
60
|
+
// Firefox renders placeholders at ~54% opacity by default; every other browser uses 1 —
|
|
61
|
+
// normalize to 1 so placeholder color is consistent and fully controlled by the placeholder: modifier.
|
|
62
|
+
"::placeholder { opacity: 1; }",
|
|
63
|
+
"input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-outer-spin-button { margin: 0; }",
|
|
64
|
+
"input[type='search']::-webkit-search-decoration, input[type='search']::-webkit-search-cancel-button { -webkit-appearance: none; }",
|
|
41
65
|
"fieldset { padding: 0; margin: 0; }",
|
|
42
66
|
"table { border-collapse: collapse; border-spacing: 0; }"
|
|
43
67
|
].join("\n");
|
|
@@ -2620,7 +2644,7 @@ function getGlobalStyleEl() {
|
|
|
2620
2644
|
}
|
|
2621
2645
|
function injectGlobalStyles(theme) {
|
|
2622
2646
|
if (typeof document === "undefined" || isRuntimeCSSDisabled()) return;
|
|
2623
|
-
const rules = [BASE_RESET];
|
|
2647
|
+
const rules = document.getElementById(RESET_STYLE_ID) ? [] : [BASE_RESET];
|
|
2624
2648
|
const ff = theme.fontFamily;
|
|
2625
2649
|
if (ff?.sans && ff.sans !== "System") {
|
|
2626
2650
|
const family = Array.isArray(ff.sans) ? ff.sans.join(", ") : ff.sans;
|
|
@@ -3168,6 +3192,23 @@ function toNumericScreens(screens) {
|
|
|
3168
3192
|
}
|
|
3169
3193
|
return out;
|
|
3170
3194
|
}
|
|
3195
|
+
function pushTemplateLiteralBody(body, into) {
|
|
3196
|
+
const interpolationRe = /\$\{(?:[^{}]|\{[^{}]*\})*\}/g;
|
|
3197
|
+
let im;
|
|
3198
|
+
while ((im = interpolationRe.exec(body)) !== null) {
|
|
3199
|
+
pushClassLikeStrings(im[0], into);
|
|
3200
|
+
}
|
|
3201
|
+
pushTokens(body.replace(/\$\{(?:[^{}]|\{[^{}]*\})*\}/g, " "), into);
|
|
3202
|
+
}
|
|
3203
|
+
function pushClassLikeStrings(text, into) {
|
|
3204
|
+
const quotedStringRe = /(["'`])((?:(?!\1).)*)\1/g;
|
|
3205
|
+
let qm;
|
|
3206
|
+
while ((qm = quotedStringRe.exec(text)) !== null) {
|
|
3207
|
+
const [, quote, content] = qm;
|
|
3208
|
+
if (quote === "`") pushTemplateLiteralBody(content, into);
|
|
3209
|
+
else pushTokens(content, into);
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3171
3212
|
function extractClassStrings(code) {
|
|
3172
3213
|
const found = /* @__PURE__ */ new Set();
|
|
3173
3214
|
const simpleRe = /(?:className|kb)=["']([^"']+)["']/g;
|
|
@@ -3188,8 +3229,7 @@ function extractClassStrings(code) {
|
|
|
3188
3229
|
i++;
|
|
3189
3230
|
}
|
|
3190
3231
|
jsxExprRe.lastIndex = i + 1;
|
|
3191
|
-
|
|
3192
|
-
for (const s of innerStrings) pushTokens(s.slice(1, -1), found);
|
|
3232
|
+
pushClassLikeStrings(block, found);
|
|
3193
3233
|
}
|
|
3194
3234
|
const clsxCallRe = /(?:clsx|cn|classnames|cx)\(/g;
|
|
3195
3235
|
while ((m = clsxCallRe.exec(code)) !== null) {
|
|
@@ -3206,19 +3246,16 @@ function extractClassStrings(code) {
|
|
|
3206
3246
|
i++;
|
|
3207
3247
|
}
|
|
3208
3248
|
clsxCallRe.lastIndex = i + 1;
|
|
3209
|
-
|
|
3210
|
-
for (const s of innerStrings) pushTokens(s.slice(1, -1), found);
|
|
3249
|
+
pushClassLikeStrings(block, found);
|
|
3211
3250
|
}
|
|
3212
3251
|
const templateRe = /`([^`]{1,2000})`/g;
|
|
3213
|
-
while ((m = templateRe.exec(code)) !== null)
|
|
3214
|
-
const staticParts = m[1].replace(/\$\{(?:[^{}]|\{[^{}]*\})*\}/g, " ");
|
|
3215
|
-
pushTokens(staticParts, found);
|
|
3216
|
-
}
|
|
3252
|
+
while ((m = templateRe.exec(code)) !== null) pushTemplateLiteralBody(m[1], found);
|
|
3217
3253
|
return [...found];
|
|
3218
3254
|
}
|
|
3255
|
+
var NOT_A_CLASS_NAME_RE = /[${}]|-$|^[?:&|!"'=<>]+$/;
|
|
3219
3256
|
function pushTokens(str, into) {
|
|
3220
3257
|
for (const tok of splitClassTokens(str)) {
|
|
3221
|
-
if (tok) into.add(tok);
|
|
3258
|
+
if (tok && !NOT_A_CLASS_NAME_RE.test(tok)) into.add(tok);
|
|
3222
3259
|
}
|
|
3223
3260
|
}
|
|
3224
3261
|
function scanDir(dir, onFile) {
|