@osmn-byhn/css-formatter
v1.0.2
Published
Format CSS with smart selectors
Maintainers
Readme
CSS Inliner
Bidirectional CSS-HTML converter with smart selector generation
Convert between CSS and inline styles effortlessly. Perfect for email templates, production optimization, and development workflows.
🚀 Features
Forward Conversion (CSS → Inline)
- ✅ Full CSS selector support - element, class, descendant, compound selectors
- ✅ Preserve responsive styles -
@media,@import,@keyframes,@font-face - ✅ Keep pseudo-classes -
:hover,:focus,:activein<style>tags - ✅ Smart style merging - respects CSS specificity and existing inline styles
Reverse Conversion (Inline → CSS)
- ✅ Smart selector generation - uses existing classes, creates nested selectors
- ✅ Style deduplication - groups identical styles under combined selectors
- ✅ Minimal HTML changes - prioritizes existing structure over auto-classes
- ✅ Two output modes - internal
<style>tag or external CSS file
📦 Installation
npm install css-inliner
# or
pnpm add css-inliner
# or
yarn add css-inliner🎯 Quick Start
Forward: CSS to Inline Styles
import { inlineCSS } from 'css-inliner';
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>Reverse: Inline Styles to CSS (Internal)
import { reverseCSSInternal } from 'css-inliner';
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>Reverse: Inline Styles to External CSS
import { reverseCSSExternal } from 'css-inliner';
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; }📚 API Reference
inlineCSS(input: string): Promise<string>
Converts CSS rules to inline styles.
Parameters:
input- HTML string or URL
Returns: HTML string with inlined CSS
Features:
- Inlines all CSS rules into element
styleattributes - Preserves
@media,@import,@keyframes,@font-facein<style>tag - Keeps pseudo-classes (
:hover,:focus) in<style>tag - Respects CSS specificity
- Merges with existing inline styles
reverseCSSInternal(input: string): Promise<string>
Extracts inline styles to internal CSS (<style> tag).
Parameters:
input- HTML string or URL
Returns: HTML string with CSS in <style> tag
Smart Selector Strategy:
- Use existing classes -
.header,.nav-list - Create nested selectors -
.parent .child,.nav a - Use element selectors -
body,html,h1 - Auto-generate only when needed -
.auto-style-1,.auto-style-2
Features:
- Deduplicates identical styles
- Preserves existing CSS (
@media,:hover, etc.) - Minimal HTML modifications
- Clean, semantic CSS output
reverseCSSExternal(input: string): Promise<{html: string, css: string}>
Extracts inline styles to external CSS file.
Parameters:
input- HTML string or URL
Returns: Object with html and css strings
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🎨 Use Cases
Email Templates
// Convert CSS to inline for email clients
const emailHTML = await inlineCSS(template);
sendEmail(emailHTML);Production Optimization
// Extract inline CSS to cacheable file
const { html, css } = await reverseCSSExternal(buildOutput);
writeFileSync('index.html', html);
writeFileSync('styles.css', css);Development Workflow
// Convert messy inline styles to readable CSS
const readable = await reverseCSSInternal(legacyHTML);Round-Trip Conversion
// Chain conversions for different outputs
const inlined = await inlineCSS(original);
const withStyleTag = await reverseCSSInternal(inlined);
const { html, css } = await reverseCSSExternal(withStyleTag);🔧 Advanced Examples
Responsive Design
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 inlinedFont Imports
const html = `
<style>
@import url('https://fonts.googleapis.com/css2?family=Roboto');
body { font-family: 'Roboto', sans-serif; }
</style>
<body>Text</body>
`;
const result = await inlineCSS(html);
// @import is preserved
// font-family is inlined with proper quote escapingNested Selectors
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>📊 Performance
- Fast parsing - Uses optimized CSS and HTML parsers
- Minimal overhead - Efficient DOM traversal and style matching
- Memory efficient - Streaming-friendly architecture
🧪 Testing
# Run tests
pnpm test
# Watch mode
pnpm test:watch
# Coverage
pnpm test:coverage🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📄 License
MIT © Osman Beyhan
🙏 Acknowledgments
Built with:
- css-tree - CSS parser
- htmlparser2 - HTML parser
- dom-serializer - DOM to HTML
Made with ❤️ for developers who work with HTML and CSS
