@povio/css-to-tailwind
v0.2.0
Published
Convert CSS styles in HTML and JSX to Tailwind CSS v4 utilities
Keywords
Readme
@povio/css-to-tailwind
Convert CSS applied to HTML, JSX, and TSX elements into validated Tailwind CSS v4 utilities.
@povio/css-to-tailwind discovers local stylesheets and inline styles, computes the applicable CSS declarations, and appends equivalent Tailwind classes while preserving existing classes, formatting, comments, and unsupported runtime-dependent styles.
Features
- Converts
.html,.htm,.jsx, and.tsxfiles. - Uses Tailwind CSS v4 defaults or a CSS-first Tailwind entry file.
- Discovers linked CSS, side-effect imports, inline
<style>blocks, and nested local@importrules. - Converts safe parts of mixed inline styles while retaining dynamic values such as
{{ nav.mark }}. - Preserves existing classes and validates generated utilities against Tailwind.
- Supports atomic in-place replacement or a parallel output tree.
- Provides both a CLI and a typed ESM/CommonJS library API.
Requirements
- Node.js 24 or newer for the published CLI and library.
- Tailwind CSS v4.
clsxin transformed JSX projects when a dynamicclassNamerequires generated utilities.- Bun 1.4 or newer when developing this repository from source.
Installation
Install the CLI globally with Bun:
bun add --global @povio/css-to-tailwindOr install it in a project as a development dependency:
bun add --dev @povio/css-to-tailwindYou can also run the published package without adding it to a project:
bunx --package @povio/css-to-tailwind css-to-tailwind --helpQuick start
Write converted files to a separate directory while preserving their paths:
css-to-tailwind src/page.html src/components/Card.tsx --output-dir convertedThe converted files will be written to:
converted/src/page.html
converted/src/components/Card.tsxTo replace inputs in place, omit --output-dir:
css-to-tailwind src/page.html src/components/Card.tsxIn-place conversion parses every input before writing anything and replaces files atomically. Commit or back up inputs before running it.
CLI reference
css-to-tailwind [options] <inputs...>| Argument or option | Description |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| <inputs...> | One or more .html, .htm, .jsx, or .tsx input files. |
| --css <path> | Apply an additional CSS file to every input. Repeat the option to add multiple files. |
| --tailwind-css <path> | Use a Tailwind CSS v4 entry file for theme variables, utilities, variants, plugins, prefixes, or @config. |
| -o, --output-dir <directory> | Write converted files to a parallel directory tree instead of replacing inputs. |
| --include-ds | Include CSS inside _ds directories. These generated design-system bundles are skipped by default. |
| --remove-styles | Remove a stylesheet reference only when its complete source converted safely. |
| -V, --version | Print the installed package version. |
| -h, --help | Print CLI usage and available options. |
Display the built-in help at any time:
css-to-tailwind --helpWhen installed locally, use:
bunx css-to-tailwind --helpExamples
Apply shared CSS to every input:
css-to-tailwind src/page.html \
--css src/styles/reset.css \
--css src/styles/components.cssUse a CSS-first Tailwind theme and preserve the source tree:
css-to-tailwind src/page.html src/Card.tsx \
--tailwind-css src/styles/tailwind.css \
--output-dir convertedInclude a generated _ds stylesheet that would otherwise be skipped:
css-to-tailwind src/page.html --include-dsRemove only fully converted stylesheet references:
css-to-tailwind src/page.html --remove-stylesCSS discovery
For HTML, the CLI loads:
- Inline
<style>blocks. - Local
<link rel="stylesheet">references. - CSS passed through repeatable
--cssoptions. - Nested local CSS
@importdependencies.
For JSX and TSX, it loads relative side-effect imports such as:
import "./card.css";Remote stylesheets and CSS Modules are skipped. CSS files inside a directory named _ds are also skipped by default because they commonly contain the compiled Tailwind utilities being targeted rather than source styles to convert. Use --include-ds to include them.
--tailwind-css is never affected by the _ds exclusion because it configures the target Tailwind design system rather than supplying styles to convert.
Source CSS files are never rewritten or deleted. With --output-dir, discovered CSS dependencies are copied unchanged into the equivalent output tree. Unrelated HTML assets and files referenced through CSS url() declarations are not copied.
Tailwind CSS v4 configuration
Without --tailwind-css, conversion uses Tailwind v4 defaults through an implicit entry:
@import "tailwindcss";Pass a Tailwind CSS entry to use project-specific theme variables, breakpoints, custom utilities, variants, plugins, prefixes, or a legacy @config directive:
@import "tailwindcss";
@theme {
--color-brand: #315cf5;
--breakpoint-wide: 90rem;
}css-to-tailwind src/page.html --tailwind-css src/styles/tailwind.cssThe entry is used only for utility selection and validation. It is not generated or rewritten.
HTML and inline styles
Existing classes are preserved and generated utilities are appended:
<!-- Before -->
<div class="card" style="display: grid; gap: 4px">Hello</div>
<!-- After -->
<div class="card grid gap-1">Hello</div>Runtime-dependent declarations remain inline while safe siblings convert:
<!-- Before -->
<span style="width:3px;height:16px;background:{{ nav.mark }}"></span>
<!-- After -->
<span style="background:{{ nav.mark }}" class="w-[0.1875rem] h-4"></span>JSX and TSX
Static classes are preserved. When applicable styles are statically known but className is dynamic, the converter rewrites it through clsx:
// Before
<button className={active ? "active" : undefined} />
// After
<button className={clsx(active ? "active" : undefined, "rounded-md px-4")} />An existing clsx import is reused. Otherwise, a collision-safe import is added and a CLSX_REQUIRED diagnostic is returned. The transformed project must have clsx installed:
bun add clsxStatic properties in style={{ ... }} are converted. Spreads, unsupported properties, and dynamic values remain in the style object.
Library API
The package exposes ESM and CommonJS builds with TypeScript declarations.
Convert source in memory
import { createConverter } from "@povio/css-to-tailwind";
const converter = await createConverter({
tailwindCss: {
filename: "/project/src/tailwind.css",
code: '@import "tailwindcss"; @theme { --color-brand: #315cf5; }',
},
});
const result = await converter.convert({
filename: "/project/src/card.html",
code: '<div class="card" style="color: #315cf5">Hello</div>',
cssSources: [
{
filename: "/project/src/card.css",
code: ".card { padding: 1rem; }",
origin: "explicit",
},
],
removeStyles: false,
});
console.log(result.code);
console.log(result.generatedClasses);
console.log(result.dependencies);
console.log(result.diagnostics);Convert files
convertFiles performs discovery and is the only public API that writes to the filesystem:
import { convertFiles } from "@povio/css-to-tailwind";
const results = await convertFiles({
inputs: ["src/page.html", "src/Card.tsx"],
cssPaths: ["src/styles/global.css"],
tailwindCssPath: "src/styles/tailwind.css",
outputDir: "converted",
removeStyles: false,
includeDesignSystemCss: false,
cwd: process.cwd(),
});The package exports ConversionResult, FileConversionResult, CssSource, Diagnostic, and all converter option types.
Diagnostics and exit codes
Warnings include a code, source filename, and line/column when available. They report skipped selectors, unsupported at-rules, remote stylesheets, CSS Modules, retained stylesheet references, required clsx installation, and assets that are not copied.
- Exit
0: conversion completed, including conversions with warnings. - Exit
1: invalid arguments, unsupported inputs, fatal template parsing, unsafe paths, Tailwind configuration failures, or filesystem errors.
Known limitations
- Input templates are limited to
.html,.htm,.jsx, and.tsx. - CSS Modules, Vue, Svelte, runtime-rendered DOM, and remote stylesheets are not evaluated.
- Selectors depending on runtime-only classes or unresolved JSX component output are retained.
- Output-directory mode does not copy unrelated HTML assets or files referenced through CSS
url()declarations. - Stylesheet removal is deliberately conservative; unmatched or unsupported rules keep their original reference.
Development
Install dependencies:
bun installRun the CLI directly from TypeScript:
bun run cli --help
bun run cli src/page.html --output-dir convertedRun the project checks:
bun test
bun run lint
bun run typecheck
bun run format:check
bun run buildThe production build is written to dist/ and contains the executable CLI, ESM/CommonJS library bundles, source maps, and type declarations.
