npm package: @nillos/vite-plugin-nillos-px2vw
Description: Custom Vite plugin for px to vw conversion with media query wrapper
Author: Nillos Tanner
Latest version: 1.0.3

# @nillos/vite-plugin-nillos-px2vw

A highly customizable Vite plugin that converts `px` to `vw` using PostCSS under the hood. It allows you to define multiple conversion rules based on file paths, automatically wrap specific stylesheets in media queries, and gives you fine-grained control over what gets converted.

Perfect for monolithic setups (like Laravel + Vue 3) where you need strict separation between desktop and responsive styles.

## ✨ Features

- **Multiple Targets:** Set different `viewportWidth` for different files (e.g., 1920px for desktop, 360px for mobile).
- **RegEx File Matching:** Precisely target files or directories using Regular Expressions.
- **Media Query Wrapping:** Automatically wrap mobile styles in a specific `@media` query.
- **Original Value Comments:** Keeps original `px` values as comments for easy debugging (e.g., `/* 16px */`).
- **Property Filtering (`propList`):** Whitelist specific CSS properties to convert (supports exact matches and RegEx).
- **Selector Blacklisting (`selectorBlackList`):** Ignore specific classes, IDs, or tags.
- **Inline Directives:** Disable conversion for specific lines directly in your SCSS/CSS using `/* px-to-vw-ignore */`.
- **A11y Font Clamping (`fontClamp`):** Optionally convert font sizes to `clamp()` to prevent text from becoming too microscopic or unnaturally huge.
- **Output Compression (`compress`):** Removes debugging comments and minifies AST whitespace to save production build size.

## 📦 Installation

```bash
npm install -D @nillos/vite-plugin-nillos-px2vw
# or
yarn add -D @nillos/vite-plugin-nillos-px2vw
```

## 🚀 Usage

Add the plugin to your `vite.config.ts` (or `vite.config.js`).

### Full Example (Laravel + Vue 3 Setup)

```typescript
import { defineConfig } from "vite";
import customPxToVw from "@nillos/vite-plugin-nillos-px2vw";

export default defineConfig({
  plugins: [
    customPxToVw({
      // 1. Define targets based on file paths
      targets: [
        {
          // Target 1: Mobile styles (matches any file inside a "responsive" folder or named responsive.scss)
          fileMatch: /responsive/,
          viewportWidth: 360,
          wrapMediaQuery: "max-width: 992px", // Automatically wraps output
        },
        {
          // Target 2: Desktop styles (matches all .scss files EXCEPT those containing "responsive")
          fileMatch: /^(?!.*responsive).*\.scss$/,
          viewportWidth: 1920,
        },
      ],

      // 2. Global Conversion Settings
      minPixelValue: 1, // Px values <= 1 will not be converted (e.g., borders)
      unitPrecision: 5, // Decimal precision for generated vw values

      // 3. Advanced Filtering
      // Leave empty [] or omit to convert ALL properties.
      // propList: ['font-size', 'width', 'height', /^margin-/, /^padding-/],

      // Ignore specific selectors (supports RegEx)
      selectorBlackList: ["body", ".no-vw", /^#admin-panel/],

      // Allow disabling conversion via /* px-to-vw-ignore */ comment
      enableInlineDirectives: true,

      // Convert font-size and line-height into clamp(min, vw, max) for better accessibility
      fontClamp: true,

      // Remove debug comments and strip whitespaces to save file size
      compress: process.env.NODE_ENV === "production", // Recommended to enable only in production
    }),
  ],
});
```

## 🔄 Input / Output Example

**Input (`responsive.scss`):**

```css
.container {
  width: 360px;
  border: 1px solid black; /* Ignored due to minPixelValue: 1 */
  margin-top: 50px; /* px-to-vw-ignore */
}

.no-vw {
  width: 100px; /* Ignored due to selectorBlackList */
}

.text {
  font-size: 16px; /* Converted to clamp() due to fontClamp: true */
}
```

**Output (Compiled CSS):**

```css
@media (max-width: 992px) {
  .container {
    width: 100vw /* 360px */;
    border: 1px solid black;
    margin-top: 50px;
  }

  .no-vw {
    width: 100px;
  }

  .text {
    font-size: clamp(12px, 4.44444vw, 24px) /* 16px */;
  }
}
```

## ⚙️ Configuration Options

| Option                   | Type                   | Default      | Description                                                                                                            |
| ------------------------ | ---------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------- |
| `targets`                | `ViewportTarget[]`     | **Required** | Array of target rules for specific files.                                                                              |
| `minPixelValue`          | `number`               | `1`          | Minimum pixel value to replace. Values smaller than or equal to this will be ignored.                                  |
| `unitPrecision`          | `number`               | `5`          | The decimal precision of the generated VW units.                                                                       |
| `propList`               | `(string \| RegExp)[]` | `[]` (All)   | The properties that can change from px to vw. Leave empty or omit to convert everything.                               |
| `selectorBlackList`      | `(string \| RegExp)[]` | `[]`         | The selectors to ignore and leave as px.                                                                               |
| `enableInlineDirectives` | `boolean`              | `true`       | Enables `/* px-to-vw-ignore */` comment to ignore the preceding CSS declaration.                                       |
| `fontClamp`              | `boolean`              | `false`      | If true, `font-size` and `line-height` will be converted to `clamp()` values instead of pure `vw`.                     |
| `compress`               | `boolean`              | `false`      | If true, removes original `/* 16px */` comments and strips whitespaces inside media queries for a smaller bundle size. |

### `ViewportTarget` Object

| Property         | Type     | Required | Description                                                                             |
| ---------------- | -------- | -------- | --------------------------------------------------------------------------------------- |
| `fileMatch`      | `RegExp` | Yes      | Regular expression to match the absolute file path.                                     |
| `viewportWidth`  | `number` | Yes      | The size of the viewport to calculate the `vw` value.                                   |
| `wrapMediaQuery` | `string` | No       | Wraps the transformed CSS inside the specified media query (e.g. `'max-width: 992px'`). |

## 📄 License

MIT