# @plumeria/eslint-plugin

> Plumeria ESLint plugin

Latest version **21.0.0** (published 2026-10-19) · MIT license · 0 weekly downloads

## Install

```sh
npm install @plumeria/eslint-plugin
pnpm add @plumeria/eslint-plugin
yarn add @plumeria/eslint-plugin
bun add @plumeria/eslint-plugin
```

## Health

**Score 55/100 (C)** — status: active.

Positive: no vulnerabilities; recently updated; high maintenance score.

Warnings: low downloads; no types; no esm support.

## Facts

| | |
|---|---|
| Version | 21.0.0 |
| Published | 2026-10-19 |
| First published | 2025-05-15 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | CommonJS |
| Dependencies | 0 |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 23 |
| Author | Refirst 11 |
| Maintainers | refirst-bot, refirst |
| Keywords | plumeria, eslint, eslint-plugin, oxlint, lint, css-in-js |

## Links

- npm: https://www.npmjs.com/package/@plumeria/eslint-plugin
- Repository: https://github.com/zss-in-js/plumeria
- Homepage: https://plumeria.dev
- Issues: https://github.com/zss-in-js/plumeria/issues
- npm.io page: https://npm.io/package/@plumeria/eslint-plugin

## Alternatives

- [eslint-plugin-sonarjs](https://npm.io/package/eslint-plugin-sonarjs.md) — 2.9M weekly downloads
- [eslint-config-expo](https://npm.io/package/eslint-config-expo.md) — 1.5M weekly downloads
- [@matter/protocol](https://npm.io/package/@matter/protocol.md) — 63.5K weekly downloads
- [@eventcatalog/linter](https://npm.io/package/@eventcatalog/linter.md) — 24.8K weekly downloads
- [@pandacss/eslint-plugin](https://npm.io/package/@pandacss/eslint-plugin.md) — 18.7K weekly downloads

## Recent versions

- 21.0.0 (latest) — 2026-10-19
- 20.0.0 — 2026-10-18
- 19.15.0 — 2026-10-18
- 19.14.4 — 2026-10-18
- 19.14.3 — 2026-10-15
- 19.14.2 — 2026-10-14
- 19.14.1 — 2026-10-14
- 19.14.0 — 2026-10-12
- 19.13.0 — 2026-10-12
- 19.12.1 — 2026-10-10
- 19.12.0 — 2026-10-09
- 19.11.0 — 2026-10-09
- 19.10.4 — 2026-10-09
- 19.10.3 — 2026-10-08
- 19.10.2 — 2026-10-07
- … 386 more at https://npm.io/package/@plumeria/eslint-plugin/versions

## README

# @plumeria/eslint-plugin

ESLint plugin for Plumeria.  
Below are the available rules and the recommended configuration.

## Recommended configuration

The `plugin:@plumeria/recommended` config enables the following:

- `@plumeria/custom-props-require-import`: **error**
- `@plumeria/expand-border-shorthands`: **warn**
- `@plumeria/no-combinator`: **error**
- `@plumeria/no-destructure`: **error**
- `@plumeria/no-inline-object`: **error**
- `@plumeria/no-inner-call`: **error**
- `@plumeria/no-invalid-selector`: **error**
- `@plumeria/no-mixed-styling-props`: **error**
- `@plumeria/no-mutable-binding`: **warn**
- `@plumeria/no-order-dependent-overlap`: **warn**
- `@plumeria/no-style-prop-relay`: **error**
- `@plumeria/no-unknown-css-properties`: **error**
- `@plumeria/no-unresolved-composition`: **warn**
- `@plumeria/no-unused-keys`: **warn**
- `@plumeria/sort-properties`: **warn**
- `@plumeria/format-properties`: **warn**
- `@plumeria/validate-values`: **warn**
- `@plumeria/validate-pseudos`: **warn**
- `@plumeria/validate-at-rules`: **warn**

```js
import plumeria from '@plumeria/eslint-plugin';

export default [plumeria.configs.recommended];
```

## Configuring the styling prop

`custom-props-require-import`, `no-inline-object`, `no-mixed-styling-props`,
`no-style-prop-relay` and `no-unresolved-composition` need to know which JSX
prop carries styles. It is `classStyle` unless you changed it, so most projects
configure nothing.

If you did rename it — via `styleProp` on `withPlumeria` or on the unplugin
options — tell the plugin the same name. The lint the bundler plugins run in the
build is given that name already. ESLint cannot read it from your bundler
config, so set it once in `settings` and every rule picks it up:

```js
import plumeria from '@plumeria/eslint-plugin';

export default [
  plumeria.configs.recommended,
  {
    settings: {
      plumeria: { styleProp: 'sx' },
    },
  },
];
```

A single rule can override that if you need it to:

```js
{
  rules: {
    '@plumeria/no-mixed-styling-props': ['error', { styleProp: 'sx' }],
  },
}
```

The name must match whatever the loader or unplugin was given. If they disagree,
the lint rules report against a prop the compiler never transforms.

## Recommended rules

The following rules are enabled by `plumeria.configs.recommended` with the
severity shown in [Recommended configuration](#recommended-configuration).

### custom-props-require-import

Require the `@plumeria/core` import in a file that passes a `css.create` style to a
component through a prop other than the styling prop. The compiler cannot tell that
such a prop carries a style, so it compiles the file only when the file imports
`@plumeria/core`. A file that writes the styling prop itself is compiled without
the import.

Accepts `{ styleProp }`; see [Configuring the styling prop](#configuring-the-styling-prop).

### expand-border-shorthands

Expands a border shorthand that bundles a width, a style and a color —
`border`, `borderBlock`, `borderInline`, and the eight edge forms — into the
three declarations it stands for. Those bundles are the only properties left
that cross an axis shorthand without either containing the other, so expanding
them turns the last unrankable pairs into ordinary shorthand-to-longhand ones.

Fixable. A value it cannot split, such as `var(--edge)` or `inherit`, is
reported without a fix: leaving it silent would let the expanded declarations
elsewhere outrank it.

```js
borderTop: '1px solid red'
// becomes
borderTopWidth: '1px', borderTopStyle: 'solid', borderTopColor: 'red'
```

A shorthand resets what it omits, so `borderBlock: 'solid'` expands with
`medium` and `currentcolor` written out.

### no-combinator

Disallow combinators `>`, `+`, `~` and descendant combinator (space) unless inside functional pseudo-classes.

### no-destructure

Disallow destructuring APIs.

### no-inline-object

Disallow passing inline object to `classStyle` and `css.use()`. Only compiled styles from `css.create()` are allowed.

### no-inner-call

Disallow calling APIs inside functions.

### no-invalid-selector

Disallow invalid selector inside `css.create()` and `css.keyframes()` and `css.viewTransitionClass()`.  
`create()` example: (Pseudo -> Query, Query -> Query)  
Also reports a functional pseudo-class nested inside one of the same name, such as `:where(:where(.a), .b)`, and flattens it with a fix when the nested call is a whole argument. `:not()` may nest inside `:not()`. Reports a key nested deeper than 16 levels or holding a stray quote.

### no-mixed-styling-props

Disallow mixing the styling prop with `className` or `style`. `classStyle` can handle both `className` and `style`.

Accepts `{ styleProp }`; see [Configuring the styling prop](#configuring-the-styling-prop).

### no-order-dependent-overlap

Warns when two properties in one style overlap but neither outranks the other,
so the one written last wins. It covers one property under its logical and its
physical name, and two shorthands that cross without either containing the
other. A shorthand and its longhand are ranked by specificity and never
reported.

It also reports two conditions in one style where one provably matches a subset
of the other — `(min-width: 900px)` inside `(min-width: 600px)`, or two queries
naming the same container — and the narrower one is written first. The optimizer
places the narrower one last whatever the source says, so the declarations read
the wrong way round. The swap is offered as a suggestion rather than a fix.

A pair of spellings is one property in a horizontal writing mode and two
properties in a vertical one, which is per element and cannot be read from the
source. The rule reads them as one property; disable the line where an element
is known to be vertical.

### no-style-prop-relay

Reports a style prop that a component receives but only passes on. Apply it to the styling prop or `css.use()` on an element the component renders.

Accepts `{ styleProp }`; see [Configuring the styling prop](#configuring-the-styling-prop).

### no-unknown-css-properties

Disallow unknown CSS properties in camelCase within `css.create`, `css.keyframes`, `css.viewTransitionClass`, and `css.positionTry`. Inside `css.positionTry`, it also reports properties that `@position-try` does not accept.

In `css.create`, a key that holds an object is reported with a hint that nested selectors start with `:`, `[` or `@`.

### no-unresolved-composition

A safety net for `css.use()`. Warns when its result is combined with other class names or passed to another function, or when a function key is passed to it. Pass every style to one `css.use()` call, and apply function keys to the styling prop.

Accepts `{ styleProp }`; see [Configuring the styling prop](#configuring-the-styling-prop).

### no-unused-keys

Warns when object keys are defined but not used, mainly in component files.

### sort-properties

Automatically sorts CSS properties in the recommended order for consistency and maintainability.

### format-properties

Automatically format for consistency and maintainability.

- Formats a line into a multi-line.
- Formats by filling in blank lines.

### validate-values

Validates CSS property values for correctness. Only standard CSS properties are checked; properties with string literal keys (e.g., computed or dynamic property names) are not validated.

### validate-pseudos

Validates CSS pseudo-classes and pseudo-elements inside `css.create()`. It checks for typos and structural correctness and supports validation of computed keys when TypeScript is available.

### validate-at-rules

Validates at-rules inside `css.create()`. It accepts `@media`, `@container`, and `@supports`, each with a prelude, whether or not a space separates it from the keyword, and `@starting-style` on its own, and supports validation of computed keys when TypeScript is available.

## Optional rules

These rules are not enabled by `plumeria.configs.recommended`. Enable the
spelling policy that fits the project:

```js
export default [
  plumeria.configs.recommended,
  {
    rules: {
      '@plumeria/no-logical-properties': 'warn',
    },
  },
];
```

### no-physical-properties / no-logical-properties

Disallow one of the two names a property can carry, so a project writes edges
under one spelling only. `no-physical-properties` reports `paddingLeft` and
suggests `paddingInlineStart`; `no-logical-properties` reports the reverse.
Accepts `{ sizes }`, which adds `width`, `height`, their `min`/`max` forms,
`overflow-x` and `overflow-y` to the properties covered. It is `false` by
default: a size is one property under either spelling, while an edge is what a
direction reverses.

Turning both rules on is contradictory. Reach for one when you want the pairs
`no-order-dependent-overlap` reports to be impossible to write: a property that
appears under one spelling only can never meet its other spelling on an element.
A shorthand with no single counterpart, such as `borderBlockWidth`, is outside
either rule and stays reported.

The bundler plugins turn the matching rule on in the build lint as well:
`withoutPhysicalProperties` runs `no-physical-properties`, and
`withoutLogicalProperties` runs `no-logical-properties`, `{ sizes }` included.

## CLI (plumeria-lint)

This package provides a CLI command, `plumeria-lint`, as a convenient way
to run Plumeria's custom ESLint rules.

It runs `oxlint`, which this package depends on, for fast linting with code
snippets in output, so oxlint needs no separate install.

### Installation

```bash
npm install -D @plumeria/eslint-plugin
# or
pnpm add -D @plumeria/eslint-plugin
```

### Usage

```bash
plumeria-lint
```

The process exits with a non-zero status code if any errors or warnings are found,
making it suitable for use in CI and build pipelines.

### Styling prop

If the styling prop is renamed, pass the name with `--style-prop`. Without the
flag, `plumeria-lint` asks ESLint for `settings.plumeria.styleProp` when ESLint
and a flat config are installed, and uses `classStyle` otherwise:

```bash
plumeria-lint --style-prop sx
```

### Aborting Builds on Lint Errors (Parallel Pipeline)

You can run `plumeria-lint` in parallel with your build command (e.g. `next build` or `vite build`) using the `--` separator:

```json
{
  "scripts": {
    "build": "plumeria-lint -- next build"
  }
}
```

If `plumeria-lint` detects any styling errors or warnings, it will print the diagnostics, kill the build process immediately, and exit with a non-zero code. This avoids compiling when styling validation fails.

With `@plumeria/next-plugin` and `@plumeria/unplugin` (except on esbuild and Bun), this lint is integrated into the build by default, so it needs no setup. Pass `lint: false` to the plugin to build without it.

---
_Source: https://npm.io/package/@plumeria/eslint-plugin · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
