Bidirectional CSS-HTML converter with smart selector generationConvert between CSS and inline styles effortlessly. Perfect for email templates, production optimization, and development workflows.
@media,
@import, @keyframes,
@font-face
:hover, :focus, :active in <style>
tagsstyletag
or external CSS filenpm install @osmn-byhn/css-formatter
# or
pnpm add @osmn-byhn/css-formatter
# or
yarn add @osmn-byhn/css-formatter
import { inlineCSS } from '@osmn-byhn/css-formatter';
const html = `
<style>
.header {
color: blue;
font-size: 24px;
}
.header:hover {
color: darkblue;
}
</style>
<div class="header">Hello World</div>
`;
const result = await inlineCSS(html);
console.log(result);
// Output:
// <style>
// .header:hover {
// color: darkblue;
// }
// </style>
// <div class="header" style="color:blue;font-size:24px">Hello World</div>
import { reverseCSSInternal } from '@osmn-byhn/css-formatter';
const html = `
<div class="header" style="color:blue;font-size:24px">Hello</div>
<div class="header" style="color:blue;font-size:24px">World</div>
`;
const result = await reverseCSSInternal(html);
console.log(result);
// Output:
// <style>
// .header { color:blue; font-size:24px; }
// </style>
// <div class="header">Hello</div>
// <div class="header">World</div>
import { reverseCSSExternal } from '@osmn-byhn/css-formatter';
import { writeFileSync } from 'fs';
const html = `
<div class="nav" style="display:flex;gap:20px">Navigation</div>
`;
const { html: htmlOutput, css: cssOutput } = await reverseCSSExternal(html);
writeFileSync('index.html', htmlOutput);
writeFileSync('styles.css', cssOutput);
// index.html:
// <link rel="stylesheet" href="styles.css">
// <div class="nav">Navigation</div>
// styles.css:
// .nav { display:flex; gap:20px; }
inlineCSS(input: string): Promise
input - HTML string or URL
style attributes@media, @import, @keyframes,
@font-face in
<style>
tag
:hover, :focus) in <style>
tag
reverseCSSInternal(input: string): Promise<string><style> tag). Parameters:
input- HTML string or URL<style>tag Smart Selector Strategy:
.header,
.nav-list
.parent .child,
.nav a
body,
html,
h1
.auto-style-1,
.auto-style-2
@media, :hover, etc.)reverseCSSExternal(input: string): Promise< {
html: string, css: string
}
>input- HTML string or URLhtmland cssstrings Use Case:Production websites that
benefit from browser caching const { html, css } = await reverseCSSExternal(inlinedHTML);
// html: contains <link rel="stylesheet" href="styles.css">
// css: all extracted CSS rules
// Convert CSS to inline for email clients
const emailHTML=await inlineCSS(template);
sendEmail(emailHTML);
// Extract inline CSS to cacheable file
const {
html,
css
}
=await reverseCSSExternal(buildOutput);
writeFileSync('index.html', html);
writeFileSync('styles.css', css);
// Convert messy inline styles to readable CSS
const readable=await reverseCSSInternal(legacyHTML);
// Chain conversions for different outputs
const inlined=await inlineCSS(original);
const withStyleTag=await reverseCSSInternal(inlined);
const {
html,
css
}
=await reverseCSSExternal(withStyleTag);
const html = `
<style>
.container {
max-width: 1200px;
}
@media (max-width: 768px) {
.container {
max-width: 100%;
}
}
</style>
<div class="container">Content</div>
`;
const result = await inlineCSS(html);
// @media queries are preserved in <style> tag
// Regular styles are inlined
const html=` <style>@import url('https://fonts.googleapis.com/css2?family=Roboto');
body {
font-family: 'Roboto', sans-serif;
}
</style>
Text
`;
const result = await inlineCSS(html);
// @import is preserved
// font-family is inlined with proper quote escaping
const html = `
<div class="nav">
<a style="color:blue;text-decoration:none">Link 1</a>
<a style="color:blue;text-decoration:none">Link 2</a>
</div>
`;
const result = await reverseCSSInternal(html);
// Output:
// <style>
// .nav a { color:blue; text-decoration:none; }
// </style>
// <div class="nav">
// <a>Link 1</a>
// <a>Link 2</a>
// </div>
# Run tests
pnpm test
# Watch mode
pnpm test:watch
# Coverage
pnpm test:coverage
Author: Osman Beyhan
โค๏ธ Make via MDtoWeb