A Vite plugin that enables the use of TC39 Import Attributes proposal for CSS files. Import CSS files with with { type: 'css' } syntax and get them as CSSStyleSheet objects, perfect for Web Components and Shadow DOM.
- 🚀 Import CSS as CSSStyleSheet: Transform CSS imports into constructable stylesheets
- 🎯 TC39 Standard Syntax: Uses the official
with { type: 'css' }syntax - ⚡ Vite Integration: Seamless integration with Vite's build process
- 🤖 Auto-Import: Automatically inject CSS imports for co-located stylesheets
- 🗜️ CSS Minification: Built-in CSS minification using Lightning CSS
- 🔧 Customizable: Support for custom transformers and additional code injection
- 📦 TypeScript Support: Full TypeScript definitions included
- 🔍 Development Friendly: Watch mode support for CSS file changes
npm install @arcmantle/vite-plugin-import-css-sheet
# or
pnpm add @arcmantle/vite-plugin-import-css-sheet
# or
yarn add @arcmantle/vite-plugin-import-css-sheetAdd the plugin to your vite.config.ts:
import { defineConfig } from 'vite';
import { importCSSSheet } from '@arcmantle/vite-plugin-import-css-sheet';
export default defineConfig({
plugins: [
importCSSSheet()
]
});Use the TC39 import attributes syntax to import CSS files as CSSStyleSheet objects:
// Import CSS as CSSStyleSheet
import styles from './component.css' with { type: 'css' };
// Use with Web Components
class MyComponent extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
// Apply the stylesheet to shadow DOM
shadow.adoptedStyleSheets = [styles];
shadow.innerHTML = `
<div class="container">
<h1>Hello World</h1>
</div>
`;
}
}
customElements.define('my-component', MyComponent);Perfect for Lit components:
import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';
import componentStyles from './component.css' with { type: 'css' };
@customElement('my-lit-component')
export class MyLitComponent extends LitElement {
static styles = [componentStyles];
render() {
return html`
<div class="container">
<p>Styled with imported CSS sheet!</p>
</div>
`;
}
}Include the provided type definitions in your project:
// In your vite-env.d.ts or types file
/// <reference types="@arcmantle/vite-plugin-import-css-sheet/client" />This provides proper TypeScript support for CSS imports:
// TypeScript will know this is a CSSStyleSheet
import styles from './styles.css' with { type: 'css' };The plugin accepts several configuration options:
import { importCSSSheet } from '@arcmantle/vite-plugin-import-css-sheet';
export default defineConfig({
plugins: [
importCSSSheet({
// Custom CSS transformers
transformers: [
(code, id) => {
// Custom transformation logic
return code.replace(/\$primary-color/g, '#007bff');
}
],
// Additional code to inject
additionalCode: [
'console.log("CSS sheet loaded:", sheet);'
],
// Disable minification (enabled by default)
minify: false
})
]
});| Option | Type | Default | Description |
|---|---|---|---|
transformers |
((code: string, id: string) => string)[] |
[] |
Array of functions to transform CSS before converting to stylesheet |
additionalCode |
string[] |
[] |
Additional JavaScript code to inject into the generated module |
minify |
boolean |
true |
Whether to minify CSS using Lightning CSS |
autoImport |
object |
undefined |
Configuration for automatic CSS import injection |
The auto-import feature automatically detects when a .css file exists alongside your component file and automatically injects the import statement and adds it to your component's static styles property. This is perfect for component-based frameworks like Lit.
When you have co-located CSS files:
src/
├── my-component.ts
└── my-component.css
The plugin can automatically transform your component to include the CSS import, eliminating boilerplate.
Enable auto-import by providing the autoImport configuration:
import { importCSSSheet } from '@arcmantle/vite-plugin-import-css-sheet';
export default defineConfig({
plugins: [
importCSSSheet({
autoImport: {
identifier: [
{
className: 'LitElement',
styleName: 'styles',
position: 'prepend' // or 'append'
}
]
}
})
]
});| Property | Type | Required | Description |
|---|---|---|---|
className |
string |
Yes | The base class name to detect (e.g., 'LitElement', 'HTMLElement') |
styleName |
string |
Yes | The static property name to inject the stylesheet into (e.g., 'styles') |
position |
'prepend' | 'append' |
No | Where to add the stylesheet in the array. Default: 'prepend' |
// my-component.ts
import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';
import componentStyles from './my-component.css' with { type: 'css' };
@customElement('my-component')
export class MyComponent extends LitElement {
static styles = [componentStyles]; // Manually added
render() {
return html`<div>Hello World</div>`;
}
}With auto-import enabled, you can omit the import and static property:
// my-component.ts
import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';
// Import is automatically injected!
@customElement('my-component')
export class MyComponent extends LitElement {
// static styles is automatically created/updated!
render() {
return html`<div>Hello World</div>`;
}
}The plugin automatically transforms this to:
import my_component_styles from './my-component.css' with { type: 'css' };
import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';
@customElement('my-component')
export class MyComponent extends LitElement {
static styles = [my_component_styles]; // Auto-injected!
render() {
return html`<div>Hello World</div>`;
}
}If you already have a static styles property, the auto-import will add to it:
// Before transformation
import { LitElement } from 'lit';
import sharedStyles from './shared.css' with { type: 'css' };
export class MyComponent extends LitElement {
static styles = [sharedStyles];
}
// After transformation (with position: 'prepend')
import my_component_styles from './my-component.css' with { type: 'css' };
import { LitElement } from 'lit';
import sharedStyles from './shared.css' with { type: 'css' };
export class MyComponent extends LitElement {
static styles = [my_component_styles, sharedStyles]; // Prepended
}
// After transformation (with position: 'append')
export class MyComponent extends LitElement {
static styles = [sharedStyles, my_component_styles]; // Appended
}You can configure auto-import for multiple base classes:
importCSSSheet({
autoImport: {
identifier: [
{
className: 'LitElement',
styleName: 'styles',
position: 'prepend'
},
{
className: 'CustomBaseElement',
styleName: 'styleSheets',
position: 'append'
},
{
className: 'MyFrameworkComponent',
styleName: 'componentStyles'
}
]
}
})The auto-import feature only activates when:
- A
.cssfile exists with the same name as your component file - Your class extends one of the configured base classes
- The file is a supported type (
.ts,.tsx,.js,.jsx,.mts,.mjs)
The position option controls CSS cascade order:
prepend(default): Component styles come first, can be overridden by later stylesappend: Component styles come last, override earlier styles
// position: 'prepend'
static styles = [componentStyles, baseStyles, themeStyles];
// ^^^^^^^^^^^^^^ auto-imported (lowest specificity)
// position: 'append'
static styles = [baseStyles, themeStyles, componentStyles];
// ^^^^^^^^^^^^^^ auto-imported (highest specificity)✅ Less Boilerplate: No need to manually import and assign stylesheets ✅ Co-location: Encourages keeping styles next to components ✅ Consistency: Automatic naming conventions ✅ Flexibility: Works with existing manual imports ✅ Type-Safe: Full TypeScript support with source maps
- Detection: The plugin scans your source files for CSS imports using the
with { type: 'css' }syntax - Virtual Modules: Creates virtual modules for CSS files that need to be converted
- Transformation: Processes CSS through any custom transformers and minification
- Code Generation: Generates JavaScript code that creates a
CSSStyleSheetobject - Export: Exports the stylesheet as the default export
This plugin generates code that uses the Constructable Stylesheets API:
- Chrome/Edge 73+
- Firefox 101+
- Safari 16.4+
For older browsers, consider using a polyfill.
// Traditional Vite CSS import (injects into document)
import './styles.css';// Get CSS as CSSStyleSheet object
import styles from './styles.css' with { type: 'css' };
// Perfect for Shadow DOM
shadowRoot.adoptedStyleSheets = [styles];- Encapsulation: Styles don't leak into global scope
- Performance: No style injection/removal overhead
- Standards Compliant: Uses official TC39 syntax
- Shadow DOM Ready: Perfect for Web Components
- Tree Shakable: Only load styles when needed
import baseStyles from './base.css' with { type: 'css' };
import themeStyles from './theme.css' with { type: 'css' };
import componentStyles from './component.css' with { type: 'css' };
// Combine multiple stylesheets
element.shadowRoot.adoptedStyleSheets = [
baseStyles,
themeStyles,
componentStyles
];// Conditional style loading
const isDark = document.body.classList.contains('dark-theme');
const themeStyles = isDark
? await import('./dark-theme.css' with { type: 'css' })
: await import('./light-theme.css' with { type: 'css' });
shadowRoot.adoptedStyleSheets = [baseStyles, themeStyles.default];# Install dependencies
pnpm install
# Run demo in development mode
pnpm dev
# Build the plugin
pnpm build- Node.js >= 22
- Vite >= 7.0.0
- lightningcss: Fast CSS transformer and minifier for CSS processing
Apache-2.0
This package is part of the Arcmantle Weave monorepo. Contributions are welcome! .......