New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details โ†’ โ†’
Socket
Book a DemoSign in
Socket

tailwind-to-style

Package Overview
Dependencies
Maintainers
1
Versions
97
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tailwind-to-style

Runtime Tailwind CSS to inline styles converter. Zero build step, SSR support, tree-shakeable. Works with React, Vue, Svelte, vanilla JS.

latest
Source
npmnpm
Version
3.2.2
Version published
Weekly downloads
157
170.69%
Maintainers
1
Weekly downloads
ย 
Created
Source

tailwind-to-style

๐Ÿ“ฆ View on npm | ๐ŸŒ Landing Page | ๐Ÿ› Playground

npm version Build Status npm downloads bundle size license

Runtime Tailwind CSS to inline styles converter. Zero build step. SSR-ready. Tree-shakeable. Works everywhere โ€” React, Vue, Svelte, Node.js, vanilla JS.

Table of Contents

Why tailwind-to-style?

FeatureDescription
Zero Build StepNo PostCSS, no compilation โ€” just JavaScript
Framework AgnosticReact, Vue, Svelte, vanilla JS
Full Tailwind SupportAll utilities, responsive, pseudo-states, arbitrary values
SCSS-like Nestingtwsx() for complex nested selector-based styles
Variant SystemType-safe component variants like CVA/tailwind-variants
Conditional ClassesBuilt-in cx() utility (like clsx/classnames)
SSR SupportServer-side rendering with startSSR()/stopSSR()
@css DirectiveInject raw CSS for vendor-specific or complex properties
CustomizableExtend theme with colors, spacing, fonts, plugins
TypeScriptFull type definitions with generics for autocomplete
Tree-ShakeableImport only what you need โ€” reduce bundle by 50-70%
Lightweight~12KB minified, zero runtime dependencies
Lightning FastPre-compiled regex + multi-level LRU caching

Installation

npm install tailwind-to-style
yarn add tailwind-to-style
pnpm add tailwind-to-style

CDN (browser):

<script src="https://unpkg.com/tailwind-to-style"></script>
<script>
  const { tws, twsx } = tailwindToStyle
</script>

Quick Start

import { tws, twsx, twsxVariants, cx } from 'tailwind-to-style'

// 1. Inline styles
const style = tws('bg-blue-500 text-white p-4 rounded-lg', true)
// โ†’ { backgroundColor: '#3b82f6', color: '#fff', padding: '1rem', borderRadius: '0.5rem' }

// 2. Real CSS with selectors
twsx({
  '.card': ['bg-white p-6 rounded-xl shadow-md', {
    '&:hover': 'shadow-xl',
    '> .title': 'text-xl font-bold text-gray-900',
  }]
})
// โ†’ auto-injects <style> with .card { ... } .card:hover { ... }

// 3. Component variants
const btn = twsxVariants('.btn', {
  base: 'px-4 py-2 rounded-lg font-medium',
  variants: {
    color: { primary: 'bg-blue-500 text-white', danger: 'bg-red-500 text-white' },
    size:  { sm: 'text-sm', md: 'text-base', lg: 'text-lg' },
  },
  defaultVariants: { color: 'primary', size: 'md' },
})
btn({ color: 'danger', size: 'lg' })  // โ†’ "btn btn-danger-lg"

// 4. Conditional classes
cx('p-4', isActive && 'bg-blue-500', { 'opacity-50': isDisabled })
// โ†’ 'p-4 bg-blue-500'

Core API

tws() โ€” Tailwind to Inline Styles

Converts Tailwind CSS class strings into CSS string or JSON style objects at runtime.

import { tws } from 'tailwind-to-style'

// CSS string (default)
tws('bg-blue-500 p-4 rounded-lg')
// โ†’ "background-color: #3b82f6; padding: 1rem; border-radius: 0.5rem;"

// JSON object (pass `true` as 2nd argument)
tws('flex items-center gap-4', true)
// โ†’ { display: 'flex', alignItems: 'center', gap: '1rem' }

Supported features:

// Responsive breakpoints
tws('text-sm md:text-base lg:text-lg')

// Pseudo-states
tws('bg-blue-500 hover:bg-blue-600 focus:ring-2')

// Arbitrary values
tws('w-[123px] text-[#abc] mt-[2.5rem] grid-cols-[1fr,2fr]')

// Important modifier
tws('!bg-red-500 !text-white')

// Negative values
tws('-mt-4 -translate-x-2')

// Opacity modifier
tws('bg-blue-500/50 text-black/75')

// Decimal spacing
tws('p-0.5 m-1.5 gap-2.5')

Use in React:

<div style={tws('flex items-center gap-4 bg-white p-6 rounded-xl shadow-md', true)}>
  <h1 style={tws('text-2xl font-bold text-gray-900', true)}>Hello</h1>
</div>

twsx() โ€” CSS-in-JS Engine

Generates real CSS from Tailwind classes with full selector support, SCSS-like nesting, and auto-injects a <style> tag into the DOM.

HMR-safe โ€” each twsx() call owns a stable slot in the injected style tag keyed by its top-level selectors. When you edit styles during development, the old slot is replaced (not appended), so changes are reflected immediately without a hard refresh.

import { twsx } from 'tailwind-to-style'

twsx({
  '.button': [
    'bg-blue-500 text-white px-6 py-3 rounded-lg font-medium transition-all',
    {
      '&:hover': 'bg-blue-600 shadow-lg transform scale-105',
      '&:active': 'bg-blue-700 scale-95',
      '&:disabled': 'bg-gray-400 opacity-50 cursor-not-allowed',
      '&.large': 'px-8 py-4 text-lg',
    }
  ],

  '.card': 'bg-white rounded-xl shadow-lg overflow-hidden',
  '.card > .header': 'p-6 border-b border-gray-200',
  '.card > .body': 'p-6',

  // Media queries
  '@media (max-width: 768px)': {
    '.card': 'rounded-lg',
    '.card > .header': 'p-4',
  }
})

Nesting syntax:

PatternExampleDescription
&:pseudo'&:hover': 'bg-blue-600'Pseudo-classes
&.modifier'&.active': 'ring-2'Class modifiers
> .child'> .title': 'text-xl'Direct children
.descendant'.icon': 'w-5 h-5'Descendants
@media'@media (max-width: 768px)': { ... }Media queries

Options:

// Disable auto-injection (returns CSS string only)
const css = twsx({ '.btn': 'bg-blue-500 text-white' }, { inject: false })

@css Directive โ€” Raw CSS Escape Hatch

For CSS that Tailwind can't express, use the @css directive:

String form:

twsx({
  '.gradient-text': '@css { background: linear-gradient(135deg, #667eea, #764ba2); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }',
})

Object form (within arrays):

twsx({
  '.gradient-text': [
    'text-3xl font-bold',
    {
      '@css': {
        'background': 'linear-gradient(90deg, #ff6b6b, #feca57)',
        '-webkit-background-clip': 'text',
        '-webkit-text-fill-color': 'transparent',
      },
    },
  ],
})

twsxVariants() โ€” Component Variant System

A CVA-like API for building type-safe component variants. Auto-generates CSS for all combinations and returns a class name builder function.

import { twsxVariants } from 'tailwind-to-style'

const btn = twsxVariants('.btn', {
  base: 'px-4 py-2 rounded-lg font-medium transition-all border',
  variants: {
    variant: {
      solid: 'shadow-sm',
      outline: 'bg-transparent border-2',
      ghost: 'bg-transparent border-transparent',
    },
    color: {
      primary: 'bg-blue-500 text-white border-blue-500',
      danger: 'bg-red-500 text-white border-red-500',
      neutral: 'bg-gray-100 text-gray-900 border-gray-300',
    },
    size: {
      sm: 'px-3 py-1.5 text-sm',
      md: 'px-4 py-2 text-base',
      lg: 'px-6 py-3 text-lg',
    },
    disabled: {
      true: 'opacity-50 cursor-not-allowed pointer-events-none',
    },
  },
  compoundVariants: [
    { variant: 'outline', color: 'primary', class: 'bg-transparent text-blue-600 border-blue-500' },
    { variant: 'outline', color: 'danger', class: 'bg-transparent text-red-600 border-red-500' },
  ],
  defaultVariants: { variant: 'solid', color: 'primary', size: 'md' },
})

// Usage โ€” returns class name string
btn()                                    // "btn"
btn({ color: 'danger' })                 // "btn btn-danger"
btn({ variant: 'outline', size: 'lg' })  // "btn btn-outline-lg"

Nested selectors โ€” style child elements:

const alert = twsxVariants('.alert', {
  base: 'p-4 rounded-lg border flex gap-3',
  variants: {
    status: {
      info: 'bg-blue-50 border-blue-200 text-blue-800',
      error: 'bg-red-50 border-red-200 text-red-800',
    },
  },
  defaultVariants: { status: 'info' },
  nested: {
    '.alert-icon': 'flex-shrink-0 mt-0.5',
    '.alert-content': 'flex-1',
    '.alert-dismiss': 'p-1 rounded hover:bg-black/10 cursor-pointer',
  }
})
// Generates: .alert .alert-icon { ... }, .alert .alert-content { ... }, etc.

Class naming convention:

CallReturnsWhy
btn()"btn"All defaults
btn({ color: 'danger' })"btn btn-danger"One non-default
btn({ variant: 'outline', color: 'danger', size: 'lg' })"btn btn-outline-danger-lg"All non-defaults

TypeScript โ€” full generics support:

import { twsxVariants, type VariantProps } from 'tailwind-to-style'

const button = twsxVariants('.btn', {
  base: 'px-4 py-2 rounded',
  variants: {
    variant: { solid: 'bg-blue-500', outline: 'border-2' },
    size: { sm: 'text-sm', md: 'text-base', lg: 'text-lg' },
  },
  defaultVariants: { variant: 'solid', size: 'md' },
})

type ButtonProps = VariantProps<typeof button>
// โ†’ { variant?: 'solid' | 'outline', size?: 'sm' | 'md' | 'lg' }

cx() โ€” Conditional Class Builder

A built-in utility for conditionally joining class names โ€” replaces clsx/classnames:

import { cx } from 'tailwind-to-style'

// Strings
cx('bg-blue-500', 'text-white')
// โ†’ 'bg-blue-500 text-white'

// Conditionals
cx('p-4', isActive && 'bg-blue-500', isDisabled && 'opacity-50')
// โ†’ 'p-4 bg-blue-500'

// Object syntax
cx('p-4', { 'bg-blue-500': isActive, 'opacity-50': isDisabled })
// โ†’ 'p-4 bg-blue-500'

// Arrays
cx(['p-4', 'bg-white'], isActive && ['ring-2', 'ring-blue-500'])
// โ†’ 'p-4 bg-white ring-2 ring-blue-500'

// Combined with tws()
const styles = tws(cx('p-4', isLarge && 'p-8', { 'bg-blue-500': isPrimary }))

cx.with() โ€” Base class factory:

const btnClass = cx.with('px-4 py-2 rounded font-medium transition-colors')

btnClass('bg-blue-500 text-white')
// โ†’ 'px-4 py-2 rounded font-medium transition-colors bg-blue-500 text-white'

btnClass({ 'opacity-50': disabled })
// โ†’ 'px-4 py-2 rounded font-medium transition-colors opacity-50'

Configuration & Plugins

configure() โ€” Custom Theme

Extend the default Tailwind theme with custom colors, spacing, fonts, and more.

import { configure } from 'tailwind-to-style'

configure({
  theme: {
    extend: {
      colors: {
        brand: {
          50: '#eff6ff',
          100: '#dbeafe',
          500: '#3b82f6',
          600: '#2563eb',
          900: '#1e3a8a',
        },
        accent: '#f59e0b',
      },
      spacing: {
        '128': '32rem',
        '144': '36rem',
      },
      fontFamily: {
        sans: ['Inter', 'system-ui', 'sans-serif'],
      },
    },
  },
})

// Now use custom values
tws('bg-brand-500 text-brand-50 p-128 font-sans')

Config API:

FunctionDescription
configure(config)Apply custom configuration
getConfig()Get current configuration
resetConfig()Reset to defaults
clearConfigCache()Clear cached config lookups

Plugin System

Create reusable plugins to extend the utility set:

import { configure, createPlugin, createUtilityPlugin } from 'tailwind-to-style'

// Simple plugin โ€” static utilities
const textShadow = createPlugin('text-shadow', {
  utilities: {
    'text-shadow-sm': { textShadow: '0 1px 2px rgba(0,0,0,0.05)' },
    'text-shadow-md': { textShadow: '0 2px 4px rgba(0,0,0,0.1)' },
    'text-shadow-lg': { textShadow: '0 4px 8px rgba(0,0,0,0.15)' },
  },
})

// Dynamic plugin โ€” value-based utilities
const glass = createUtilityPlugin('glass', {
  prefix: 'glass',
  values: { sm: '4px', md: '8px', lg: '16px' },
  formatter: (value) => ({
    backdropFilter: `blur(${value})`,
    backgroundColor: 'rgba(255,255,255,0.1)',
  }),
})

configure({ plugins: [textShadow, glass] })

// Now use custom utilities
tws('text-shadow-md glass-lg')

SSR (Server-Side Rendering)

Collect CSS during server-side rendering instead of injecting into the DOM:

import { startSSR, stopSSR, getSSRStyles, twsx } from 'tailwind-to-style'

// 1. Start collecting
startSSR()

// 2. Render your app (twsx() collects CSS instead of injecting)
twsx({ '.card': 'bg-white p-6 rounded-lg shadow-md' })
twsx({ '.btn': 'bg-blue-500 text-white px-4 py-2 rounded' })
const html = renderToString(<App />)

// 3. Get collected CSS
const css = stopSSR()

// 4. Inject into HTML response
const fullHtml = `
  <html>
    <head><style>${css}</style></head>
    <body>${html}</body>
  </html>
`

SSR API:

FunctionDescription
startSSR()Begin collecting CSS
stopSSR()Stop collecting, return all CSS as string
getSSRStyles()Peek at collected CSS without stopping
IS_BROWSERtrue in browser environment
IS_SERVERtrue in Node.js/server environment

Animation System

Built-in CSS Animations

tws('animate-spin')    // โ†’ animation: spin 1s linear infinite
tws('animate-bounce')  // โ†’ animation: bounce 1s infinite
tws('animate-pulse')   // โ†’ animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite
tws('animate-ping')    // โ†’ animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite

Web Animations API

import { applyWebAnimation } from 'tailwind-to-style'

// Apply a named animation to a DOM element
applyWebAnimation(element, 'fadeIn')
applyWebAnimation(element, 'slideUp')

Inline Animations

import { applyInlineAnimation, animateElement, chainAnimations, staggerAnimations } from 'tailwind-to-style'

// Single animation
applyInlineAnimation(element, 'fadeIn')

// Programmatic animation
animateElement(element, { opacity: [0, 1] }, { duration: 300 })

// Sequential chain
chainAnimations(element, ['fadeIn', 'slideUp', 'bounceIn'])

// Staggered across multiple elements
staggerAnimations('.card', 'fadeIn', { delay: 100 })

Tree-Shakeable Imports

Import only what you need to reduce bundle size by 50-70%:

// Individual imports (recommended for production)
import { tws } from 'tailwind-to-style/tws'                    // ~3KB
import { twsx } from 'tailwind-to-style/twsx'                   // ~6KB
import { twsxVariants } from 'tailwind-to-style/twsx-variants'  // ~6KB
import { cx } from 'tailwind-to-style/cx'                       // <1KB

// Full import (everything)
import { tws, twsx, twsxVariants, cx } from 'tailwind-to-style' // ~12KB
Import PathIncludesSize (minified)
tailwind-to-styleEverything~12KB
tailwind-to-style/twstws() only~3KB
tailwind-to-style/twsxtwsx()~6KB
tailwind-to-style/twsx-variantstwsxVariants()~6KB
tailwind-to-style/cxcx()<1KB
tailwind-to-style/utilsLogger, LRUCache, error handler~2KB

All sub-paths provide ESM + CJS bundles with TypeScript type definitions.

Preflight CSS

For best results, import Tailwind's preflight (base/reset styles):

import 'tailwind-to-style/preflight.css'
<!-- Or in HTML -->
<link rel="stylesheet" href="node_modules/tailwind-to-style/preflight.css">

Provides consistent box-sizing, reset margins/paddings, normalized form elements, and better default font rendering. Skip this if you're already using Tailwind CSS in your project.

Framework Integration

React

import { tws, twsx } from 'tailwind-to-style'
import { useEffect } from 'react'

function App() {
  // Inject CSS on mount
  useEffect(() => {
    twsx({
      '.card': ['bg-white rounded-xl shadow-md p-6', {
        '&:hover': 'shadow-xl',
        '> .title': 'text-xl font-bold',
      }]
    })
  }, [])

  return (
    <div style={tws('flex items-center gap-4', true)}>
      <button style={tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true)}>
        Click me
      </button>
    </div>
  )
}

Vue

<script setup>
import 'tailwind-to-style/preflight.css'
import { tws, twsx } from 'tailwind-to-style'

// twsx() at the top level of <script setup> is HMR-safe.
// When you edit the classes, Vite's HMR re-runs this block and
// the old CSS slot is replaced automatically โ€” no hard refresh needed.
twsx({
  'html': 'bg-gray-100 min-h-screen flex items-center justify-center',
  '.card': [
    'bg-white p-5 border border-gray-300 rounded-xl shadow-md',
    {
      '&:hover': 'shadow-xl',
      '> .title': 'text-xl font-bold text-gray-900 mb-3',
      '> .body': 'text-sm text-gray-700',
    },
  ],
})
</script>

<template>
  <div class="card">
    <div class="title">Card Title</div>
    <p class="body">Styled with twsx โ€” hot reload works out of the box.</p>
  </div>
</template>

For simple inline styles, use tws() with the reactive system:

<script setup>
import { tws } from 'tailwind-to-style'
const btnStyle = tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true)
</script>

<template>
  <button :style="btnStyle">Click me</button>
</template>

Svelte

<script>
  import { tws } from 'tailwind-to-style'
  const style = tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true)
</script>

<button style={Object.entries(style).map(([k,v]) => `${k}:${v}`).join(';')}>
  Click me
</button>

Vanilla JS

import { tws, twsx } from 'tailwind-to-style'

// Inline styles
const el = document.createElement('button')
Object.assign(el.style, tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true))

// Inject global styles
twsx({
  '.card': 'bg-white p-6 rounded-lg shadow-md',
  '.card:hover': 'shadow-xl',
})

Performance

v3.2.0 includes major performance optimizations:

  • Pre-compiled regex โ€” compiled once at module load, reused for every call
  • Multi-level LRU caching โ€” class resolution, CSS generation, config lookups
  • Bounded caches โ€” Maps capped at 5,000 entries, Sets at 10,000 to prevent memory leaks
  • Slot-based CSS injection โ€” each twsx() call owns a named slot; updates rebuild the tag instead of appending, preventing HMR accumulation
  • FNV-1a hashing โ€” 100x faster than JSON.stringify for cache keys
Parse 10,000 classes:
  Cold:   ~12ms
  Cached: ~0.12ms  (100x faster)

Bundle sizes:
  Full import: ~12KB minified
  tws() only:  ~3KB minified
  twsx() only: ~6KB minified

Performance utilities:

import { performanceUtils } from 'tailwind-to-style'

// View cache stats
performanceUtils.getStats()

// Clear all caches
performanceUtils.clearCaches()

// Enable performance logging
performanceUtils.enablePerformanceLogging(true)

Debugging & Logging

Logging is disabled by default. Enable via environment variable or programmatically:

TWSX_LOG_LEVEL=debug npm start   # debug, info, warn, error, silent
import { logger } from 'tailwind-to-style'

logger.setLevel('debug')
console.log(logger.getLevel()) // โ†’ 'debug'
LevelDescription
debugDetailed processing info
infoGeneral information
warnPerformance warnings
errorErrors only
silentNo logging (default)

Comparison

Featuretailwind-to-styleTailwind CSSCSS-in-JStailwind-variants
Build StepNoneRequiredNoneRequired
Bundle Size3-12KB~80KB+20-40KB~15KB
Runtime StylesYesNoYesPartial
Full Tailwind SupportYesYesNoClasses only
SSR SupportYesYesDependsYes
Variant SystemBuilt-inNoNoYes
Conditional Classescx()NoNotv()
SCSS-like NestingYesPluginsYesNo
@css Raw InjectionYesNoYesNo
Framework AgnosticYesYesDependsYes
Tree-ShakingYesPartialYesYes
TypeScript GenericsYesYesYesYes
Zero DependenciesYesPostCSSNotailwind-merge

Migration from v2

See MIGRATION.md for the detailed guide from v2.x to v3.x.

Quick summary:

StatusAPI
No changestws(), twsx(), configure()
New in v3.1twsxVariants()
New in v3.2cx(), SSR, tree-shakeable imports
Removedstyled(), tv(), useTwsx(), TwsxProvider, CLI tools

Contributing

Contributions are welcome! Please read CONTRIBUTING.md for architecture overview, testing guidelines, and build output docs.

๐Ÿ’– Support

If you find this library helpful, consider supporting:

โ˜• Buy me a coffee

License

MIT ยฉ Bigetion

v3.2.0 โ€” Changelog ยท Architecture ยท Migration Guide

Keywords

tailwind

FAQs

Package last updated on 04 Mar 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts