Small plugin that enables the use of tc39/proposal-import-attributes for css files in vite.
npm install @arcmantle/vite-plugin-import-css-sheetA 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
``bash`
npm install @arcmantle/vite-plugin-import-css-sheetor
pnpm add @arcmantle/vite-plugin-import-css-sheetor
yarn add @arcmantle/vite-plugin-import-css-sheet
Add the plugin to your vite.config.ts:
`typescript
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:
`typescript
// 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 =
;
}
}customElements.define('my-component', MyComponent);
`$3
Perfect for Lit components:
`typescript
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
Styled with imported CSS sheet!
;
}
}
`$3
Include the provided type definitions in your project:
`typescript
// In your vite-env.d.ts or types file
///
`This provides proper TypeScript support for CSS imports:
`typescript
// TypeScript will know this is a CSSStyleSheet
import styles from './styles.css' with { type: 'css' };
`Configuration
The plugin accepts several configuration options:
`typescript
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
})
]
});
`$3
| 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 |Auto-Import Feature
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.$3
When you have co-located CSS files:
`text
src/
āāā my-component.ts
āāā my-component.css
`The plugin can automatically transform your component to include the CSS import, eliminating boilerplate.
$3
Enable auto-import by providing the
autoImport configuration:`typescript
import { importCSSSheet } from '@arcmantle/vite-plugin-import-css-sheet';export default defineConfig({
plugins: [
importCSSSheet({
autoImport: {
identifier: [
{
className: 'LitElement',
styleName: 'styles',
position: 'prepend' // or 'append'
}
]
}
})
]
});
`$3
| 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' |$3
`typescript
// 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
;
}
}
`$3
With auto-import enabled, you can omit the import and static property:
`typescript
// 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
;
}
}
`The plugin automatically transforms this to:
`typescript
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
;
}
}
`$3
If you already have a
static styles property, the auto-import will add to it:`typescript
// 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
}
`$3
You can configure auto-import for multiple base classes:
`typescript
importCSSSheet({
autoImport: {
identifier: [
{
className: 'LitElement',
styleName: 'styles',
position: 'prepend'
},
{
className: 'CustomBaseElement',
styleName: 'styleSheets',
position: 'append'
},
{
className: 'MyFrameworkComponent',
styleName: 'componentStyles'
}
]
}
})
`$3
The auto-import feature only activates when:
1. A
.css file exists with the same name as your component file
2. Your class extends one of the configured base classes
3. The file is a supported type (.ts, .tsx, .js, .jsx, .mts, .mjs)$3
The
position option controls CSS cascade order:-
prepend (default): Component styles come first, can be overridden by later styles
- append: Component styles come last, override earlier styles`typescript
// position: 'prepend'
static styles = [componentStyles, baseStyles, themeStyles];
// ^^^^^^^^^^^^^^ auto-imported (lowest specificity)// position: 'append'
static styles = [baseStyles, themeStyles, componentStyles];
// ^^^^^^^^^^^^^^ auto-imported (highest specificity)
`$3
ā
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
Plugin Architecture
1. Detection: The plugin scans your source files for CSS imports using the
with { type: 'css' } syntax
2. Virtual Modules: Creates virtual modules for CSS files that need to be converted
3. Transformation: Processes CSS through any custom transformers and minification
4. Code Generation: Generates JavaScript code that creates a CSSStyleSheet object
5. Export: Exports the stylesheet as the default exportBrowser Support
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.
Why Use This Plugin?
$3
`typescript
// Traditional Vite CSS import (injects into document)
import './styles.css';
`$3
`typescript
// Get CSS as CSSStyleSheet object
import styles from './styles.css' with { type: 'css' };// Perfect for Shadow DOM
shadowRoot.adoptedStyleSheets = [styles];
`$3
- 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
Examples
$3
`typescript
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
];
`$3
`typescript
// 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];
`Development
`bash
Install dependencies
pnpm installRun demo in development mode
pnpm devBuild 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!
.......