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/native

v0.6.0

Published

Single-package Kbach install for React Native and Expo — includes core, components, Babel plugin, and Metro config

Readme

@kbach/native

Tailwind-like utility classes for React Native and Expo. One package includes everything: the core engine, JSX runtime, and Babel preset. Classes are written as className strings and resolved to inline styles at render time.

import { View, Text, TouchableOpacity } from 'react-native';
import { styled } from '@kbach/native';

const Button = styled(
  TouchableOpacity,
  'bg-blue-7 pressed:bg-blue-8 dark:bg-indigo-6 rounded-xl px-6 py-3 items-center'
);

export default function Screen() {
  return (
    <View className="flex-1 bg-gray-2 dark:bg-gray-11 p-4">
      <Text className="text-2xl font-bold text-gray-10 dark:text-white mb-4">
        Hello Kbach
      </Text>
      <Button>
        <Text className="text-white font-semibold">Press me</Text>
      </Button>
    </View>
  );
}

Install

npm package

npm install @kbach/native

Setup

1. babel.config.js

One-liner using the helper:

const { createKbachConfig } = require('@kbach/native');
module.exports = createKbachConfig();

Or manually:

module.exports = function (api) {
  api.cache(true);
  return {
    presets: [
      ['babel-preset-expo', { jsxImportSource: '@kbach/native' }],
      '@kbach/native/babel',
    ],
  };
};

Merging into an existing Babel config:

const { withKbachBabel } = require('@kbach/native');

module.exports = withKbachBabel({
  presets: ['babel-preset-expo'],
  plugins: [/* your existing plugins */],
});

After changing babel.config.js, clear the Metro cache:

npx expo start --clear

2. Wrap your app

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

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

ThemeProvider reads useColorScheme() and useWindowDimensions() automatically — no extra props needed.

API

className / kb prop

Works on any React Native component — View, Text, TouchableOpacity, TextInput, and third-party components.

<View className="flex-1 bg-gray-2 dark:bg-gray-11 p-4" />
<Text className="text-lg font-bold text-gray-10 dark:text-white" />
<TouchableOpacity className="bg-blue-7 pressed:bg-blue-8 rounded-xl px-6 py-3" />

styled(Component, classes)

Pre-style any component. Handles interaction states (pressed:, focus:, etc.) automatically. Pass extra classes at use time with the kb prop.

import { styled } from '@kbach/native';
import { View, Text, TouchableOpacity, TextInput } from 'react-native';

const Card = styled(View, 'bg-white dark:bg-gray-9 rounded-2xl p-6 shadow');
const Title = styled(Text, 'text-2xl font-bold text-gray-10 dark:text-white');
const Button = styled(
  TouchableOpacity,
  'bg-blue-7 pressed:bg-blue-8 dark:bg-indigo-6 dark:pressed:bg-indigo-7 rounded-xl px-6 py-3'
);
const Input = styled(
  TextInput,
  'border border-gray-4 dark:border-gray-7 focus:border-blue-6 bg-white dark:bg-gray-8 rounded-lg px-4 py-3'
);

<Card kb="mt-4 mb-2">
  <Title kb="text-3xl">Hello</Title>
</Card>

cx(...classes)

Conditionally join class strings. Falsy values are ignored.

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

<View className={cx(
  'p-4 rounded-xl border',
  isSelected ? 'border-blue-6 bg-blue-1' : 'border-gray-3 bg-white',
  isDisabled && 'opacity-50',
)} />

useStyles(classes, state?)

Resolve classes to a style object inside a component.

import { useStyles } from '@kbach/native';
import { View, Text } from 'react-native';

function Badge() {
  const containerStyle = useStyles('bg-blue-6 dark:bg-indigo-6 px-3 py-1 rounded-full');
  const textStyle = useStyles('text-white text-xs font-bold');
  return (
    <View style={containerStyle}>
      <Text style={textStyle}>New</Text>
    </View>
  );
}

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

kb(classes)

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

import { StyleSheet } from 'react-native';
import { kb } from '@kbach/native';

const styles = StyleSheet.create({
  container: kb('flex-1 bg-white p-4') as object,
  title:     kb('text-2xl font-bold text-gray-10') as object,
});

useTheme()

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

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 |

useIsDark()

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

useColors()

Returns the active theme's color palette as a smart proxy.

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

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)'

Modifiers

Chain modifiers in any order before the utility name:

<View className="dark:sm:pressed:bg-blue-8" />

Theme

| Modifier | Trigger | |---|---| | dark: | Dark mode | | light: / not-dark: | Light mode |

Interactive

| Modifier | Trigger | |---|---| | pressed: | Touch / click pressed | | focus: | Element focused | | hover: | Mouse hover (React Native Web) | | active: | Active state | | disabled: | Disabled | | checked: | Checked |

Negated: not-pressed:, not-focus:, not-hover:, not-active:, not-disabled:, not-checked:

Responsive

Resolved from current window width via useWindowDimensions().

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

<View className="p-4 md:p-8 lg:p-12" />

Important

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

Arbitrary values

<View className="bg-[#6366f1]" />
<View className="p-[14px]" />
<Text className="text-[18px]" />
<View className="rounded-[20px]" />
<View className="w-[200px]" />

Negative values

<View className="-mt-4" />      // marginTop: -16
<View className="-mx-2" />      // marginHorizontal: -8
<View className="-mt-[10px]" /> // marginTop: -10

Color system

12-shade scale — 1 lightest, 12 darkest. Use as bg-blue-6, text-gray-10, border-red-4/50.

Families: slate gray zinc neutral stone red orange amber yellow lime green emerald teal cyan sky blue indigo violet purple fuchsia pink rose
Special: transparent black white

Opacity modifier: bg-blue-6/50 (50% alpha)

Native-only utilities

| Utility | Effect | |---|---| | tint-{color} | tintColor on Image / icon components | | perspective-{n} | 3-D perspective transform | | backface-hidden | backfaceVisibility: hidden | | text-shadow | Text shadow (small) | | text-shadow-lg | Text shadow (large) |

<Image source={icon} className="tint-blue-6 w-6 h-6" />
<View className="perspective-500 rotate-y-45" />
<Text className="text-shadow font-bold">Shadowed</Text>

Expo Web / React Native Web

When the bundle runs in a browser (Expo Web, Metro web target), @kbach/native automatically switches to the same CSS-class strategy as @kbach/react:

  • React Native components are substituted with semantic HTML elements:

| Component | HTML tag | |---|---| | View, ScrollView, SafeAreaView | div | | Text | span | | TextInput | input / textarea (multiline) | | Image | img | | Pressable, TouchableOpacity | div (role="button") | | FlatList, SectionList | div |

  • CSS classes are applied instead of inline styles — the same kbach.css approach as @kbach/react.
  • React Native–only props (onChangeText, source, secureTextEntry, keyboardType, etc.) are mapped to their HTML equivalents.
  • To register additional components: registerWebElement(Animated.View, 'div').

To use the Vite plugin on an Expo Web project, add it to vite.config.ts:

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

export default defineConfig({
  plugins: [kbach()],
});

Create and import kbach.css in your entry file exactly as in the @kbach/react setup.

Web-only utilities (gracefully ignored on native)

These resolve to null on React Native and produce no warning:

caret-*, accent-*, touch-*, float-*, clear-*, line-clamp-*, scroll-*, animate-*, transition, filter, backdrop-filter, print:, before:, after:, selection:, first-letter:, first-line:, marker:, landscape:, portrait:, motion-reduce:, motion-safe:, contrast-more:, contrast-less:, rtl:, ltr:, grid, grid-cols-*, ring, outline-*, cursor-*, bg-gradient-*

Configuration

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

  theme: {
    colors: { brand: { 1: '#eff6ff', 6: '#3b82f6', 10: '#1e3a5f' } },
  },

  extend: {
    colors: { brand: { 6: '#6366f1' } },
    spacing: { 18: 72 },
    screens: { '3xl': 1920 },
    fontFamily: {
      sans:        'CormorantGaramond_400Regular',
      'sans-md':   'CormorantGaramond_500Medium',
      'sans-bold': 'CormorantGaramond_700Bold',
    },
  },

  plugins: [
    ({ addUtility, addVariant, theme }) => {
      addUtility('border-brand', {
        borderColor: theme('colors.brand.6'),
        borderWidth: 2,
      });
      addVariant('long-press', {
        pseudo: ':active',
        jsBehavior: 'interactive',
        jsMatch: (_, state) => !!state.longPressed,
      });
    },
  ],
};

CSS inheritance does not exist in React Native. Apply font utilities to each Text, or define styled components once:

const Body    = styled(Text, 'font-sans text-gray-10 dark:text-white');
const Heading = styled(Text, 'font-sans-bold text-2xl text-gray-10 dark:text-white');

Full reference

See kbach-native.md for the complete utility reference, native-only utilities, and configuration options.