npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kbach/react

v0.5.42

Published

React / React Native components and hooks for the Kbach framework

Readme

@kbach/react

Tailwind-like utility classes for React (web). Classes are resolved to inline styles at render time through a custom JSX runtime — no separate stylesheet step required. Responsive, dark mode, hover, and pseudo-element rules are injected as real CSS so they work with the full CSS cascade.

/** @jsxImportSource @kbach/react */

<div className="bg-white dark:bg-gray-10 p-4 rounded-xl shadow" />
<div className="bg-blue-7 hover:bg-blue-8 dark:bg-indigo-6 rounded-lg px-6 py-3" />
<div className="bg-[#6366f1] p-[14px] rounded-[20px]" />
<div className="group">
  <span className="opacity-0 group-hover:opacity-100 transition" />
</div>

Install

npm install @kbach/react

Setup

1. Configure the JSX runtime

tsconfig.json

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "@kbach/react"
  }
}

Vite

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { kbach } from '@kbach/react/vite';

export default defineConfig({
  plugins: [
    react({ jsxImportSource: '@kbach/react' }),
    kbach(), // generates CSS and writes it to your main CSS file
  ],
});

The kbach() Vite plugin scans your source files for class strings (including ternary expressions, template literals, and clsx/cn call patterns) and writes the generated CSS between /* kbach:start */ / /* kbach:end */ markers in your main CSS file (src/index.css, app/app.css, etc.). On HMR only the changed file is rescanned — unchanged class tokens reuse their cached CSS.

Babel

// babel.config.js
module.exports = {
  presets: [
    ['@babel/preset-react', { runtime: 'automatic', importSource: '@kbach/react' }],
  ],
};

Per-file pragma (no config change required)

/** @jsxImportSource @kbach/react */

2. Wrap your app

import { ThemeProvider } from '@kbach/react';

export default function Root() {
  return (
    <ThemeProvider defaultMode="system">
      <App />
    </ThemeProvider>
  );
}

API

className / kb prop

Works on any HTML element or React component once the JSX runtime is configured. Both className and kb are equivalent — kb is shorter and avoids conflicts with library components that forward className to their own DOM elements.

<div className="bg-white dark:bg-gray-10 p-4 rounded-xl" />
<p kb="text-gray-10 dark:text-white text-lg font-bold" />
<button className="bg-blue-7 hover:bg-blue-8 pressed:bg-blue-9 rounded-lg px-4 py-2" />
<input className="caret-blue-6 focus:ring-2" />

styled(Component, classes)

Pre-style any component. Returns a new component that merges the base classes with any kb prop passed at use time. On web, the class string is forwarded as className so CSS rules (group-hover:, before:, print:, etc.) match the element.

import { styled } from '@kbach/react';

const Card = styled('div', 'bg-white dark:bg-gray-9 rounded-2xl p-6 shadow');
const Title = styled('h2', 'text-2xl font-bold text-gray-10 dark:text-white');
const Button = styled(
  'button',
  'bg-blue-7 hover:bg-blue-8 dark:bg-indigo-6 rounded-xl px-6 py-3'
);

// Pass extra classes at use time
<Card kb="mt-4 mb-2">
  <Title kb="text-3xl">Hello</Title>
</Card>

useStyles(classes, state?)

Resolve classes to a style object inside a component. Accepts a string, an array of strings (merged left-to-right), and an optional interaction state object.

import { useStyles } from '@kbach/react';

// Basic
const style = useStyles('bg-blue-6 dark:bg-indigo-6 px-3 py-1 rounded-full');

// Multiple class strings merged
const style = useStyles(['bg-white p-4', 'dark:bg-gray-9 rounded-xl']);

// With interaction state
const [pressed, setPressed] = useState(false);
const style = useStyles('bg-blue-5 pressed:bg-blue-7 rounded-lg', { pressed });

useResolvedStyle(classes)

Returns the raw bucket map ({ base, dark, hover, … }) without flattening. Useful when you need to pass different buckets to different elements, or animate between states with Animated.

const resolved = useResolvedStyle('bg-white dark:bg-gray-9 p-4');
// { base: { backgroundColor: '#fff', padding: 16 }, dark: { backgroundColor: '#1f2937' } }

kb(classes)

Resolve classes outside a component (static contexts, StyleSheet.create(), etc.).

import { kb } from '@kbach/react';

const cardStyle = kb('bg-white p-4 rounded-xl') as React.CSSProperties;

cx(...classes)

Conditionally join class strings. Falsy values are ignored.

import { cx } from '@kbach/react';

<div className={cx(
  'p-4 rounded-xl',
  isSelected && 'border-2 border-blue-6',
  isDisabled && 'opacity-50',
)} />

useTheme()

import { useTheme } from '@kbach/react';

const { mode, resolvedMode, isDark, setMode, toggle, config } = useTheme();

| Value | Type | Description | |---|---|---| | mode | 'light' \| 'dark' \| 'system' | User-selected mode | | resolvedMode | 'light' \| 'dark' | Resolved after system lookup | | isDark | boolean | Shorthand for resolvedMode === 'dark' | | setMode | fn | Set mode explicitly | | toggle | fn | Toggle between light and dark | | config | ResolvedConfig | Full resolved config (theme, darkMode, plugins) |

useIsDark()

import { useIsDark } from '@kbach/react';
const isDark = useIsDark();

useBreakpoint() / useResponsive()

import { useBreakpoint, useResponsive } from '@kbach/react';

const bp = useBreakpoint(); // 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'

const { sm, md, lg } = useResponsive();
const padding = lg ? 32 : sm ? 16 : 8;

useColors()

Returns the active theme's color palette as a smart proxy. Supports shade access, /opacity modifier, and arbitrary CSS color manipulation. Handles 3, 4, 6, and 8-digit hex values.

import { useColors } from '@kbach/react';

const colors = useColors();

colors.blue[6]           // '#3b82f6'
colors.blue['6/50']      // 'rgba(59,130,246,0.5)'
colors.white             // '#ffffff'
colors['white/20']       // 'rgba(255,255,255,0.2)'
colors.alpha('#ff6b35', 60) // 'rgba(255,107,53,0.6)'

ThemeToggle

Drop-in toggle component.

<ThemeToggle />                                // button (default)
<ThemeToggle variant="switch" />              // toggle switch
<ThemeToggle variant="icon-button" />         // icon button
<ThemeToggle variant="button" includeSystem /> // three-way selector

Modifiers

Modifiers are chainable with no hard cap — as many as needed in any order:

<div className="dark:sm:hover:focus:p-4" />
<div className="dark:group-hover:not-disabled:text-blue-6" />

Theme & mode

| Modifier | Trigger | |---|---| | dark: | Dark mode active | | not-dark: / light: | Light mode active | | not-light: | Dark mode active (alias) |

Interactive — JS state + CSS pseudo

| Modifier | Trigger | |---|---| | hover: / not-hover: | Mouse hover | | focus: / not-focus: | Focused | | pressed: / not-pressed: | Click / touch down | | active: / not-active: | Active state | | disabled: / not-disabled: | Disabled | | checked: / not-checked: | Checkbox / radio checked | | visited: / not-visited: | Visited link | | placeholder: | Placeholder text |

CSS-only pseudo-classes (structural)

| Modifier | CSS | |---|---| | first: | :first-child | | last: | :last-child | | odd: | :nth-child(odd) | | even: | :nth-child(even) | | only: | :only-child | | focus-within: | :focus-within | | focus-visible: | :focus-visible |

Pseudo-elements

<div className="before:content-['*'] before:text-red-6 relative" />
<p className="first-letter:text-4xl first-letter:font-bold" />
<input className="placeholder:text-gray-5" />
<p className="selection:bg-blue-3" />

| Modifier | CSS | |---|---| | before: | ::before | | after: | ::after | | selection: | ::selection | | first-letter: | ::first-letter | | first-line: | ::first-line | | marker: | ::marker | | placeholder: | ::placeholder |

Group / peer ancestor selectors

<div className="group">
  <span className="opacity-0 group-hover:opacity-100 transition" />
</div>
<input className="peer" />
<p className="peer-focus:text-blue-6" />

| Modifier | Fires when | |---|---| | group-hover: | Ancestor .group is hovered | | group-focus: | Ancestor .group is focused | | peer-hover: | Previous sibling .peer is hovered | | peer-focus: | Previous sibling .peer is focused |

Responsive

| Modifier | Min-width | |---|---| | sm: | 576 px | | md: | 768 px | | lg: | 1024 px | | xl: | 1280 px | | 2xl: | 1536 px |

Print

<div className="print:hidden" />
<div className="print:text-black print:bg-white" />

Orientation & accessibility media

| Modifier | Media query | |---|---| | landscape: | (orientation: landscape) | | portrait: | (orientation: portrait) | | motion-reduce: | (prefers-reduced-motion: reduce) | | motion-safe: | (prefers-reduced-motion: no-preference) | | contrast-more: | (prefers-contrast: more) | | contrast-less: | (prefers-contrast: less) |

Directionality

<div className="rtl:text-right ltr:text-left" />

Important

Prefix any class with ! to add !important to every declaration it produces:

<div className="!p-0 !m-0" />

Arbitrary values

<div className="bg-[#6366f1]" />
<div className="p-[14px]" />
<div className="text-[18px]" />
<div className="w-[calc(100%-2rem)]" />
<div className="bg-[rgba(99,102,241,0.15)]" />

Color with opacity

<div className="bg-blue-6/50" />   // 50% opacity
<div className="bg-gray-10/75" />  // 75% opacity

Color scale

Colors use a 12-shade scale — 1 is lightest, 12 is darkest.

shade 1  ·  2  ·  3  ·  4  ·  5  ·  6  ·  7  ·  8  ·  9  ·  10  ·  11  ·  12
light ──────────────────────────────────────────────────────────────── dark

Available families: slate, gray, zinc, neutral, stone, red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, rose.

Common utilities

| Category | Examples | |---|---| | Background | bg-white, bg-gray-10, bg-blue-6, bg-[#fff] | | Text | text-sm, text-2xl, text-gray-10, font-bold, font-sans | | Text wrap | text-balance, text-pretty, text-nowrap, text-wrap | | Line clamp | line-clamp-3, line-clamp-none | | Padding | p-4, px-6, py-3 | | Margin | m-4, mx-auto, mt-2, -mt-4 | | Size | size-12, w-full, h-screen, w-screen, w-[200px] | | Flex | flex-1, flex-row, items-center, justify-between, gap-4 | | Grid | grid-3, col-span-2, grid-flow-col | | Border | border, border-2, border-gray-4, rounded-xl | | Shadow | shadow, shadow-md, shadow-lg | | Opacity | opacity-50, opacity-75 | | Overflow | overflow-hidden, overflow-clip, overflow-x-auto | | Cursor | cursor-pointer, cursor-grab, cursor-zoom-in | | Scroll | scroll-smooth, scroll-auto | | Float / clear | float-left, float-right, clear-both | | Vertical align | align-middle, align-top, align-baseline | | Touch action | touch-none, touch-pan-x, touch-manipulation | | Caret | caret-blue-6, caret-transparent | | Accent | accent-blue-6, accent-auto | | Animation | animate-spin, animate-pulse, animate-bounce, animate-ping | | Transition | transition, duration-300, delay-150 |

Configuration

// kbach.config.js
module.exports = {
  darkMode: 'attribute', // 'attribute' | 'class' | 'media'

  theme: {
    // Replace a theme section entirely
    colors: {
      brand: { 1: '#eff6ff', 6: '#3b82f6', 10: '#1e3a5f' },
    },
  },

  extend: {
    // Add to defaults without replacing them
    colors: { brand: { 6: '#6366f1' } },
    spacing: { 18: '72px' },
    screens: { '3xl': '1920px' },
  },

  plugins: [
    ({ addUtility, addVariant, theme }) => {
      // Custom utility
      addUtility('border-brand', {
        borderColor: theme('colors.brand.6'),
        borderWidth: 2,
      });

      // Custom variant — selector string (auto-converted to CSS rules)
      addVariant('hocus', ':hover, :focus');
      addVariant('supports-grid', '@media (display: grid)');
      addVariant('dark-theme', '.dark-theme');

      // Custom variant — full ModifierDef for JS-tracked interactive states
      addVariant('long-press', {
        pseudo: ':active',
        jsBehavior: 'interactive',
        jsMatch: (_, state) => !!state.longPressed,
      });
    },
  ],
};

Color aliases

Color values can reference other palette entries — chains up to 5 levels deep are resolved:

extend: {
  colors: {
    primary: 'blue-6',          // → resolved hex of blue shade 6
    brand: { 6: 'primary' },    // → follows through to blue-6's hex
  },
},

Font Family

// kbach.config.js
module.exports = {
  extend: {
    fontFamily: {
      sans:        'Inter_400Regular',
      'sans-md':   'Inter_500Medium',
      'sans-bold': 'Inter_700Bold',
    },
  },
};

Use as utilities:

<p className="font-sans">Regular</p>
<p className="font-sans-md">Medium</p>
<p className="font-sans-bold">Bold</p>

Global default font (web): When fontFamily.sans is set to anything other than 'System', Kbach automatically injects body { font-family: <your-font> } — all text inherits it without font-sans on every element.

Apply at runtime (e.g. from a server-side user preference):

import { updateConfig } from '@kbach/react';

updateConfig({ extend: { colors: { brand: { 6: '#6366f1' } } } });

Custom responsive breakpoints

Add breakpoints in extend.screens — they automatically register as new modifier names:

// kbach.config.js
module.exports = {
  extend: {
    screens: { '3xl': '1920px' },
  },
};
<div className="3xl:max-w-screen-3xl 3xl:mx-auto" />

Full reference

See kbach-react.md for the complete utility reference, all modifiers, configuration options, and common patterns.

For React Native / Expo, see the @kbach/native package.