npm package: @pastweb/atomic-css-vite
Description: plugin for Atom Css Utility Modules
Author: Domenico Pasto
Latest version: 1.1.2

# Atomic CSS plugin for Vite
A [Vite] plugin inspired to [CSS Modules] and [Tailwind] CSS framework.

[vite]: https://github.com/vitejs/vite
[css modules]: https://github.com/css-modules/css-modules
[tailwind]: https://github.com/tailwindlabs/tailwindcss

* 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](https://github.com/pastweb/atomic-css) page.

## install
```bash
npm i -D @pastweb/atomic-css-vite
```

## Usage
```js
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](#options)
* [cl function](#cl-function)
* [astPlugins](#astPlugins)
* [Limitations](#limitations)

## Options
All options are available as described in the [documentation](https://github.com/pastweb/atomic-css), 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:**
```tsx
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](https://github.com/pastweb/tools?tab=readme-ov-file#cl).

## 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](https://github.com/facebook/react), [preact](https://github.com/preactjs/preact), [vue](https://github.com/vuejs) and [svelte](https://github.com/sveltejs/svelte).

You can check the [example](https://github.com/pastweb/atom-css/tree/master/packages/vite/examples/rimmel) for [rimmel](https://github.com/ReactiveHTML/rimmel).

astPlugin example:
```js
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 parameterr `Set<string>` to the ast function.
  * `defaultSpecifier`: is optional and is the default value to be used.
* `ast`: is an Object `Record<string, (node: Node, specifiers: Set<string>) => void | Node | [ string, string ] | Promise<void | Node | [ string, string ]>`.
How you can see the ast function can be an `async` function and it can returns `void` for no operation, an array `[ filePath, classNames ]` or the ast `Node` which represent the classes string.
Even the string composition are supported as like any class composition function like [clsx](https://github.com/lukeed/clsx) for react which follows this parameters syntax.
To analyze your code you can use [astexplorer](https://astexplorer.net/) selecting `acorn` as ast standard;

example:
```svelte
<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:
```vue
<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.