@tenphi/eslint-plugin-tasty 1.18.0 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -45
- package/docs/rules/consistent-token-usage.md +72 -0
- package/docs/rules/known-property.md +55 -0
- package/docs/rules/no-classname-prop.md +60 -0
- package/docs/rules/no-ignored-pseudo-element-state.md +97 -0
- package/docs/rules/no-important.md +25 -0
- package/docs/rules/no-nested-selector.md +39 -0
- package/docs/rules/no-nested-state-map.md +33 -0
- package/docs/rules/no-own-at-root.md +57 -0
- package/docs/rules/no-protected-restyle.md +192 -0
- package/docs/rules/no-raw-color-values.md +49 -0
- package/docs/rules/no-raw-motion-duration.md +50 -0
- package/docs/rules/no-raw-transition-duration.md +56 -0
- package/docs/rules/no-runtime-styles-mutation.md +58 -0
- package/docs/rules/no-state-in-selector.md +142 -0
- package/docs/rules/no-style-prop.md +127 -0
- package/docs/rules/no-style-spread.md +53 -0
- package/docs/rules/no-styles-prop.md +57 -0
- package/docs/rules/no-unknown-state-alias.md +45 -0
- package/docs/rules/prefer-auto-calc.md +30 -0
- package/docs/rules/prefer-custom-property-syntax.md +56 -0
- package/docs/rules/prefer-directional-shorthand.md +46 -0
- package/docs/rules/prefer-element-selector.md +77 -0
- package/docs/rules/prefer-fill-mix.md +119 -0
- package/docs/rules/prefer-hide.md +31 -0
- package/docs/rules/prefer-longhand-property.md +25 -0
- package/docs/rules/prefer-shorthand-property.md +194 -0
- package/docs/rules/prefer-state-negation.md +44 -0
- package/docs/rules/require-default-state.md +50 -0
- package/docs/rules/static-no-dynamic-values.md +32 -0
- package/docs/rules/static-valid-selector.md +36 -0
- package/docs/rules/valid-boolean-property.md +39 -0
- package/docs/rules/valid-color-token.md +64 -0
- package/docs/rules/valid-custom-property.md +36 -0
- package/docs/rules/valid-custom-unit.md +34 -0
- package/docs/rules/valid-default-state-order.md +44 -0
- package/docs/rules/valid-directional-modifier.md +46 -0
- package/docs/rules/valid-preset.md +35 -0
- package/docs/rules/valid-radius-shape.md +31 -0
- package/docs/rules/valid-recipe.md +35 -0
- package/docs/rules/valid-state-definition.md +46 -0
- package/docs/rules/valid-state-key.md +62 -0
- package/docs/rules/valid-styles-structure.md +48 -0
- package/docs/rules/valid-sub-element.md +100 -0
- package/docs/rules/valid-transition.md +46 -0
- package/docs/rules/valid-value.md +65 -0
- package/harness/dev.harnesspm/hooks.json +12 -0
- package/harness/dev.harnesspm/rules/usage.md +11 -0
- package/harness/dev.harnesspm/scripts/check-edit.mjs +127 -0
- package/harness/dev.harnesspm/scripts/check.mjs +159 -0
- package/harness/dev.harnesspm/scripts/lint.mjs +236 -0
- package/harness/plugin.json +23 -0
- package/package.json +11 -7
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# tasty/no-style-spread
|
|
2
|
+
|
|
3
|
+
Reports object and array spreads inside runtime Tasty style objects, and recommends `mergeStyles(...)` when a style object is built from two or more spreads.
|
|
4
|
+
|
|
5
|
+
**Presets:** warning in recommended and strict. **Fix:** none.
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
A spread hides which properties a style object defines and makes overrides hard to review. Object spread is also shallow: spreading `Label: { fill: '#accent' }` over `Label: { padding: '2x', fill: '#surface' }` loses `padding`. `mergeStyles` merges sub-elements and state maps the way Tasty does.
|
|
10
|
+
|
|
11
|
+
## Examples
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
// ❌ Use mergeStyles(...) to compose Tasty style objects.
|
|
15
|
+
const finalStyles: Styles = { ...outerStyles, ...styles };
|
|
16
|
+
|
|
17
|
+
// ✅ Tasty's merge, in the same override order
|
|
18
|
+
const mergedStyles: Styles = mergeStyles(outerStyles, styles);
|
|
19
|
+
|
|
20
|
+
// ❌ List Tasty style properties explicitly instead of spreading.
|
|
21
|
+
const Card = tasty({
|
|
22
|
+
styles: {
|
|
23
|
+
...sharedStyles,
|
|
24
|
+
fill: { '': '#surface', active: '#primary' },
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## What is reported
|
|
30
|
+
|
|
31
|
+
- **Two or more spreads at the root of a style object** get one `mergeStyles` warning, at the first spread. This includes typed and shared objects such as `const finalStyles: Styles = { ...outerStyles, ...styles }`, call-site styles, variants, JSX and Storybook styles, and configured helpers.
|
|
32
|
+
- **Every other spread** in a runtime style object, including state maps and sub-elements, gets a plain warning.
|
|
33
|
+
|
|
34
|
+
There is no autofix: `mergeStyles` changes override behavior. A state map without `''` extends the inherited entries, while a map with `''` replaces them.
|
|
35
|
+
|
|
36
|
+
`tastyStatic()` spreads stay errors under [`tasty/static-no-dynamic-values`](static-no-dynamic-values.md): the extractor rejects them, and a disable comment does not make them valid. To fail CI on warnings that nobody suppressed, run ESLint with `--max-warnings=0`.
|
|
37
|
+
|
|
38
|
+
## Exceptions
|
|
39
|
+
|
|
40
|
+
An intentional spread takes a disable comment on its line, with a reason:
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
const Card = tasty({
|
|
44
|
+
styles: {
|
|
45
|
+
// eslint-disable-next-line tasty/no-style-spread -- generated defaults are shared by all cards
|
|
46
|
+
...sharedStyles,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Options
|
|
52
|
+
|
|
53
|
+
None.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# tasty/no-styles-prop
|
|
2
|
+
|
|
3
|
+
Warn about instance `styles` props on recognized Tasty components.
|
|
4
|
+
|
|
5
|
+
**Presets:** warning in recommended and strict. **Fix:** none.
|
|
6
|
+
|
|
7
|
+
Choose the replacement according to the override:
|
|
8
|
+
|
|
9
|
+
- Dynamic values: declare token references and pass `tokens`.
|
|
10
|
+
- State changes: define state maps and pass `mods` or exposed modifier props.
|
|
11
|
+
- Supported customization: use exposed style props or variants.
|
|
12
|
+
- Structural or reusable overrides: create a `tasty(Component, { styles })` wrapper.
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { tasty } from '@tenphi/tasty';
|
|
16
|
+
|
|
17
|
+
const Box = tasty({ styles: { padding: '1x' } });
|
|
18
|
+
|
|
19
|
+
// ❌ Prefer a named, reusable extension.
|
|
20
|
+
<Box styles={{ padding: '2x' }} />;
|
|
21
|
+
|
|
22
|
+
// ✅ Preferred.
|
|
23
|
+
const RoomyBox = tasty(Box, { styles: { padding: '2x' } });
|
|
24
|
+
<RoomyBox />;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Variables, function results, conditional values, TypeScript wrappers, null, and
|
|
28
|
+
visible `styles` keys in inline JSX object spreads are checked too. The rule is
|
|
29
|
+
report-only: choosing a token, state, variant, or wrapper requires the component's
|
|
30
|
+
contract.
|
|
31
|
+
|
|
32
|
+
An edge case may need an instance style object, for example an adapter that must
|
|
33
|
+
pass through externally supplied overrides. Keep the override and acknowledge
|
|
34
|
+
the exception with an explicit local ignore and a reason:
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
const adapted = (
|
|
38
|
+
// eslint-disable-next-line tasty/no-styles-prop -- legacy adapter requires instance overrides
|
|
39
|
+
<Box styles={adapterOverrides} />
|
|
40
|
+
);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Place the directive immediately before the reported attribute line. It suppresses
|
|
44
|
+
only this rule for that line. The warning message includes this exception path;
|
|
45
|
+
the plugin does not silently exempt edge cases or enforce comment reasons.
|
|
46
|
+
|
|
47
|
+
Recognition uses local `const` Tasty/configured-options factory results, their
|
|
48
|
+
`const` aliases/sub-elements, and imports from `importSources`. Exports of relative modules (`import { Panel } from './styled'`) count when they
|
|
49
|
+
resolve to one of these, through re-exports too; following them needs the
|
|
50
|
+
`typescript` package. Native elements,
|
|
51
|
+
unrelated components, type-only imports, shadowed bindings, mutable aliases, and
|
|
52
|
+
opaque spread bindings are excluded. Factory definition options (`tasty({ styles })`
|
|
53
|
+
or `tasty(Component, { styles })`) are allowed.
|
|
54
|
+
|
|
55
|
+
## Options
|
|
56
|
+
|
|
57
|
+
None.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# tasty/no-unknown-state-alias
|
|
2
|
+
|
|
3
|
+
Reports an `@name` state alias that is not in your config, not defined locally and not built in, wherever it appears in a state key.
|
|
4
|
+
|
|
5
|
+
**Presets:** warning in strict. **Fix:** none.
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
Tasty cannot expand an alias it does not know, so the state never applies. A typo such as `@drak` fails silently.
|
|
10
|
+
|
|
11
|
+
## Examples
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
// ❌ Unknown state alias '@drak'. Add it to 'states' in your tasty config, or define it locally as a '@name' key.
|
|
15
|
+
padding: { '': '4x', '@drak': '2x' }
|
|
16
|
+
|
|
17
|
+
// ❌ The same alias inside a compound key
|
|
18
|
+
padding: { '': '4x', 'hovered & @drak': '2x' }
|
|
19
|
+
|
|
20
|
+
// ✅ A local alias
|
|
21
|
+
tasty({
|
|
22
|
+
styles: {
|
|
23
|
+
'@compact': 'size=small',
|
|
24
|
+
padding: { '': '4x', '@compact': '2x' },
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Checks
|
|
30
|
+
|
|
31
|
+
Compound keys are parsed, so each alias is checked wherever it appears: `'@dark | hovered'`, `'hovered & @dark'`, `'(@dark | hovered)'`, `'@root(@dark)'`.
|
|
32
|
+
|
|
33
|
+
An alias is known when it is:
|
|
34
|
+
|
|
35
|
+
- listed in `states` in `tasty.config.*`;
|
|
36
|
+
- a local alias: an `@name: '<state>'` key of the object, or of an enclosing sub-element up to the root. A `mergeStyles(...)` argument stands for the merged object, so aliases from every object argument count, and a `mergeStyles` call used as a sub-element value also sees the styles around it;
|
|
37
|
+
- built in: `@media`, `@root`, `@parent`, `@own`, `@supports`, `@starting`, and the `@(` container query syntax.
|
|
38
|
+
|
|
39
|
+
Without `states` in the config and no local aliases in view, the rule does nothing.
|
|
40
|
+
|
|
41
|
+
[`tasty/valid-state-key`](valid-state-key.md), in the recommended preset, reports an unknown alias too when `states` is set.
|
|
42
|
+
|
|
43
|
+
## Options
|
|
44
|
+
|
|
45
|
+
None.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# tasty/prefer-auto-calc
|
|
2
|
+
|
|
3
|
+
Reports `calc(...)` in a style value. Tasty adds `calc()` to a parenthesized expression by itself.
|
|
4
|
+
|
|
5
|
+
**Presets:** warning in recommended and strict. **Fix:** autofix.
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
In Tasty, a parenthesized expression such as `(100% - 2x)` is auto-calc: Tasty renders it as `calc()`. Writing `calc(...)` yourself is longer and mixes two spellings for one thing.
|
|
10
|
+
|
|
11
|
+
## Examples
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
// ❌ Replace 'calc(100% - 2x)' with Tasty auto-calc '(100% - 2x)'.
|
|
15
|
+
width: 'calc(100% - 2x)'
|
|
16
|
+
|
|
17
|
+
// ✅ After the autofix
|
|
18
|
+
width: '(100% - 2x)'
|
|
19
|
+
|
|
20
|
+
// ❌ Also inside a state map
|
|
21
|
+
margin: { '': '0', hovered: 'calc(1x + 2x)' }
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Where it applies
|
|
25
|
+
|
|
26
|
+
Values in Tasty style objects, and style props of components known to be Tasty components (a local `tasty()` result or a component from your `importSources`). It skips the values of `$name` and `#name` keys, which define tokens.
|
|
27
|
+
|
|
28
|
+
## Options
|
|
29
|
+
|
|
30
|
+
None.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# tasty/prefer-custom-property-syntax
|
|
2
|
+
|
|
3
|
+
Reports raw CSS where Tasty has a token: `var(--name)`, a `$name-color` reference, `transparent` and `currentColor`.
|
|
4
|
+
|
|
5
|
+
**Presets:** warning in recommended and strict. **Fix:** autofix.
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
Tasty's token syntax is shorter and checked: [`tasty/valid-color-token`](valid-color-token.md) and [`tasty/valid-custom-property`](valid-custom-property.md) catch a misspelled `#name` or `$name`, but not a misspelled `var(--name)`.
|
|
10
|
+
|
|
11
|
+
## Examples
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
// ❌ Use Tasty token syntax '$gap' instead of 'var(--gap)'.
|
|
15
|
+
padding: 'var(--gap)'
|
|
16
|
+
|
|
17
|
+
// ❌ Use Tasty token syntax '#accent' instead of 'var(--accent-color)'.
|
|
18
|
+
fill: 'var(--accent-color)'
|
|
19
|
+
|
|
20
|
+
// ❌ Use color token '#text' instead of custom property reference '$text-color'.
|
|
21
|
+
color: '$text-color'
|
|
22
|
+
|
|
23
|
+
// ❌ Use the '#clear' token instead of the 'transparent' keyword.
|
|
24
|
+
border: '1bw solid transparent'
|
|
25
|
+
|
|
26
|
+
// ❌ Use the '#current' token instead of the 'currentColor' keyword.
|
|
27
|
+
outline: 'currentColor'
|
|
28
|
+
|
|
29
|
+
// ✅ After the autofix
|
|
30
|
+
padding: '$gap'
|
|
31
|
+
fill: '#accent'
|
|
32
|
+
color: '#text'
|
|
33
|
+
border: '1bw solid #clear'
|
|
34
|
+
outline: '#current'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Autofix
|
|
38
|
+
|
|
39
|
+
| Before | After |
|
|
40
|
+
| --- | --- |
|
|
41
|
+
| `var(--name)` | `$name` |
|
|
42
|
+
| `var(--name-color)` | `#name` |
|
|
43
|
+
| `var(--name, fallback)` | `($name, fallback)` |
|
|
44
|
+
| `$name-color`, `$name-color.5` | `#name`, `#name.5` |
|
|
45
|
+
| `transparent` | `#clear` |
|
|
46
|
+
| `currentColor` | `#current` |
|
|
47
|
+
|
|
48
|
+
Every fix renders the same CSS. Tasty expands `#name` only in properties that take colors, so the rule stays quiet where a color token would not expand, such as `var(--accent-color)` in `fontSize`.
|
|
49
|
+
|
|
50
|
+
## Where it applies
|
|
51
|
+
|
|
52
|
+
Values in Tasty style objects, and style props of components known to be Tasty components. It skips the values of `$name` and `#name` keys, which define tokens.
|
|
53
|
+
|
|
54
|
+
## Options
|
|
55
|
+
|
|
56
|
+
None.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# tasty/prefer-directional-shorthand
|
|
2
|
+
|
|
3
|
+
Reports a four-value box, such as `margin: '0 0 1x 0'`, that sets only part of the box. Tasty's directional form, `'1x bottom'`, says the same thing.
|
|
4
|
+
|
|
5
|
+
**Presets:** warning in recommended and strict. **Fix:** autofix, and an editor suggestion for `border`.
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
In a four-value box, the reader has to count positions to see which side is set. The directional form names it.
|
|
10
|
+
|
|
11
|
+
## Examples
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
// ❌ 'margin' value '0 0 1x 0' only sets part of the box. Use '1x bottom' instead.
|
|
15
|
+
margin: '0 0 1x 0'
|
|
16
|
+
|
|
17
|
+
// ✅ After the autofix
|
|
18
|
+
margin: '1x bottom'
|
|
19
|
+
|
|
20
|
+
// ❌ 'inset' value 'auto 0 0 0' only sets part of the box. Use '0 bottom dock' instead.
|
|
21
|
+
inset: 'auto 0 0 0'
|
|
22
|
+
|
|
23
|
+
// ✅ After the autofix
|
|
24
|
+
inset: '0 bottom dock'
|
|
25
|
+
|
|
26
|
+
// ❌ 'border' value '0 0 1bw 0' does not do what it looks like. Suggestion: '1bw bottom'
|
|
27
|
+
border: '0 0 1bw 0'
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Checks
|
|
31
|
+
|
|
32
|
+
The rule reads `margin`, `padding`, `inset`, `radius`, `fade` and `border` values with exactly four parts, plain or in a state map. A part is a placeholder when it is `0` (or `auto` for `inset`).
|
|
33
|
+
|
|
34
|
+
| Four-value box | Directional form |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| One real value, such as `'0 0 0 2x'` | `'2x left'` |
|
|
37
|
+
| `radius` with one real corner, such as `'0 1r 0 0'` | `'1r top-right'` |
|
|
38
|
+
| `inset` with three equal values and one `auto`, such as `'0 0 0 auto'` | `'0 right dock'` |
|
|
39
|
+
|
|
40
|
+
The value is always carried into the fix, never replaced by the property default: `margin` defaults to `var(--gap)`, not `1x`, so `'0 0 1x 0'` must not become `'bottom'`.
|
|
41
|
+
|
|
42
|
+
For `border`, Tasty reads four parts as one border value, so `'0 0 1bw 0'` draws no border at all today. The directional form is what the author meant, but it changes what renders, so it is an editor suggestion, not an autofix.
|
|
43
|
+
|
|
44
|
+
## Options
|
|
45
|
+
|
|
46
|
+
None.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# tasty/prefer-element-selector
|
|
2
|
+
|
|
3
|
+
Prefer Tasty element names over explicit `data-element` attributes in sub-element
|
|
4
|
+
`$` selectors. The autofix applies when the shorter form preserves the generated selector.
|
|
5
|
+
|
|
6
|
+
**Presets:** warning in recommended and strict. **Fix:** autofix.
|
|
7
|
+
|
|
8
|
+
```js
|
|
9
|
+
// ❌ Before
|
|
10
|
+
const Component = tasty({
|
|
11
|
+
styles: {
|
|
12
|
+
Search: {
|
|
13
|
+
$: '[data-element="Primary"] > [data-element="Search"]',
|
|
14
|
+
display: 'block',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// ✅ After autofix
|
|
20
|
+
const Component = tasty({
|
|
21
|
+
styles: {
|
|
22
|
+
Search: {
|
|
23
|
+
$: 'Primary > Search',
|
|
24
|
+
display: 'block',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The rule reports one warning per eligible `$` value and shortens all safe
|
|
31
|
+
attributes in that value. It accepts exact, unnamespaced `data-element` equality
|
|
32
|
+
selectors whose names match `[A-Z][a-zA-Z0-9]*`, including single-quoted,
|
|
33
|
+
double-quoted and unquoted CSS values. Child and sibling combinators, selector
|
|
34
|
+
lists and explicit `&` prefixes are supported.
|
|
35
|
+
|
|
36
|
+
## Preserving the target
|
|
37
|
+
|
|
38
|
+
Tasty can append the owning sub-element after a different trailing element name.
|
|
39
|
+
For example, in an `Other` sub-element, the rule fixes:
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
Other: {
|
|
43
|
+
// Before: '[data-element="Primary"] > [data-element="Search"]'
|
|
44
|
+
$: 'Primary > [data-element="Search"]',
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The final attribute remains explicit because `$: 'Primary > Search'` in that
|
|
49
|
+
scope would select `Search`'s `Other` descendants instead of `Search` itself.
|
|
50
|
+
|
|
51
|
+
Element names also insert a separator. Attributes inside compounds, including
|
|
52
|
+
whitespace that the affix renderer ignores, remain explicit. For example,
|
|
53
|
+
`'[data-element="Primary"][data-element="Search"]'` becomes
|
|
54
|
+
`'Primary[data-element="Search"]'`, preserving the same-element requirement.
|
|
55
|
+
|
|
56
|
+
## Scope and limits
|
|
57
|
+
|
|
58
|
+
This rule checks literal sub-element `$` values and templates without
|
|
59
|
+
interpolation. It supports the plugin's existing Tasty style contexts, including
|
|
60
|
+
typed/shared styles, JSX/Storybook styles, nested sub-elements and configured
|
|
61
|
+
`styleFunctions`. TypeScript wrappers and statically computed keys are supported.
|
|
62
|
+
Root `$` values, global selector arguments and unrelated objects are outside its
|
|
63
|
+
scope.
|
|
64
|
+
|
|
65
|
+
Attributes with CSS comparison flags, namespaces, non-equality operators or
|
|
66
|
+
unsupported names remain explicit. Branches containing pseudo selectors or
|
|
67
|
+
other unsupported syntax are left alone. Selectors containing comments or CSS
|
|
68
|
+
escapes are also left alone. These are conservative rewrite limits, not syntax
|
|
69
|
+
errors. Existing state-selector rules still apply independently.
|
|
70
|
+
|
|
71
|
+
If the JavaScript source uses escapes (for example, escaped quotes), the warning
|
|
72
|
+
can still appear, but no autofix is offered: source offsets may differ from the
|
|
73
|
+
string's actual value.
|
|
74
|
+
|
|
75
|
+
## Options
|
|
76
|
+
|
|
77
|
+
None.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# tasty/prefer-fill-mix
|
|
2
|
+
|
|
3
|
+
Replace deprecated two-color fills with compound colors, and catch mix parts
|
|
4
|
+
that are weighted by their opacity.
|
|
5
|
+
|
|
6
|
+
**Presets:** warning in recommended and strict. **Fix:** autofix and editor suggestions.
|
|
7
|
+
|
|
8
|
+
Tasty 3.16 layers color tokens with `+`: `'#surface+#dark.04'` paints `#dark` at
|
|
9
|
+
4% over `#surface` as one color,
|
|
10
|
+
`color-mix(in srgb, var(--dark-color) 4%, var(--surface-color))`, in any color
|
|
11
|
+
slot. It replaces both older ways to put two colors in a `fill` (or
|
|
12
|
+
`backgroundColor`):
|
|
13
|
+
|
|
14
|
+
- `'#surface #dark.04'` paints the second color as a `background-image` layer,
|
|
15
|
+
which contrast checkers such as axe-core cannot see through. Deprecated in
|
|
16
|
+
Tasty 3.15.
|
|
17
|
+
- `'#dark 4%, #surface'` mixes the colors as the arguments of `color-mix()`. It
|
|
18
|
+
is the same color as the compound, but only in `fill`. Deprecated in Tasty
|
|
19
|
+
3.17.
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
// ❌ Before
|
|
23
|
+
const Item = tasty({
|
|
24
|
+
styles: {
|
|
25
|
+
fill: {
|
|
26
|
+
'': '#surface #clear',
|
|
27
|
+
hovered: '#surface #dark.04',
|
|
28
|
+
pressed: '#dark 8%, #surface',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// ✅ After autofix
|
|
34
|
+
const Item = tasty({
|
|
35
|
+
styles: {
|
|
36
|
+
fill: {
|
|
37
|
+
'': '#surface',
|
|
38
|
+
hovered: '#surface+#dark.04',
|
|
39
|
+
pressed: '#surface+#dark.08',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The rule's name predates compound colors: it covers every two-color fill.
|
|
46
|
+
|
|
47
|
+
## Autofixes
|
|
48
|
+
|
|
49
|
+
| Before | After |
|
|
50
|
+
|--------|-------|
|
|
51
|
+
| `'#base #tok.N'` | `'#base+#tok.N'` |
|
|
52
|
+
| `'#base #clear'`, `'#base #tok.0'` | `'#base'` |
|
|
53
|
+
| `'#tok N%, #base'`, `'#base, #tok N%'` | `'#base+#tok.N'` |
|
|
54
|
+
| `'#tok ($prop * 100%), #base'` | `'#base+#tok.$prop'` |
|
|
55
|
+
| `'#tok 0%, #base'` / `'#tok 100%, #base'` | `'#base'` / `'#tok'` |
|
|
56
|
+
| any other two-color mix | `'color-mix(in srgb, <the same parts>)'` |
|
|
57
|
+
|
|
58
|
+
`#base` can be faded, or a compound itself: `'#accent 10%, #surface+#dark.04'`
|
|
59
|
+
becomes `'#surface+#dark.04+#accent.1'`. A weight becomes the opacity the way
|
|
60
|
+
Tasty reads it, as a decimal fraction: `4%` is `.04`, `4.5%` is `.045`, `30%` is
|
|
61
|
+
`.3`. A base a compound cannot take, such as `rgb(…)`, is mixed in `color-mix()`
|
|
62
|
+
itself.
|
|
63
|
+
|
|
64
|
+
Every comma-mix fix is the same color as the mix: a compound compiles to the
|
|
65
|
+
same `color-mix()`. That includes a weight from a custom property, such as
|
|
66
|
+
`'#tok $weight, #base'`, which becomes
|
|
67
|
+
`'color-mix(in srgb, #tok $weight, #base)'`: a compound's `$prop` opacity has to
|
|
68
|
+
hold a number (`.04`), not a percentage (`4%`), so to use one, change what the
|
|
69
|
+
property holds and write `'#base+#tok.$alpha'`. A weighted `#clear` stays a
|
|
70
|
+
`color-mix()` too: a compound leaves a transparent layer out, while the mix lets
|
|
71
|
+
it thin out the other color.
|
|
72
|
+
|
|
73
|
+
A space-separated fix renders the same color as the layer when the overlay token
|
|
74
|
+
holds an opaque color. Drawing an opaque color at alpha `a` over the base is the
|
|
75
|
+
compound, also when the base is translucent. If `#tok` itself holds a
|
|
76
|
+
translucent color, the compound differs from the layer; give such a token an
|
|
77
|
+
opaque color and put its alpha in the suffix.
|
|
78
|
+
|
|
79
|
+
## Reported without a fix
|
|
80
|
+
|
|
81
|
+
When the overlay's alpha is not written on it, the rule cannot tell what the
|
|
82
|
+
layer painted, so it only reports:
|
|
83
|
+
|
|
84
|
+
- an overlay token with no opacity, such as `'#surface #primary-accent-surface'`.
|
|
85
|
+
If the token is opaque, it covered the base completely: use it alone. If it
|
|
86
|
+
holds a translucent color, split it into an opaque color and an opacity:
|
|
87
|
+
`'#base+#overlay.$overlay-alpha'`.
|
|
88
|
+
- a literal overlay such as `rgb(0 0 0 / .1)`;
|
|
89
|
+
- three or more space-separated colors.
|
|
90
|
+
|
|
91
|
+
A comma mix of three or more colors is reported too: Tasty mixes only the first
|
|
92
|
+
two.
|
|
93
|
+
|
|
94
|
+
## Translucent mix parts
|
|
95
|
+
|
|
96
|
+
When neither part of a comma mix has a percentage, each takes half, so a part
|
|
97
|
+
with an opacity is weighed down twice, by its share of the mix and by its own
|
|
98
|
+
alpha, and the background comes out see-through: `'#dark.04, #surface'` is 52%
|
|
99
|
+
opaque, not `#dark` at 4% over `#surface`. The rule reports it with an editor
|
|
100
|
+
suggestion, `'#surface+#dark.04'` (or the other color alone for `#clear`). It is
|
|
101
|
+
not an autofix, because the rewrite changes what renders. Opposite a part with a
|
|
102
|
+
percentage, a faded part is the base the other is painted over, and the mix is
|
|
103
|
+
exact: `'#dark 4%, #surface.5'` is fixed to `'#surface.5+#dark.04'`.
|
|
104
|
+
|
|
105
|
+
## Tasty version
|
|
106
|
+
|
|
107
|
+
The rule does nothing unless the project has `@tenphi/tasty` 3.15.0 or later
|
|
108
|
+
installed: an older Tasty renders only the first color of a two-color fill, so a
|
|
109
|
+
fix would break it.
|
|
110
|
+
|
|
111
|
+
With Tasty 3.15, which has no compound colors, the rule fixes the
|
|
112
|
+
space-separated form to a comma mix (`'#base #tok.N'` to `'#tok N%, #base'`),
|
|
113
|
+
leaves comma mixes alone, and suggests a weight for a translucent part
|
|
114
|
+
(`'#dark 4%, #surface'`). Once the project is on 3.16, the next run fixes those
|
|
115
|
+
comma mixes to compound colors.
|
|
116
|
+
|
|
117
|
+
## Options
|
|
118
|
+
|
|
119
|
+
None.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# tasty/prefer-hide
|
|
2
|
+
|
|
3
|
+
Reports `display: 'none'`. Use `hide: true` instead.
|
|
4
|
+
|
|
5
|
+
**Presets:** warning in recommended and strict. **Fix:** autofix for a plain value; a value in a state map is reported only.
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
Tasty resolves `display` together with `hide`, `textOverflow`, `overflow` and `whiteSpace`, and `hide` has the highest priority. `hide: true` hides the element whatever `display` a state, a recipe or an extension sets. `display: 'none'` is one more `display` value that another one can override.
|
|
10
|
+
|
|
11
|
+
## Examples
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
// ❌ Use 'hide: true' instead of "display: 'none'".
|
|
15
|
+
tasty({ styles: { display: 'none' } });
|
|
16
|
+
|
|
17
|
+
// ✅ After the autofix
|
|
18
|
+
tasty({ styles: { hide: true } });
|
|
19
|
+
|
|
20
|
+
// ❌ Reported in a state map, without a fix
|
|
21
|
+
tasty({ styles: { display: { '': 'flex', collapsed: 'none' } } });
|
|
22
|
+
|
|
23
|
+
// ✅ By hand
|
|
24
|
+
tasty({ styles: { display: 'flex', hide: { '': false, collapsed: true } } });
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
A `'none'` inside a state map has no autofix, because moving it to `hide` changes the shape of both maps.
|
|
28
|
+
|
|
29
|
+
## Options
|
|
30
|
+
|
|
31
|
+
None.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# tasty/prefer-longhand-property
|
|
2
|
+
|
|
3
|
+
Reports the `flex` shorthand. Use `flexGrow`, `flexShrink` and `flexBasis` instead.
|
|
4
|
+
|
|
5
|
+
**Presets:** error in recommended and strict. **Fix:** none.
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
`flex` resets the parts you leave out to values that are not their initial ones: `flex: 1` sets `flex-basis` to `0%`, not `auto`. The longhands set only what you write, which also lets a state or an extension change one part without touching the others.
|
|
10
|
+
|
|
11
|
+
## Examples
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
// ❌ 'flex' resets the components you omit to non-initial values. Use flexGrow / flexShrink / flexBasis instead.
|
|
15
|
+
flex: '1'
|
|
16
|
+
|
|
17
|
+
// ✅ Only what you mean
|
|
18
|
+
flexGrow: 1
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
There is no autofix: splitting the shorthand needs to know which parts you meant to set.
|
|
22
|
+
|
|
23
|
+
## Options
|
|
24
|
+
|
|
25
|
+
None.
|