@pastweb/atomic-css-vite v1.1.2
Atomic CSS plugin for Vite
A Vite plugin inspired to CSS Modules and Tailwind CSS framework.
- Zero configuration
- Reduce the css size nesting the selectors where convenient.
- Handle CSS modules.
- Scopes CSS variables.
- Calculate atomic css utilities on the fly and assign them in the CSS module object.
- Treeshake CSS removing the unused classes.
For more info about tools and the options check the Atomic CSS page.
install
npm i -D @pastweb/atomic-css-viteUsage
import { defineConfig } from 'vite'
import preact from '@preact/preset-vite'
import { atomicCss } from '@pastweb/atomic-css-vite';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
preact(), // or any other framework plugin
atomicCss(), // atomCss({ ...options }),
],
})Summary
Options
All options are available as described in the documentation, with the exception for the following:
getModules, getUtilityModules and test which are used internally in the vite plugin.
usedClasses is a boolean (true by default) in case you don't want to use the astPlugins in order to remove the unused classes from the css.
The mode functionality in the utility option is set to semireadable for development and encoded for production by default, and the output functionality is not available
as it is used internally in order to collect all the utilities which will be rendered in the main css output file in oder to be available as soon as possible for production, and will be a separated style tag (<style id="atom-css-utilities"></style>) for development modality.
cl function
As often happen, a single Element could need to define more then one class and, as the classes are splitted in atomic utilities we coiuld see a lot of utilities classes duplication.
To avoid this problem you cas use the cl function as in the example below:
Example:
import { useState } from 'react';
import { cl } from '@pastweb/tools';
import classes from './Panel.module.css';
const cls = cl.setClasses(classes)''
export default function Panel() {
const [isOpen, setIsOpen] = useState(false);
return (
<div className={cls('Panel')}>
<div className={cls('panel-header', { isOpen })}>
<div className={cls('panel-box')}>
this is the Panel Header
</div>
</div>
this is the content
<div className={cls('panel-footer', { isOpen })}>
<div className={cls('panel-box')}>
this is the panel footer
</div>
</div>
</div>
);
}For more info about the cl function click here.
AstPlugins
The AstPlugin is a plugin which read the javascript source file in order to exctract the classNames used in your source code.
This list o classes are later passes to atomic-css in order to remove the unused classes from the resultant css code.
There are already internal plugins in order to provide this functionality for the most used Front End frameworks such as react, preact, vue and svelte.
You can check the example for rimmel.
astPlugin example:
export default defineConfig({
plugins: [
atomicCss({
astPlugins: [
{
name: 'react',
import: {
source: /^react\/?(jsx-runtime|jsx-dev-runtime)?/,
specifier: /^createElement$|^jsx(s|DEV)?$/,
defaultSpecifier: 'createElement',
},
ast: {
['CallExpression'](node, specifiers) {
if (!specifiers.has(node.callee.name)) return;
if (node.arguments[1].type !== 'ObjectExpression') return;
const [ properties ] = node.arguments[1]; // the ast node representing the props object
if (!properties) return;
for (const { type, name, value } of properties) {
if (type === 'Property' && name === 'className') return value;
}
}
},
},
],
}),
],
});In the example above is described an astPlugin for react.
name: the plugin name (it gets the framework name by convention);import: the import information needed to identify the the ast node (in the above example the react jsx or createEleemnt function) which will be passed the the ast function later.source: is a regular expression for detect the framework import line.specifier: is a regular expression for detect the function specifier/s which will be passed as second parameterrSet<string>to the ast function.defaultSpecifier: is optional and is the default value to be used.
ast: is an ObjectRecord<string, (node: Node, specifiers: Set<string>) => void | Node | [ string, string ] | Promise<void | Node | [ string, string ]>. How you can see the ast function can be anasyncfunction and it can returnsvoidfor no operation, an array[ filePath, classNames ]or the astNodewhich represent the classes string. Even the string composition are supported as like any class composition function like clsx for react which follows this parameters syntax. To analyze your code you can use astexplorer selectingacornas ast standard;
example:
<script>
import clsx from 'clsx';
import classes from './Panel.module.css';
const isOpen = false;
const fullWidth = true;
</script>
<div class={classes.Panel}>
<div class={clsx([classes['panel' + '-' + 'header'], { isOpen }])} fullWidth:fullWidth>
<div class={classes['panel-box']}>
this is the Panel Header
</div>
</div>
this is the content
<div class={clsx([classes['panel-footer'], { isOpen }])}>
<div class={classes['panel-box']}>
this is the panel footer
</div>
</div>
</div>How you can see in the example above the clsx function conditional assignment as the svelte native conditional assignment are supported.
This is valid even for vue, which use an internal function for the conditional assignment.
example:
<script setup>
import { shallowReactive } from 'vue';
import classes from './Panel.module.css';
const state = shallowReactive({ isOpen: false });
</script>
<template>
<div :class="classes.Panel">
<div :class="[classes['panel' + '-' + 'header'], { isOpen: state.isOpen }]">
<div :class="classes['panel-box']">
this is the Panel Header
</div>
</div>
this is the content
<div :class="[classes['panel-footer'], { isOpen: state.isOpen }]">
<div :class="classes['panel-box']">
this is the panel footer
</div>
</div>
</div>
</template>Limitations
How you can see in the examples above, to be able to process the css you need to import the css file in the js file even if you are using a SFC framework.
The css declared inside the tag <style> of a single file component will be not processed.
If lightningcss is used in the vite configuration, the plugin will not process the css.