# Optional rules

Source: https://plumeria.dev/docs/api-reference/plugins/eslint-plugin/optional-rules



`recommended` reports defects. These three do something else: they take away the
room for one, at the cost of narrowing how you are allowed to write. None of
them is on by default.

They are turned on in an ESLint config only: `plumerialint` runs a fixed oxlint
config that does not list them, and a rule that config leaves out cannot be
turned on from the command line.

## What they are for [#what-they-are-for]

Specificity settles every pair where one property contains another — a shorthand
and its longhand, an axis and an edge. What it cannot settle is a pair where
neither side contains the other, and there are exactly two shapes of those:

|                              | Example                              | Left to                    |
| :--------------------------- | :----------------------------------- | :------------------------- |
| One property under two names | `paddingTop` and `paddingBlockStart` | the order they are written |
| Two shorthands that cross    | `borderTop` and `borderBlockWidth`   | the order they are written |

`no-order-dependent-overlap`, which `recommended` turns on, warns when such a
pair meets on one element. The rules below stop the pair from existing.

It also warns on two states that can match at once and set the same property —
`:hover` against `:focus`, or `:hover::before` against `:focus::before` on one
pseudo-element. That pair has no property shape to take away, so the rules below
do not reach it. Composing the two styles settles it instead: the atom from the
source written further right receives one more `:not(#\#)`, so the right one wins
where both states hold, in either module order. The warning is for the case where
both are written in one style, where there is no composition to read. See
[Specificity](/docs/specificity).

## Recommended for a large codebase [#recommended-for-a-large-codebase]

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

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

Eighty-six pairs exist that nothing ranks: 30 are crossings, every one
containing a border bundle, and 56 are the same property under two names.
`expand-border-shorthands` removes the 30, a spelling rule — either direction —
removes the 56 (the twelve axis pairs with `{ sizes: true }`), so running both
leaves zero. Both are fixable or offer a rename, so adopting them on an
existing codebase is mostly a matter of running the fixer once and reading the
leftovers.

## expand-border-shorthands [#expand-border-shorthands]

`border`, `borderBlock`, `borderInline` and the eight edge forms each set a
width, a style and a color at once. Every pair that specificity cannot rank
contains one of those eleven, so writing out the three declarations they stand
for is what removes the shape.

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

`borderTop` and `borderBlockWidth` cross: the first sets three values on one
edge, the second sets one value on two edges, and they meet on
`border-top-width` without either containing the other. `borderTopWidth` and
`borderBlockWidth` do not cross — the first is contained by the second, and the
depth of each decides the winner.

A shorthand resets whatever it omits, so the expansion writes the initial value
of every component you left out:

```js
borderBlock: 'solid'
// fixes to
borderBlockWidth: 'medium',
borderBlockStyle: 'solid',
borderBlockColor: 'currentColor'
```

A `var()` or an interpolated variable that stands as one token takes the single
component the other tokens leave open:

```js
borderLeft: `1px solid ${edge}`
// fixes to
borderLeftWidth: '1px',
borderLeftStyle: 'solid',
borderLeftColor: edge
```

A value the rule cannot split — `var(--edge)` on its own, `inherit`, a
reference — is reported without a fix. Write the three declarations yourself,
or the ones the fixer expanded elsewhere will outrank it.

## no-physical-properties / no-logical-properties [#no-physical-properties--no-logical-properties]

A property that carries both a logical and a physical name can be written twice
under two spellings. Pick one spelling and the pair cannot occur.

```js
'@plumeria/no-physical-properties': 'warn'
// paddingLeft → rename to paddingInlineStart

'@plumeria/no-logical-properties': 'warn'
// paddingInlineStart → rename to paddingLeft
```

Turning both on is contradictory — pick one direction. Either one removes the
pair. `no-logical-properties` fits a codebase written in the physical names;
`no-physical-properties` is the direction that keeps a document working in
another writing mode or another direction, which is what the logical names
exist for.

Each accepts `{ sizes }`, `false` by default:

```js
'@plumeria/no-physical-properties': ['warn', { sizes: true }]
```

Off, the rule covers the edges — `paddingLeft`, `marginTop`, `top`,
`borderTopColor`. On, it also covers `width`, `height`, their `min` and `max`
forms, and `overflow`, `overscroll-behavior` and `contain-intrinsic-size` on
both axes. An edge is what a direction reverses; a size is the same box either
way, so renaming it buys less and touches far more code.

Renaming is offered as a suggestion rather than a fix. In a vertical writing
mode the two spellings are two different edges, so the rewrite is a change of
meaning that you should apply deliberately.

### Holding the count at zero [#holding-the-count-at-zero]

A rule reports; it does not guarantee. It can be set to `'off'` for one
developer, and it does nothing where no linter runs. Once the 56 pairs are gone,
the bundler can keep them gone. `withoutLogicalProperties` and
`withoutPhysicalProperties` reject the same spelling at build time and take the
same `{ sizes }` option, on
[Next.js](/docs/api-reference/plugins/next-plugin#withoutlogicalproperties-and-withoutphysicalproperties)
and on
[every other bundler](/docs/api-reference/plugins/unplugin#withoutlogicalproperties-and-withoutphysicalproperties).
They do not depend on each developer running ESLint, and they apply to every
file the compiler processes — which `include` and `exclude` decide, so a file
outside that set is not compiled and not checked.

They are the same choice made at a different level, so run the rule first. It
reports every occurrence at once and offers the rename, which is what moving an
existing codebase needs; the build option is for afterwards, and its direction
has to match the rule you kept. Turning both build options on at once is a
configuration error, the same contradiction as running both rules.

## What is left [#what-is-left]

A shorthand with no single counterpart, such as `borderBlockWidth`, belongs to
neither spelling and is never renamed. With `expand-border-shorthands` on, it no
longer has anything to cross with.

Two conditions that can match at the same time are settled by the optimizer
where one provably matches a subset of the other: the narrower one is placed
last. No depth can rank them, because whether one condition implies another is
not decidable in general, so a pair that overlaps without either containing the
other is left to source order. `no-order-dependent-overlap` reports a nested
pair written in the losing order, which the stylesheet corrects but the source
still reads the wrong way round.
