@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.
Files changed (53) hide show
  1. package/README.md +64 -45
  2. package/docs/rules/consistent-token-usage.md +72 -0
  3. package/docs/rules/known-property.md +55 -0
  4. package/docs/rules/no-classname-prop.md +60 -0
  5. package/docs/rules/no-ignored-pseudo-element-state.md +97 -0
  6. package/docs/rules/no-important.md +25 -0
  7. package/docs/rules/no-nested-selector.md +39 -0
  8. package/docs/rules/no-nested-state-map.md +33 -0
  9. package/docs/rules/no-own-at-root.md +57 -0
  10. package/docs/rules/no-protected-restyle.md +192 -0
  11. package/docs/rules/no-raw-color-values.md +49 -0
  12. package/docs/rules/no-raw-motion-duration.md +50 -0
  13. package/docs/rules/no-raw-transition-duration.md +56 -0
  14. package/docs/rules/no-runtime-styles-mutation.md +58 -0
  15. package/docs/rules/no-state-in-selector.md +142 -0
  16. package/docs/rules/no-style-prop.md +127 -0
  17. package/docs/rules/no-style-spread.md +53 -0
  18. package/docs/rules/no-styles-prop.md +57 -0
  19. package/docs/rules/no-unknown-state-alias.md +45 -0
  20. package/docs/rules/prefer-auto-calc.md +30 -0
  21. package/docs/rules/prefer-custom-property-syntax.md +56 -0
  22. package/docs/rules/prefer-directional-shorthand.md +46 -0
  23. package/docs/rules/prefer-element-selector.md +77 -0
  24. package/docs/rules/prefer-fill-mix.md +119 -0
  25. package/docs/rules/prefer-hide.md +31 -0
  26. package/docs/rules/prefer-longhand-property.md +25 -0
  27. package/docs/rules/prefer-shorthand-property.md +194 -0
  28. package/docs/rules/prefer-state-negation.md +44 -0
  29. package/docs/rules/require-default-state.md +50 -0
  30. package/docs/rules/static-no-dynamic-values.md +32 -0
  31. package/docs/rules/static-valid-selector.md +36 -0
  32. package/docs/rules/valid-boolean-property.md +39 -0
  33. package/docs/rules/valid-color-token.md +64 -0
  34. package/docs/rules/valid-custom-property.md +36 -0
  35. package/docs/rules/valid-custom-unit.md +34 -0
  36. package/docs/rules/valid-default-state-order.md +44 -0
  37. package/docs/rules/valid-directional-modifier.md +46 -0
  38. package/docs/rules/valid-preset.md +35 -0
  39. package/docs/rules/valid-radius-shape.md +31 -0
  40. package/docs/rules/valid-recipe.md +35 -0
  41. package/docs/rules/valid-state-definition.md +46 -0
  42. package/docs/rules/valid-state-key.md +62 -0
  43. package/docs/rules/valid-styles-structure.md +48 -0
  44. package/docs/rules/valid-sub-element.md +100 -0
  45. package/docs/rules/valid-transition.md +46 -0
  46. package/docs/rules/valid-value.md +65 -0
  47. package/harness/dev.harnesspm/hooks.json +12 -0
  48. package/harness/dev.harnesspm/rules/usage.md +11 -0
  49. package/harness/dev.harnesspm/scripts/check-edit.mjs +127 -0
  50. package/harness/dev.harnesspm/scripts/check.mjs +159 -0
  51. package/harness/dev.harnesspm/scripts/lint.mjs +236 -0
  52. package/harness/plugin.json +23 -0
  53. 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.