taste-lint 0.0.1

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 (138) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +141 -0
  3. package/data/config.schema.json +123 -0
  4. package/data/corpus/blode-co-review.jsonl +80 -0
  5. package/data/corpus/copywriting.jsonl +139 -0
  6. package/data/corpus/typography.jsonl +53 -0
  7. package/data/rule-drafts/README.md +9 -0
  8. package/data/rule-drafts/interaction/interaction-layout-shift.yaml +54 -0
  9. package/data/rule-drafts/typography/typography-long-content-safety.yaml +60 -0
  10. package/data/rules/authoring/authoring-approval-every-step.yaml +44 -0
  11. package/data/rules/authoring/authoring-fixed-agent-quota.yaml +44 -0
  12. package/data/rules/authoring/authoring-generic-coding-advice.yaml +44 -0
  13. package/data/rules/authoring/authoring-generic-verification.yaml +45 -0
  14. package/data/rules/authoring/authoring-plan-vague-acceptance.yaml +43 -0
  15. package/data/rules/copywriting/copywriting-announced-honesty.yaml +43 -0
  16. package/data/rules/copywriting/copywriting-anthropomorphism.yaml +29 -0
  17. package/data/rules/copywriting/copywriting-artificial-balance.yaml +35 -0
  18. package/data/rules/copywriting/copywriting-bare-confirm-label.yaml +23 -0
  19. package/data/rules/copywriting/copywriting-boilerplate-offer.yaml +38 -0
  20. package/data/rules/copywriting/copywriting-canned-phrasing.yaml +53 -0
  21. package/data/rules/copywriting/copywriting-changed-supplied-facts.yaml +31 -0
  22. package/data/rules/copywriting/copywriting-claim-without-evidence.yaml +59 -0
  23. package/data/rules/copywriting/copywriting-click-here-links.yaml +22 -0
  24. package/data/rules/copywriting/copywriting-conditions-first.yaml +50 -0
  25. package/data/rules/copywriting/copywriting-confirmshaming.yaml +43 -0
  26. package/data/rules/copywriting/copywriting-cta-names-outcome.yaml +54 -0
  27. package/data/rules/copywriting/copywriting-dead-weight-hedges.yaml +58 -0
  28. package/data/rules/copywriting/copywriting-drafting-instruction-echo.yaml +31 -0
  29. package/data/rules/copywriting/copywriting-emoji-in-ui.yaml +20 -0
  30. package/data/rules/copywriting/copywriting-empty-state-no-action.yaml +51 -0
  31. package/data/rules/copywriting/copywriting-error-descriptions.yaml +53 -0
  32. package/data/rules/copywriting/copywriting-excessive-hedging.yaml +48 -0
  33. package/data/rules/copywriting/copywriting-fabricated-specificity.yaml +35 -0
  34. package/data/rules/copywriting/copywriting-filler-words.yaml +55 -0
  35. package/data/rules/copywriting/copywriting-flat-description.yaml +37 -0
  36. package/data/rules/copywriting/copywriting-friction-cta.yaml +53 -0
  37. package/data/rules/copywriting/copywriting-generic-audience.yaml +55 -0
  38. package/data/rules/copywriting/copywriting-generic-framing.yaml +35 -0
  39. package/data/rules/copywriting/copywriting-generic-language.yaml +35 -0
  40. package/data/rules/copywriting/copywriting-generic-validation.yaml +46 -0
  41. package/data/rules/copywriting/copywriting-idea-repetition.yaml +34 -0
  42. package/data/rules/copywriting/copywriting-latinisms.yaml +25 -0
  43. package/data/rules/copywriting/copywriting-leaked-error-message.yaml +23 -0
  44. package/data/rules/copywriting/copywriting-live-region-feedback.yaml +24 -0
  45. package/data/rules/copywriting/copywriting-machine-prose.yaml +73 -0
  46. package/data/rules/copywriting/copywriting-mechanism-over-outcome.yaml +33 -0
  47. package/data/rules/copywriting/copywriting-meta-commentary.yaml +48 -0
  48. package/data/rules/copywriting/copywriting-missing-reasoning-link.yaml +33 -0
  49. package/data/rules/copywriting/copywriting-nominalisations.yaml +45 -0
  50. package/data/rules/copywriting/copywriting-over-explanation.yaml +34 -0
  51. package/data/rules/copywriting/copywriting-passive-voice.yaml +53 -0
  52. package/data/rules/copywriting/copywriting-permission-benefit.yaml +43 -0
  53. package/data/rules/copywriting/copywriting-profile-register-mismatch.yaml +31 -0
  54. package/data/rules/copywriting/copywriting-promotional-docs-voice.yaml +58 -0
  55. package/data/rules/copywriting/copywriting-prompt-restatement.yaml +35 -0
  56. package/data/rules/copywriting/copywriting-punctuation-tics.yaml +35 -0
  57. package/data/rules/copywriting/copywriting-reader-first.yaml +59 -0
  58. package/data/rules/copywriting/copywriting-register-shift.yaml +58 -0
  59. package/data/rules/copywriting/copywriting-repetitive-transitions.yaml +48 -0
  60. package/data/rules/copywriting/copywriting-requirements-language.yaml +50 -0
  61. package/data/rules/copywriting/copywriting-searchable-headings.yaml +52 -0
  62. package/data/rules/copywriting/copywriting-semantic-links.yaml +23 -0
  63. package/data/rules/copywriting/copywriting-structural-symmetry.yaml +35 -0
  64. package/data/rules/copywriting/copywriting-success-state-specific.yaml +43 -0
  65. package/data/rules/copywriting/copywriting-temporal-words.yaml +55 -0
  66. package/data/rules/copywriting/copywriting-toast-successfully.yaml +22 -0
  67. package/data/rules/copywriting/copywriting-unexplained-action.yaml +33 -0
  68. package/data/rules/copywriting/copywriting-unnatural-polish.yaml +48 -0
  69. package/data/rules/copywriting/copywriting-unraised-objection.yaml +43 -0
  70. package/data/rules/copywriting/copywriting-unverifiable-proof.yaml +23 -0
  71. package/data/rules/copywriting/copywriting-vague-error.yaml +58 -0
  72. package/data/rules/copywriting/copywriting-vague-personal-request.yaml +43 -0
  73. package/data/rules/copywriting/copywriting-visual-direction-instruction.yaml +43 -0
  74. package/data/rules/craft/craft-affordance-mismatch.yaml +23 -0
  75. package/data/rules/craft/craft-decoration-no-role.yaml +27 -0
  76. package/data/rules/craft/craft-faux-product-chrome.yaml +22 -0
  77. package/data/rules/craft/craft-hover-only-affordance.yaml +23 -0
  78. package/data/rules/craft/craft-i18n-rtl-untested.yaml +23 -0
  79. package/data/rules/craft/craft-i18n-untested.yaml +28 -0
  80. package/data/rules/craft/craft-image-dimensions-and-priority.yaml +23 -0
  81. package/data/rules/craft/craft-inline-styles.yaml +24 -0
  82. package/data/rules/craft/craft-lazy-load-offscreen.yaml +28 -0
  83. package/data/rules/craft/craft-near-duplicate-scale.yaml +29 -0
  84. package/data/rules/craft/craft-token-drift.yaml +22 -0
  85. package/data/rules/craft/craft-viewport-scaling.yaml +25 -0
  86. package/data/rules/craft/craft-virtualize-large-lists.yaml +24 -0
  87. package/data/rules/interaction/interaction-a11y-color-only-meaning.yaml +23 -0
  88. package/data/rules/interaction/interaction-a11y-data-table-semantics.yaml +24 -0
  89. package/data/rules/interaction/interaction-a11y-disabled-control-tooltip.yaml +23 -0
  90. package/data/rules/interaction/interaction-a11y-document-language.yaml +24 -0
  91. package/data/rules/interaction/interaction-a11y-icon-controls-labeled.yaml +23 -0
  92. package/data/rules/interaction/interaction-a11y-image-alt-text.yaml +24 -0
  93. package/data/rules/interaction/interaction-a11y-media-captions.yaml +24 -0
  94. package/data/rules/interaction/interaction-a11y-semantic-html-first.yaml +23 -0
  95. package/data/rules/interaction/interaction-a11y-skip-link-heading-order.yaml +24 -0
  96. package/data/rules/interaction/interaction-a11y-tooltip-no-interactive.yaml +23 -0
  97. package/data/rules/interaction/interaction-broken-focus-trap.yaml +25 -0
  98. package/data/rules/interaction/interaction-dont-block-paste-ime.yaml +23 -0
  99. package/data/rules/interaction/interaction-error-association.yaml +24 -0
  100. package/data/rules/interaction/interaction-focus-visible.yaml +25 -0
  101. package/data/rules/interaction/interaction-inline-errors-first-focus.yaml +24 -0
  102. package/data/rules/interaction/interaction-input-affix-hit-target.yaml +23 -0
  103. package/data/rules/interaction/interaction-keyboard-operable.yaml +23 -0
  104. package/data/rules/interaction/interaction-labels-and-autocomplete.yaml +23 -0
  105. package/data/rules/interaction/interaction-lost-data-on-error.yaml +23 -0
  106. package/data/rules/interaction/interaction-mobile-input-font-size.yaml +24 -0
  107. package/data/rules/interaction/interaction-no-disable-while-submitting.yaml +23 -0
  108. package/data/rules/interaction/interaction-no-empty-state.yaml +26 -0
  109. package/data/rules/interaction/interaction-no-error-boundary.yaml +27 -0
  110. package/data/rules/interaction/interaction-no-error-state.yaml +26 -0
  111. package/data/rules/interaction/interaction-no-suspense-boundary.yaml +23 -0
  112. package/data/rules/interaction/interaction-not-restored.yaml +25 -0
  113. package/data/rules/interaction/interaction-on-dynamic-content.yaml +25 -0
  114. package/data/rules/interaction/interaction-optimistic-without-rollback.yaml +23 -0
  115. package/data/rules/interaction/interaction-out-of-order-responses.yaml +27 -0
  116. package/data/rules/interaction/interaction-target-size.yaml +26 -0
  117. package/data/rules/interaction/interaction-use-form-status-misuse.yaml +23 -0
  118. package/data/rules/product/product-nested-modals.yaml +26 -0
  119. package/data/rules/product/product-select-for-few-options.yaml +24 -0
  120. package/data/rules/schema/rule.schema.json +262 -0
  121. package/data/rules/tuning.json +1 -0
  122. package/data/rules/typography/typography-dashes.yaml +28 -0
  123. package/data/rules/typography/typography-ellipsis.yaml +27 -0
  124. package/data/rules/typography/typography-hover-weight-shift.yaml +24 -0
  125. package/data/rules/typography/typography-midpoint-separators.yaml +21 -0
  126. package/data/rules/typography/typography-multiplication-sign.yaml +27 -0
  127. package/data/rules/typography/typography-nbsp-value-unit.yaml +27 -0
  128. package/data/rules/typography/typography-readable-scale.yaml +23 -0
  129. package/data/rules/typography/typography-straight-quotes.yaml +37 -0
  130. package/data/rules/typography/typography-title-case-heading.yaml +49 -0
  131. package/dist/cli.d.ts +2 -0
  132. package/dist/cli.js +6317 -0
  133. package/dist/cli.js.map +1 -0
  134. package/dist/index.d.ts +736 -0
  135. package/dist/index.d.ts.map +1 -0
  136. package/dist/index.js +5330 -0
  137. package/dist/index.js.map +1 -0
  138. package/package.json +87 -0
@@ -0,0 +1,58 @@
1
+ # Ported from mblode/agent-skills skills/ui-design/rules/microcopy-vague-error.md.
2
+ id: copywriting-vague-error
3
+ title: Error message with no cause and no next step
4
+ categoryId: actionable-microcopy
5
+ source:
6
+ line: 10
7
+ path: skills/ui-design/rules/microcopy-vague-error.md
8
+ repo: mblode/agent-skills
9
+ ruleId: microcopy-vague-error
10
+ unit:
11
+ - jsx-text
12
+ - attr-string
13
+ mechanical:
14
+ phrases:
15
+ - error occurred
16
+ - an error has occurred
17
+ - something went wrong
18
+ - please try again
19
+ - try again later
20
+ - oops
21
+ - invalid
22
+ - unexpected error
23
+ - an error occurred
24
+ - failed
25
+ question:
26
+ context:
27
+ - element
28
+ - docType
29
+ instructions: The TEXT is a message shown to a user after something failed. Return true when it tells the user neither what went wrong nor what to do next (for example "Something went wrong", "Invalid", "An error occurred. Please try again."). Return false when it names the cause or the recovery (what failed, what to check, where to go), when it is a sign-in credential message that is deliberately vague for security ("Wrong email or password"), or when the TEXT is not an error message at all.
30
+ criteria:
31
+ "true":
32
+ what: No cause and no recovery.
33
+ examples:
34
+ - Something went wrong
35
+ - Invalid
36
+ - Oops! An error occurred. Please try again.
37
+ "false":
38
+ what: Cause or recovery named, or a deliberate security message.
39
+ examples:
40
+ - Email already in use. Sign in instead.
41
+ - Couldn't reach the payment provider. Your card was not charged. Try again.
42
+ - Wrong email or password.
43
+ severity: major
44
+ fix:
45
+ hint: Write what happened, why when you know it, and the next step. Never blame the person and never clear the form.
46
+ preconditions:
47
+ notInCode: true
48
+ docType:
49
+ - ui
50
+ - unknown
51
+ - lesson
52
+ status: review-only
53
+ handWritten:
54
+ - mechanical
55
+ - question
56
+ - fix
57
+ - severityOverrides
58
+ - preconditions
@@ -0,0 +1,43 @@
1
+ id: copywriting-vague-personal-request
2
+ title: Personal request has an unspecified time expectation
3
+ categoryId: actionable-microcopy
4
+ source:
5
+ repo: mblode/ghostwriter
6
+ path: skills/ghostwriter/references/strategy.md
7
+ line: 1
8
+ unit:
9
+ - paragraph
10
+ - jsx-text
11
+ - attr-string
12
+ mechanical:
13
+ regex: \b(?:ASAP|soon|when you get a chance)\b
14
+ flags: giu
15
+ question:
16
+ context:
17
+ - docType
18
+ - headingAbove
19
+ - role
20
+ - element
21
+ instructions: Return true when a request uses a vague urgency phrase that makes its timing unclear. Return false for a deliberately open-ended invitation, quoted wording, or when the supplied text already names a concrete deadline. Never invent a deadline.
22
+ criteria:
23
+ "true":
24
+ what: The stated failure is supported by the supplied text.
25
+ examples:
26
+ - Please send the launch review ASAP.
27
+ "false":
28
+ what: The text is specific, justified, quoted, or outside this rule.
29
+ examples:
30
+ - Send the launch review by Thursday at noon.
31
+ severity: minor
32
+ fix:
33
+ hint: Use the actual deadline if known; otherwise clarify urgency without inventing a time.
34
+ preconditions:
35
+ notInCode: true
36
+ docType:
37
+ - personal
38
+ status: review-only
39
+ handWritten:
40
+ - mechanical
41
+ - question
42
+ - preconditions
43
+ - fix
@@ -0,0 +1,43 @@
1
+ id: copywriting-visual-direction-instruction
2
+ title: UI instruction depends on visual position
3
+ categoryId: actionable-microcopy
4
+ source:
5
+ repo: installed/copywriting
6
+ path: skills/copywriting/references/ui-states.md
7
+ line: 1
8
+ unit:
9
+ - paragraph
10
+ - jsx-text
11
+ - attr-string
12
+ mechanical:
13
+ regex: \b(?:above|below|left|right)\b
14
+ flags: giu
15
+ question:
16
+ context:
17
+ - docType
18
+ - headingAbove
19
+ - role
20
+ - element
21
+ instructions: Return true only when an instruction identifies a control solely by visual position, without naming the control or a semantic destination. Return false for named controls, keyboard arrow instructions, descriptions of physical directions, or text where position is the information requested.
22
+ criteria:
23
+ "true":
24
+ what: The stated failure is supported by the supplied text.
25
+ examples:
26
+ - Click the button on the right.
27
+ "false":
28
+ what: The text is specific, justified, quoted, or outside this rule.
29
+ examples:
30
+ - Select Save changes.
31
+ severity: minor
32
+ fix:
33
+ hint: Name the control or destination so the instruction works without seeing the layout.
34
+ preconditions:
35
+ notInCode: true
36
+ docType:
37
+ - ui
38
+ status: review-only
39
+ handWritten:
40
+ - mechanical
41
+ - question
42
+ - preconditions
43
+ - fix
@@ -0,0 +1,23 @@
1
+ # Ported from mblode/agent-skills skills/ui-design/rules/slop-affordance-mismatch.md by scripts/port-rules.ts.
2
+ # Keys listed in handWritten are preserved on re-run; everything else is regenerated.
3
+ id: craft-affordance-mismatch
4
+ title: Affordance styling with nothing behind it
5
+ categoryId: generic-decoration
6
+ source:
7
+ line: 10
8
+ path: skills/ui-design/rules/slop-affordance-mismatch.md
9
+ repo: mblode/agent-skills
10
+ ruleId: slop-affordance-mismatch
11
+ scope:
12
+ include:
13
+ - "**/*.tsx"
14
+ - "**/*.jsx"
15
+ unit:
16
+ - source
17
+ mechanical:
18
+ flags: gu
19
+ regex: cursor-pointer|hover:(bg|shadow|scale|border|ring|translate)-
20
+ severity: minor
21
+ fix:
22
+ hint: A card lifts on hover, the cursor turns into a hand, the border brightens, and clicking does nothing. The user has already committed to the click by the time they learn it was a decoration; they click again, assume the page is broken or slow, and then trust the next hover state less. This is worse t
23
+ status: review-only
@@ -0,0 +1,27 @@
1
+ # Hand-ported from mblode/agent-skills skills/ui-design/rules/slop-decoration-no-role.md. The detection there is a
2
+ # shell loop or pipeline; regex plus absent is the same check over one file.
3
+ id: craft-decoration-no-role
4
+ title: Stacked decorative layers with no role
5
+ categoryId: generic-decoration
6
+ source:
7
+ repo: mblode/agent-skills
8
+ path: skills/ui-design/rules/slop-decoration-no-role.md
9
+ line: 10
10
+ ruleId: slop-decoration-no-role
11
+ scope:
12
+ include:
13
+ - "**/*.tsx"
14
+ - "**/*.jsx"
15
+ unit: [source]
16
+ mechanical:
17
+ flags: gus
18
+ regex:
19
+ "<(?:div|span)\\b(?=[^>]*?(?:\\babsolute\\b|\\bfixed\\b))(?=[^>]*?(?:pointer-events-none|ari\
20
+ a-hidden))(?=[^>]*?(?:gradient|bg-linear|bg-radial|\\bblur|backdrop-blur|mix-blend|opacity-))(?\
21
+ ![^>]*?(?:size-\\[max\\(100%|pointer-fine:hidden|bg-clip-text|\\bmask-))[^>]*?/>"
22
+ severity: minor
23
+ fix:
24
+ hint: >-
25
+ A blurred blob, a grid overlay, a noise layer and a radial glow behind the heading: absolutely positioned elements that contain nothing and mean nothing. Cut them until each remaining layer has a job a reader could name.
26
+ status: review-only
27
+ handWritten: [mechanical, fix, status]
@@ -0,0 +1,22 @@
1
+ # Ported from mblode/agent-skills skills/ui-design/rules/slop-faux-product-chrome.md by scripts/port-rules.ts.
2
+ # Keys listed in handWritten are preserved on re-run; everything else is regenerated.
3
+ id: craft-faux-product-chrome
4
+ title: Faux product chrome wrapping nothing
5
+ categoryId: generic-decoration
6
+ source:
7
+ line: 10
8
+ path: skills/ui-design/rules/slop-faux-product-chrome.md
9
+ repo: mblode/agent-skills
10
+ ruleId: slop-faux-product-chrome
11
+ scope:
12
+ include:
13
+ - "**/*.tsx"
14
+ unit:
15
+ - source
16
+ mechanical:
17
+ flags: gu
18
+ regex: rounded-full[^"`]*\bbg-(red|rose|yellow|amber|green|emerald)-[45]00
19
+ severity: minor
20
+ fix:
21
+ hint: "Three coloured dots in a row, a rounded bar with a URL typed into it, a laptop outline around an empty gradient panel: this is a drawing of a product where the product should be. A visitor reads it as a screenshot, leans in to see what the app actually looks like, and finds there is nothing to see. "
22
+ status: review-only
@@ -0,0 +1,23 @@
1
+ # Ported from mblode/agent-skills skills/ui-design/rules/mobile-hover-only-affordance.md by scripts/port-rules.ts.
2
+ # Keys listed in handWritten are preserved on re-run; everything else is regenerated.
3
+ id: craft-hover-only-affordance
4
+ title: Critical actions hidden behind hover-only affordances
5
+ categoryId: resilience
6
+ source:
7
+ line: 10
8
+ path: skills/ui-design/rules/mobile-hover-only-affordance.md
9
+ repo: mblode/agent-skills
10
+ ruleId: mobile-hover-only-affordance
11
+ scope:
12
+ include:
13
+ - "**/*.ts"
14
+ - "**/*.tsx"
15
+ unit:
16
+ - source
17
+ mechanical:
18
+ flags: gu
19
+ regex: (group-hover|peer-hover|\bhover:):(opacity-100|visible|flex|block)
20
+ severity: minor
21
+ fix:
22
+ hint: 'Reveal-on-hover quietly disappears on touch: a row exposes "Edit / Delete / Share" only when the cursor enters, but a phone has no cursor. Touch users never discover the actions, or only via a frustrating long-press. The same pattern hides tooltips, secondary CTAs, and meaningful state ("3 unread") '
23
+ status: review-only
@@ -0,0 +1,23 @@
1
+ # Ported from mblode/agent-skills skills/ui-design/rules/dark-i18n-rtl-untested.md by scripts/port-rules.ts.
2
+ # Keys listed in handWritten are preserved on re-run; everything else is regenerated.
3
+ id: craft-i18n-rtl-untested
4
+ title: Layout uses physical (left/right) instead of logical (start/end) properties
5
+ categoryId: resilience
6
+ source:
7
+ line: 10
8
+ path: skills/ui-design/rules/dark-i18n-rtl-untested.md
9
+ repo: mblode/agent-skills
10
+ ruleId: dark-i18n-rtl-untested
11
+ scope:
12
+ include:
13
+ - "**/*.ts"
14
+ - "**/*.tsx"
15
+ unit:
16
+ - source
17
+ mechanical:
18
+ flags: gu
19
+ regex: className="[^"]*\b(ml-|mr-|pl-|pr-|left-|right-|text-(left|right)|float-(left|right)|border-(l|r))\b
20
+ severity: minor
21
+ fix:
22
+ hint: 'Arabic, Hebrew, Persian, and Urdu read right-to-left. A layout using physical properties (`margin-left`, `padding-right`, `text-align: left`, Tailwind `ml-2`, `pr-4`, `text-left`) does not flip when document direction is `rtl`: icons land on the wrong side, "forward" chevrons point backward, padding'
23
+ status: review-only
@@ -0,0 +1,28 @@
1
+ # Hand-ported from mblode/agent-skills skills/ui-design/rules/dark-i18n-untested.md. The detection there is a
2
+ # shell loop or pipeline; regex plus absent is the same check over one file.
3
+ # The source filters matching lines through `rg -v dark:`; the negative lookahead is
4
+ # that filter on the same line.
5
+ id: craft-i18n-untested
6
+ title: Hardcoded light-mode token with no dark variant on the line
7
+ categoryId: resilience
8
+ source:
9
+ repo: mblode/agent-skills
10
+ path: skills/ui-design/rules/dark-i18n-untested.md
11
+ line: 10
12
+ ruleId: dark-i18n-untested
13
+ scope:
14
+ include:
15
+ - "**/*.ts"
16
+ - "**/*.tsx"
17
+ unit: [source]
18
+ mechanical:
19
+ flags: gum
20
+ regex:
21
+ "^(?!.*dark:).*className=\"[^\"]*\\b(?:bg-white|bg-black|text-black|text-white|bg-gray-\\d{2\
22
+ ,3}|text-gray-\\d{2,3}|border-gray-\\d{2,3})\\b[^\"]*\".*$"
23
+ severity: minor
24
+ fix:
25
+ hint: >-
26
+ bg-white, text-black and fixed grays break in dark mode: white surfaces glow, grays lose contrast, borders vanish. Use the semantic tokens (bg-background, text-foreground, border-border) or pair each with a dark: variant.
27
+ status: review-only
28
+ handWritten: [mechanical, fix, status]
@@ -0,0 +1,23 @@
1
+ # Ported from mblode/agent-skills skills/ui-design/rules/perf-image-dimensions-and-priority.md by scripts/port-rules.ts.
2
+ # Keys listed in handWritten are preserved on re-run; everything else is regenerated.
3
+ id: craft-image-dimensions-and-priority
4
+ title: Set Image Dimensions and Priority Intentionally
5
+ categoryId: resilience
6
+ source:
7
+ line: 9
8
+ path: skills/ui-design/rules/perf-image-dimensions-and-priority.md
9
+ repo: mblode/agent-skills
10
+ ruleId: perf-image-dimensions-and-priority
11
+ scope:
12
+ include:
13
+ - "**/*.tsx"
14
+ - "**/*.jsx"
15
+ unit:
16
+ - source
17
+ mechanical:
18
+ flags: gus
19
+ regex: <img\b(?![^>]*\bwidth=)[^>]*>
20
+ severity: major
21
+ fix:
22
+ hint: Declare `width`/`height` (or aspect ratio) and prioritize only above-the-fold hero images. Undeclared dimensions shift the page as images arrive, and an unprioritized hero delays the largest paint.
23
+ status: review-only
@@ -0,0 +1,24 @@
1
+ # Hand-authored from shadcn-ui/lint (shadcn/no-inline-styles). The rule is re-expressed over
2
+ # taste-lint's extracted class lists; the original runs as an ESLint or oxlint plugin.
3
+ id: craft-inline-styles
4
+ title: Inline style object or style element in a component
5
+ categoryId: look-constraints
6
+ source:
7
+ repo: shadcn-ui/lint
8
+ path: README.md
9
+ line: 1
10
+ ruleId: shadcn/no-inline-styles
11
+ scope:
12
+ include: ["**/*.tsx", "**/*.jsx"]
13
+ exclude:
14
+ - "**/components/ui/**"
15
+ unit: [source]
16
+ mechanical:
17
+ flags: gu
18
+ regex: "style=\\{\\{|<style\\b"
19
+ severity: minor
20
+ fix:
21
+ hint: >-
22
+ Move the styling into utility classes or the theme. Inline styles bypass the design tokens, dark mode and the linter alike.
23
+ status: review-only
24
+ handWritten: [mechanical, fix, status, scope]
@@ -0,0 +1,28 @@
1
+ # Hand-ported from mblode/agent-skills skills/ui-design/rules/perf-lazy-load-offscreen.md. The detection there is a
2
+ # shell loop or pipeline; regex plus absent is the same check over one file.
3
+ # The source's `(?i:hero|banner|...)` inline modifier is written out as letter
4
+ # classes so the regex compiles on Node 22 and 24 alike.
5
+ id: craft-lazy-load-offscreen
6
+ title: Hero or above-the-fold image marked loading lazy
7
+ categoryId: resilience
8
+ source:
9
+ repo: mblode/agent-skills
10
+ path: skills/ui-design/rules/perf-lazy-load-offscreen.md
11
+ line: 9
12
+ ruleId: perf-lazy-load-offscreen
13
+ scope:
14
+ include:
15
+ - "**/*.tsx"
16
+ - "**/*.jsx"
17
+ unit: [source]
18
+ mechanical:
19
+ flags: gus
20
+ regex:
21
+ "<(?:img|Image)\\b(?=[^>]*loading=\"lazy\")(?=[^>]*(?:[Hh]ero|[Bb]anner|[Cc]over|[Mm]asthead\
22
+ |[Pp]riority))[^>]*>"
23
+ severity: minor
24
+ fix:
25
+ hint: >-
26
+ Never lazy-load the largest above-the-fold image; it delays the largest contentful paint. Load the hero eagerly (priority) and keep loading="lazy" for images below the fold.
27
+ status: review-only
28
+ handWritten: [mechanical, fix, status]
@@ -0,0 +1,29 @@
1
+ # Hand-ported from mblode/agent-skills skills/ui-design/rules/slop-near-duplicate-scale.md. The detection there is a
2
+ # shell loop or pipeline; regex plus absent is the same check over one file.
3
+ # Nudges of 2px or less are excluded in the regex, as the source's second filter does.
4
+ # The source asks for a theme precondition; this port has none and stays review-only.
5
+ id: craft-near-duplicate-scale
6
+ title: Arbitrary pixel value one step off the scale
7
+ categoryId: look-constraints
8
+ source:
9
+ repo: mblode/agent-skills
10
+ path: skills/ui-design/rules/slop-near-duplicate-scale.md
11
+ line: 10
12
+ ruleId: slop-near-duplicate-scale
13
+ scope:
14
+ include:
15
+ - "**/*.tsx"
16
+ - "**/*.jsx"
17
+ unit: [source]
18
+ mechanical:
19
+ flags: gu
20
+ regex:
21
+ "\\b(?:text|p|px|py|pt|pb|pl|pr|m|mt|mb|ml|mr|mx|my|gap|gap-[xy]|space-[xy]|rounded(?:-[a-z]\
22
+ +)?|leading|w|h|size|inset|top|bottom|left|right)-\\[(?!-?(?:[01](?:\\.\\d+)?|2(?:\\.0+)?)px\\]\
23
+ )-?\\d+(?:\\.\\d+)?px\\](?!:)"
24
+ severity: minor
25
+ fix:
26
+ hint: >-
27
+ text-[15px] beside text-sm and text-base is a third size that reads as a mistake, not a level. Step the scale, or register the value in the theme when it recurs. Check the theme first: with no scale defined there is nothing to be a near-duplicate of.
28
+ status: review-only
29
+ handWritten: [mechanical, fix, status]
@@ -0,0 +1,22 @@
1
+ # Ported from mblode/agent-skills skills/ui-design/rules/slop-token-drift.md by scripts/port-rules.ts.
2
+ # Keys listed in handWritten are preserved on re-run; everything else is regenerated.
3
+ id: craft-token-drift
4
+ title: Arbitrary values drifting off the theme
5
+ categoryId: look-constraints
6
+ source:
7
+ line: 10
8
+ path: skills/ui-design/rules/slop-token-drift.md
9
+ repo: mblode/agent-skills
10
+ ruleId: slop-token-drift
11
+ scope:
12
+ include:
13
+ - "**/*.css"
14
+ unit:
15
+ - source
16
+ mechanical:
17
+ flags: gu
18
+ regex: "@theme"
19
+ severity: minor
20
+ fix:
21
+ hint: "A theme exists so that sizes, colours, and radii repeat across a product and the eye reads them as one system. When a file reaches for five or six one-off bracket values, the surface stops agreeing with the rest of the app: headings land at sizes no other heading uses, a card corner is 3px rounder t"
22
+ status: review-only
@@ -0,0 +1,25 @@
1
+ # Ported from mblode/agent-skills skills/ui-design/rules/mobile-viewport-scaling.md by scripts/port-rules.ts.
2
+ # Keys listed in handWritten are preserved on re-run; everything else is regenerated.
3
+ id: craft-viewport-scaling
4
+ title: Missing viewport meta, 100vh on mobile, no safe-area insets
5
+ categoryId: resilience
6
+ source:
7
+ line: 10
8
+ path: skills/ui-design/rules/mobile-viewport-scaling.md
9
+ repo: mblode/agent-skills
10
+ ruleId: mobile-viewport-scaling
11
+ scope:
12
+ include:
13
+ - "**/*.md"
14
+ - "**/*.mdx"
15
+ - "**/*.tsx"
16
+ - "**/*.jsx"
17
+ unit:
18
+ - source
19
+ mechanical:
20
+ flags: gu
21
+ regex: export const viewport|<meta name="viewport"
22
+ severity: minor
23
+ fix:
24
+ hint: 'Three quiet bugs ship together on mobile, each a one-line fix invisible in desktop testing: a missing `<meta name="viewport">` makes iOS Safari render at 980 px and zoom out; `100vh` includes the address bar (~60 px wrong on iOS); a fixed bottom bar covers the home-indicator notch and clips content.'
25
+ status: review-only
@@ -0,0 +1,24 @@
1
+ # Ported from mblode/agent-skills skills/ui-design/rules/perf-virtualize-large-lists.md by scripts/port-rules.ts.
2
+ # Keys listed in handWritten are preserved on re-run; everything else is regenerated.
3
+ id: craft-virtualize-large-lists
4
+ title: Virtualize Long Lists
5
+ categoryId: resilience
6
+ source:
7
+ line: 9
8
+ path: skills/ui-design/rules/perf-virtualize-large-lists.md
9
+ repo: mblode/agent-skills
10
+ ruleId: perf-virtualize-large-lists
11
+ scope:
12
+ include:
13
+ - "**/*.tsx"
14
+ - "**/*.jsx"
15
+ unit:
16
+ - source
17
+ mechanical:
18
+ absent: react-window|@tanstack/react-virtual|react-virtuoso|virtua
19
+ flags: gus
20
+ regex: <(?:ul|ol|tbody)\b[^>]*>\s*\{[^}]*\.map\(
21
+ severity: minor
22
+ fix:
23
+ hint: Large lists (roughly >50 visible items) should use virtualization/windowing. Rendering the whole dataset stalls scrolling and grows memory with the list.
24
+ status: review-only
@@ -0,0 +1,23 @@
1
+ # Ported from mblode/agent-skills skills/ui-design/rules/a11y-color-only-meaning.md by scripts/port-rules.ts.
2
+ # Keys listed in handWritten are preserved on re-run; everything else is regenerated.
3
+ id: interaction-a11y-color-only-meaning
4
+ title: Avoid Color-Only Meaning
5
+ categoryId: focus-and-a11y
6
+ source:
7
+ line: 9
8
+ path: skills/ui-design/rules/a11y-color-only-meaning.md
9
+ repo: mblode/agent-skills
10
+ ruleId: a11y-color-only-meaning
11
+ scope:
12
+ include:
13
+ - "**/*.tsx"
14
+ - "**/*.jsx"
15
+ unit:
16
+ - source
17
+ mechanical:
18
+ flags: gu
19
+ regex: \?[^:]*\b(text|bg|border)-(red|green|amber|emerald|rose|yellow)-\d
20
+ severity: major
21
+ fix:
22
+ hint: State carried by hue alone (a red row, a green dot, a colored border) is invisible to color-blind users and on washed-out screens. Pair color with text, an icon, or shape. Contrast ratios are axe's job, not this rule's.
23
+ status: review-only
@@ -0,0 +1,24 @@
1
+ # Ported from mblode/agent-skills skills/ui-design/rules/a11y-data-table-semantics.md by scripts/port-rules.ts.
2
+ # Keys listed in handWritten are preserved on re-run; everything else is regenerated.
3
+ id: interaction-a11y-data-table-semantics
4
+ title: Mark Up Data Tables With Real Table Semantics
5
+ categoryId: focus-and-a11y
6
+ source:
7
+ line: 9
8
+ path: skills/ui-design/rules/a11y-data-table-semantics.md
9
+ repo: mblode/agent-skills
10
+ ruleId: a11y-data-table-semantics
11
+ scope:
12
+ include:
13
+ - "**/*.tsx"
14
+ - "**/*.jsx"
15
+ unit:
16
+ - source
17
+ mechanical:
18
+ absent: <caption\b|scope="(col|row)"
19
+ flags: gu
20
+ regex: <table\b|role="(table|grid)"
21
+ severity: major
22
+ fix:
23
+ hint: Tabular data needs a real `<table>` with `<caption>`, header cells, and `scope`. Divs styled as a grid carry no row/column relationships, so screen readers read cells as a flat list. (Layout-only grids use CSS, not `<table>`.)
24
+ status: review-only
@@ -0,0 +1,23 @@
1
+ # Ported from mblode/agent-skills skills/ui-design/rules/a11y-disabled-control-tooltip.md by scripts/port-rules.ts.
2
+ # Keys listed in handWritten are preserved on re-run; everything else is regenerated.
3
+ id: interaction-a11y-disabled-control-tooltip
4
+ title: Do not put tooltips on disabled controls
5
+ categoryId: focus-and-a11y
6
+ source:
7
+ line: 10
8
+ path: skills/ui-design/rules/a11y-disabled-control-tooltip.md
9
+ repo: mblode/agent-skills
10
+ ruleId: a11y-disabled-control-tooltip
11
+ scope:
12
+ include:
13
+ - "**/*.tsx"
14
+ - "**/*.jsx"
15
+ unit:
16
+ - source
17
+ mechanical:
18
+ flags: gus
19
+ regex: <[^>]*\bdisabled\b[^>]*>[\s\S]{0,400}<(Tooltip|HoverCard)
20
+ severity: minor
21
+ fix:
22
+ hint: "A tooltip on a `disabled` button never reaches keyboard or screen-reader users: disabled controls are omitted from the tab order, so the reason the action is blocked is invisible. Mouse users see a hover hint; everyone else hits a dead control with no explanation."
23
+ status: review-only
@@ -0,0 +1,24 @@
1
+ # Ported from mblode/agent-skills skills/ui-design/rules/a11y-document-language.md by scripts/port-rules.ts.
2
+ # Keys listed in handWritten are preserved on re-run; everything else is regenerated.
3
+ id: interaction-a11y-document-language
4
+ title: Declare Document and Inline Language
5
+ categoryId: focus-and-a11y
6
+ source:
7
+ line: 9
8
+ path: skills/ui-design/rules/a11y-document-language.md
9
+ repo: mblode/agent-skills
10
+ ruleId: a11y-document-language
11
+ scope:
12
+ include:
13
+ - "**/*.tsx"
14
+ - "**/*.jsx"
15
+ - "**/*.html"
16
+ unit:
17
+ - source
18
+ mechanical:
19
+ flags: gus
20
+ regex: <html(?![^>]*\blang=)[^>]*>
21
+ severity: major
22
+ fix:
23
+ hint: Set the primary language on `<html lang>` with a valid BCP 47 tag, and mark any inline passage in another language with its own `lang`. Without it, a screen reader reads everything with one pronunciation engine.
24
+ status: review-only
@@ -0,0 +1,23 @@
1
+ # Ported from mblode/agent-skills skills/ui-design/rules/a11y-icon-controls-labeled.md by scripts/port-rules.ts.
2
+ # Keys listed in handWritten are preserved on re-run; everything else is regenerated.
3
+ id: interaction-a11y-icon-controls-labeled
4
+ title: Label Icon-Only Controls
5
+ categoryId: focus-and-a11y
6
+ source:
7
+ line: 9
8
+ path: skills/ui-design/rules/a11y-icon-controls-labeled.md
9
+ repo: mblode/agent-skills
10
+ ruleId: a11y-icon-controls-labeled
11
+ scope:
12
+ include:
13
+ - "**/*.tsx"
14
+ - "**/*.jsx"
15
+ unit:
16
+ - source
17
+ mechanical:
18
+ flags: gus
19
+ regex: <button\b(?![^>]*(aria-label|aria-labelledby|title=))[^>]*>\s*<[A-Z][\w.]*[^>]*/>\s*</button>
20
+ severity: minor
21
+ fix:
22
+ hint: Any control with no visible text requires an accessible name. Without one, assistive tech announces nothing usable and the control cannot be identified.
23
+ status: review-only
@@ -0,0 +1,24 @@
1
+ # Ported from mblode/agent-skills skills/ui-design/rules/a11y-image-alt-text.md by scripts/port-rules.ts.
2
+ # Keys listed in handWritten are preserved on re-run; everything else is regenerated.
3
+ id: interaction-a11y-image-alt-text
4
+ title: Give Every Image a Correct Alt Attribute
5
+ categoryId: focus-and-a11y
6
+ source:
7
+ line: 9
8
+ path: skills/ui-design/rules/a11y-image-alt-text.md
9
+ repo: mblode/agent-skills
10
+ ruleId: a11y-image-alt-text
11
+ scope:
12
+ include:
13
+ - "**/*.tsx"
14
+ - "**/*.jsx"
15
+ - "**/*.html"
16
+ unit:
17
+ - source
18
+ mechanical:
19
+ flags: gus
20
+ regex: <img\b(?![^>]*\balt=)[^>]*>
21
+ severity: major
22
+ fix:
23
+ hint: 'Every `<img>` needs an `alt`: describe the purpose for informative images, use an empty `alt=""` for decorative ones so screen readers skip them. A missing `alt` makes the file name get read aloud. The `alt` is also what renders when the image itself fails to load.'
24
+ status: review-only
@@ -0,0 +1,24 @@
1
+ # Ported from mblode/agent-skills skills/ui-design/rules/a11y-media-captions.md by scripts/port-rules.ts.
2
+ # Keys listed in handWritten are preserved on re-run; everything else is regenerated.
3
+ id: interaction-a11y-media-captions
4
+ title: Caption Video and Transcribe Audio
5
+ categoryId: focus-and-a11y
6
+ source:
7
+ line: 9
8
+ path: skills/ui-design/rules/a11y-media-captions.md
9
+ repo: mblode/agent-skills
10
+ ruleId: a11y-media-captions
11
+ scope:
12
+ include:
13
+ - "**/*.tsx"
14
+ - "**/*.jsx"
15
+ unit:
16
+ - source
17
+ mechanical:
18
+ absent: kind="captions"
19
+ flags: gu
20
+ regex: <video\b|<audio\b
21
+ severity: major
22
+ fix:
23
+ hint: Deaf and hard-of-hearing users get nothing from uncaptioned media. Video needs synchronised captions via `<track kind="captions">`; audio-only needs a text transcript. Auto-generated captions alone are not sufficient for meaning-critical media.
24
+ status: review-only