tailwind-to-style
v4.0.1
Published
Zero-build runtime Tailwind CSS engine. Convert utility classes to real CSS with variants, slots, tokens, and React bindings. SSR-ready, tree-shakeable.
Downloads
985
Maintainers
Readme
tailwind-to-style
Zero-build runtime Tailwind CSS engine. Convert utility classes to real CSS — with variants, slots, design tokens, and React bindings. No build step, no PostCSS, no config file. Just works.
Why tailwind-to-style?
| Feature | tailwind-to-style | Tailwind CSS | Stitches | CVA | |---------|:---:|:---:|:---:|:---:| | Zero build step | ✅ | ❌ | ✅ | ❌ | | Tailwind syntax | ✅ | ✅ | ❌ | ✅ | | Runtime variants | ✅ | ❌ | ✅ | ✅ | | Slots (multi-part) | ✅ | ❌ | ❌ | ❌ | | Design tokens | ✅ | ❌ | ✅ | ❌ | | Inline style output | ✅ | ❌ | ❌ | ❌ | | SSR support | ✅ | ✅ | ✅ | ✅ | | React bindings | ✅ | ❌ | ✅ | ❌ | | Framework agnostic | ✅ | ✅ | ❌ | ✅ | | Tree-shakeable | ✅ | N/A | ✅ | ✅ |
Installation
npm install tailwind-to-stylepnpm add tailwind-to-styleyarn add tailwind-to-styleOr use a CDN:
<script src="https://unpkg.com/tailwind-to-style"></script>Quick Start
import { tw, tws, cx } from 'tailwind-to-style';
// Generate atomic CSS classes (auto-injected into DOM)
document.body.className = tw('flex items-center justify-center min-h-screen bg-gray-100');
// Convert to inline styles
element.style.cssText = tws('bg-blue-500 text-white p-4 rounded-lg');
// Conditional class merging
const classes = cx('base', isActive && 'ring-2', { 'opacity-50': disabled });Examples
Want to try the library with real demos?
examples/basic/— runtimetws()examples for inline conversion and custom values.examples/react-demo/— full React showcase with components, variants, tokens, and theme switching.examples/twsx-classname-app/— Vite-based runtimetw()v4 demo with variant and slots components.
Run the demos by opening examples/README.md or using the commands below:
cd examples/react-demo
npm install
npm run devcd examples/twsx-classname-app
npm install
npm run devAPI Reference
tw() — The Main Function
One function, four modes:
Mode 1: String → Atomic Classes
tw('flex items-center gap-4 hover:bg-gray-100')
// → "tw-flex tw-items-center tw-gap-4 tw-hover-bg-gray-100"
// CSS is auto-injected with full pseudo-class supportMode 2: Named Class
tw('sidebar', 'w-64 h-screen bg-white border-r border-gray-200')
// → "sidebar"
// Generates .sidebar { ... } with all the Tailwind stylesMode 3: Variants
const button = tw({
name: 'btn',
base: 'px-4 py-2 rounded-lg font-medium transition-all',
variants: {
color: {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
danger: 'bg-red-600 text-white hover:bg-red-700',
ghost: 'bg-transparent text-gray-700 hover:bg-gray-100',
},
size: {
sm: 'text-sm px-3 py-1.5',
md: 'text-base px-4 py-2',
lg: 'text-lg px-6 py-3',
},
},
defaultVariants: { color: 'primary', size: 'md' },
});
button({ color: 'danger', size: 'lg' })
// → "btn btn--color-danger btn--size-lg"Mode 4: Slots (Multi-Part Components)
const card = tw({
name: 'card',
slots: {
root: 'bg-white rounded-xl shadow-lg overflow-hidden',
header: 'px-6 py-4 border-b border-gray-100',
body: 'px-6 py-4',
footer: 'px-6 py-4 bg-gray-50',
},
});
card()
// → { root: "card__root", header: "card__header", body: "card__body", footer: "card__footer" }Utility Methods
tw.extractCSS() // Get all generated CSS as string (SSR)
tw.clearCache() // Clear internal caches
tw.config({ prefix: 'my', hash: false }) // Configure globallytws() — Inline Styles
Convert Tailwind classes directly to CSS styles. No DOM injection needed.
// Returns CSS string
tws('bg-blue-500 p-4 rounded-lg')
// → "background-color: rgb(59,130,246); padding: 1rem; border-radius: 0.5rem;"
// Returns JSON object (for React style prop, etc.)
tws('flex items-center gap-4', true)
// → { display: 'flex', alignItems: 'center', gap: '1rem' }cx() — Conditional Class Names
A lightweight clsx alternative built-in.
cx('base-class', isActive && 'active-class', { 'disabled': isDisabled })
// → "base-class active-class"
// Arrays work too
cx(['p-4', 'bg-white'], condition && ['ring-2', 'ring-blue-500'])
// Create pre-filled cx
const btnClass = cx.with('px-4 py-2 rounded font-medium');
btnClass('bg-blue-500') // → "px-4 py-2 rounded font-medium bg-blue-500"React Bindings
import { styled, ThemeProvider, useTheme, useTws } from 'tailwind-to-style/react';styled() — Create Styled Components
import { styled } from 'tailwind-to-style/react';
const Button = styled('button', {
name: 'btn',
base: 'px-4 py-2 rounded-lg font-medium transition-colors',
variants: {
color: {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
},
size: {
sm: 'text-sm px-3 py-1.5',
lg: 'text-lg px-6 py-3',
},
},
defaultVariants: { color: 'primary', size: 'sm' },
});
// Variant props are type-safe and stripped from DOM
<Button color="primary" size="lg" onClick={handleClick}>
Click Me
</Button>ThemeProvider & useTheme
import { ThemeProvider, useTheme } from 'tailwind-to-style/react';
const theme = {
colors: { primary: '#3b82f6', danger: '#ef4444' },
radius: { sm: '0.25rem', md: '0.5rem' },
};
function App() {
return (
<ThemeProvider theme={theme}>
<MyComponent />
</ThemeProvider>
);
}
function MyComponent() {
const { theme, setTheme } = useTheme();
// Access tokens via CSS variables: var(--tws-colors-primary)
}useTws() — Inline Style Hook
import { useTws } from 'tailwind-to-style/react';
function Box({ classes }) {
const style = useTws(classes); // memoized style object
return <div style={style}>Content</div>;
}Design Tokens
import { createTheme, tokenRegistry, token } from 'tailwind-to-style/tokens';createTheme()
createTheme({
colors: {
primary: '#3b82f6',
secondary: '#8b5cf6',
success: '#10b981',
},
spacing: { sm: '0.5rem', md: '1rem', lg: '1.5rem' },
radius: { sm: '0.25rem', md: '0.5rem', lg: '1rem' },
}, { selector: ':root' });
// Injects CSS variables on :root:
// --tws-colors-primary: #3b82f6;
// --tws-colors-secondary: #8b5cf6;
// ...tokenRegistry
tokenRegistry.get('colors.primary') // → '#3b82f6'
tokenRegistry.set('colors.primary', '#2563eb')
tokenRegistry.toCSS() // → full :root CSS string
tokenRegistry.subscribe((tokens) => { /* react to changes */ })token() — CSS Variable Reference
token('colors.primary') // → "var(--tws-colors-primary)"
token('colors.primary', '#000') // → "var(--tws-colors-primary, #000)"Animations
import { animate, defineAnimation, getAnimationNames } from 'tailwind-to-style/animations';Built-in Presets
element.className = animate('fadeIn');
element.className = animate('slideInUp', { duration: '500ms', delay: '100ms' });
element.className = animate('bounce');
element.className = animate('spin'); // infiniteAvailable presets: fadeIn, fadeOut, slideInUp, slideInDown, slideInLeft, slideInRight, scaleIn, scaleOut, bounce, shake, pulse, spin, ping
Custom Animations
defineAnimation('wiggle', {
keyframes: [
{ transform: 'rotate(0deg)' },
{ transform: 'rotate(-3deg)' },
{ transform: 'rotate(3deg)' },
{ transform: 'rotate(0deg)' },
],
duration: '300ms',
easing: 'ease-in-out',
});
animate('wiggle'); // works!SSR (Server-Side Rendering)
import { tw, createSSRCollector } from 'tailwind-to-style';
// Collect all CSS generated during render
const collector = createSSRCollector();
const html = renderToString(<App />);
const css = collector.extract();
// Inject into HTML head
const fullHtml = `
<html>
<head><style>${css}</style></head>
<body>${html}</body>
</html>
`;Tree-Shakeable Imports
Import only what you need for minimal bundle size:
| Import Path | What You Get | ~Size |
|---|---|---|
| tailwind-to-style | tw, tws, cx | Full engine |
| tailwind-to-style/react | styled, ThemeProvider, useTheme, useTws | +2KB |
| tailwind-to-style/tokens | createTheme, tokenRegistry, token | +1KB |
| tailwind-to-style/animations | animate, defineAnimation | +1.5KB |
| tailwind-to-style/cx | cx only | ~300B |
| tailwind-to-style/tws | tws only | Subset |
Framework Support
Works with any framework or vanilla JS:
- React — Full bindings via
tailwind-to-style/react(example available inexamples/react-demo) - Vanilla JS — Direct DOM usage with
tw()andtws() - Node.js / SSR —
tws()for inline styles +createSSRCollector()for CSS extraction - Vue / Svelte — supported in runtime with
tw()/tws(), examples can be added in future releases
License
MIT © Bigetion
