taste-lint 0.0.10 → 0.0.11
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/data/rules/copywriting/copywriting-live-region-feedback.yaml +6 -0
- package/data/rules/copywriting/copywriting-semantic-links.yaml +6 -0
- package/data/rules/copywriting/copywriting-unverifiable-proof.yaml +6 -0
- package/data/rules/craft/craft-faux-product-chrome.yaml +6 -0
- package/data/rules/craft/craft-i18n-rtl-untested.yaml +6 -0
- package/data/rules/craft/craft-i18n-untested.yaml +6 -0
- package/data/rules/craft/craft-inline-styles.yaml +5 -0
- package/data/rules/craft/craft-lazy-load-offscreen.yaml +6 -0
- package/data/rules/craft/craft-token-drift.yaml +6 -0
- package/data/rules/interaction/interaction-a11y-data-table-semantics.yaml +6 -0
- package/data/rules/interaction/interaction-a11y-disabled-control-tooltip.yaml +6 -0
- package/data/rules/interaction/interaction-a11y-document-language.yaml +6 -0
- package/data/rules/interaction/interaction-a11y-icon-controls-labeled.yaml +6 -0
- package/data/rules/interaction/interaction-a11y-media-captions.yaml +6 -0
- package/data/rules/interaction/interaction-a11y-semantic-html-first.yaml +6 -0
- package/data/rules/interaction/interaction-a11y-tooltip-no-interactive.yaml +6 -0
- package/data/rules/interaction/interaction-broken-focus-trap.yaml +6 -0
- package/data/rules/interaction/interaction-dont-block-paste-ime.yaml +6 -0
- package/data/rules/interaction/interaction-error-association.yaml +6 -0
- package/data/rules/interaction/interaction-focus-visible.yaml +6 -0
- package/data/rules/interaction/interaction-input-affix-hit-target.yaml +6 -0
- package/data/rules/interaction/interaction-keyboard-operable.yaml +6 -0
- package/data/rules/interaction/interaction-labels-and-autocomplete.yaml +6 -0
- package/data/rules/interaction/interaction-mobile-input-font-size.yaml +6 -0
- package/data/rules/interaction/interaction-no-error-boundary.yaml +6 -0
- package/data/rules/interaction/interaction-no-suspense-boundary.yaml +6 -0
- package/data/rules/interaction/interaction-not-restored.yaml +6 -0
- package/data/rules/interaction/interaction-on-dynamic-content.yaml +6 -0
- package/data/rules/interaction/interaction-optimistic-without-rollback.yaml +6 -0
- package/data/rules/interaction/interaction-out-of-order-responses.yaml +6 -0
- package/data/rules/interaction/interaction-target-size.yaml +6 -0
- package/data/rules/interaction/interaction-use-form-status-misuse.yaml +6 -0
- package/data/rules/product/product-nested-modals.yaml +6 -0
- package/data/rules/product/product-select-for-few-options.yaml +6 -0
- package/data/rules/schema/rule.schema.json +27 -0
- package/data/rules/typography/typography-hover-weight-shift.yaml +6 -0
- package/data/rules/typography/typography-readable-scale.yaml +6 -0
- package/dist/cli.js +314 -251
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +306 -247
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -22,3 +22,9 @@ severity: minor
|
|
|
22
22
|
fix:
|
|
23
23
|
hint: Toasts and validation summaries should use polite live regions unless interruption is critical. A toast that is only painted on screen never reaches assistive tech, so the user never learns the action finished.
|
|
24
24
|
status: review-only
|
|
25
|
+
review:
|
|
26
|
+
applicability: A transient status message communicates an outcome without moving focus.
|
|
27
|
+
exceptions: A shared toast or live-region provider may already announce the message.
|
|
28
|
+
evidence: The mounted message, provider implementation, live-region attributes, and announcement timing.
|
|
29
|
+
verification: Trigger the outcome with a screen reader and confirm exactly one meaningful announcement without focus theft.
|
|
30
|
+
sourceHash: 48cea89e0c3ca2e41e677f194d6eecc55f4c82b5a356318540c8569bccba32b7
|
|
@@ -21,3 +21,9 @@ severity: minor
|
|
|
21
21
|
fix:
|
|
22
22
|
hint: Navigation should use `<a>` or framework `<Link>`, not click handlers on generic elements. A click handler on a div loses middle-click, open-in-new-tab, copy-link, and the browser's own back behaviour.
|
|
23
23
|
status: review-only
|
|
24
|
+
review:
|
|
25
|
+
applicability: Navigation is exposed through a generic element's click handler.
|
|
26
|
+
exceptions: A redundant card shortcut around an accessible real link can be intentional.
|
|
27
|
+
evidence: The actual destination, nested links, handler behavior, and keyboard or browser navigation options.
|
|
28
|
+
verification: Verify keyboard navigation, open-in-new-tab, and copy-link behavior while preserving legitimate larger click targets.
|
|
29
|
+
sourceHash: 09e2de7a3e263b2dd145eb661ac460d9e49e3b360841bb3e8484f5f62d602347
|
|
@@ -21,3 +21,9 @@ severity: minor
|
|
|
21
21
|
fix:
|
|
22
22
|
hint: '"99.9% uptime", "10,000+ teams", "3x faster", a testimonial from Jane Doe, CEO at Acme, and a strip of logos nobody has heard of. Every one of these is a claim about the world, and a visitor who cannot check any of them learns that this page''s numbers are decoration. The damage is not aesthetic: it '
|
|
23
23
|
status: review-only
|
|
24
|
+
review:
|
|
25
|
+
applicability: User-facing marketing copy makes a factual claim or presents social proof.
|
|
26
|
+
exceptions: Clearly labeled placeholders, teaching examples, chart data, and claims supported by supplied evidence.
|
|
27
|
+
evidence: The exact claim and location, its displayed context, and a checkable source or an accountable owner.
|
|
28
|
+
verification: Verify the claim against its source or remove only the unsupported claim; preserve real evidence and clearly marked examples.
|
|
29
|
+
sourceHash: 0901fbcaeac7ed1d59328aa3012fb808332c4761ab4cb44c268433eb5a48d7f1
|
|
@@ -20,3 +20,9 @@ severity: minor
|
|
|
20
20
|
fix:
|
|
21
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
22
|
status: review-only
|
|
23
|
+
review:
|
|
24
|
+
applicability: Decorative browser or device chrome presents itself as a product preview.
|
|
25
|
+
exceptions: Real screenshots, video, rendered product content, labeled status dots, and intentionally illustrative examples.
|
|
26
|
+
evidence: Adjacent dot elements and labels, frame children, and the content being represented.
|
|
27
|
+
verification: Confirm the preview depicts real product content or remove the misleading frame while preserving legitimate illustrations.
|
|
28
|
+
sourceHash: 857c4c37111c9c2591c5746cb22d391d97e29f9f66656b62068eb7dbe94a4529
|
|
@@ -21,3 +21,9 @@ severity: minor
|
|
|
21
21
|
fix:
|
|
22
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
23
|
status: review-only
|
|
24
|
+
review:
|
|
25
|
+
applicability: A layout is intended to support right-to-left locales.
|
|
26
|
+
exceptions: Explicitly LTR-only products, fixed brand marks, and physical directions that should not mirror.
|
|
27
|
+
evidence: Locale policy, logical and physical styles, RTL tests, and the rendered layout.
|
|
28
|
+
verification: Exercise the task in an RTL locale; verify text, navigation, and directional icons while preserving fixed-direction assets.
|
|
29
|
+
sourceHash: fff00a214b53fd3d40c4dab0e2fe823193b3254ca0e425176f57e00706822f5c
|
|
@@ -26,3 +26,9 @@ fix:
|
|
|
26
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
27
|
status: review-only
|
|
28
28
|
handWritten: [mechanical, fix, status]
|
|
29
|
+
review:
|
|
30
|
+
applicability: A surface is intended to support dark mode.
|
|
31
|
+
exceptions: Explicit light-only brand surfaces, fixed-color illustrations and logos, and inherited theme overrides.
|
|
32
|
+
evidence: Theme policy, computed colors, global styles, and the surface in both supported themes.
|
|
33
|
+
verification: Switch themes and inspect text, borders, and controls; preserve intentionally fixed-color brand assets.
|
|
34
|
+
sourceHash: bd3df3c19addb343e38f313757092a20d8fe46b305fda84071d0a435b283ad79
|
|
@@ -22,3 +22,8 @@ fix:
|
|
|
22
22
|
Move the styling into utility classes or the theme. Inline styles bypass the design tokens, dark mode and the linter alike.
|
|
23
23
|
status: review-only
|
|
24
24
|
handWritten: [mechanical, fix, status, scope]
|
|
25
|
+
review:
|
|
26
|
+
applicability: Inline styles may bypass an established reusable design token or component API.
|
|
27
|
+
exceptions: Measured geometry, animation values, dynamic CSS variables, and legitimate one-off styles.
|
|
28
|
+
evidence: The style values, their runtime dependencies, repeated usage, and the actual design-system policy.
|
|
29
|
+
verification: Compare relevant states and viewport sizes after any change; preserve dynamic layout and animation behavior.
|
|
@@ -26,3 +26,9 @@ fix:
|
|
|
26
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
27
|
status: review-only
|
|
28
28
|
handWritten: [mechanical, fix, status]
|
|
29
|
+
review:
|
|
30
|
+
applicability: A lazy-loaded image may be needed in the initial viewport.
|
|
31
|
+
exceptions: Below-the-fold covers or banners and images hidden until a later interaction.
|
|
32
|
+
evidence: Actual viewport position, responsive visibility, loading priority, and page performance traces.
|
|
33
|
+
verification: Load at supported viewport sizes under throttling and confirm the initial image loads promptly without eagerly loading offscreen content.
|
|
34
|
+
sourceHash: ee89307867a0159516cdcad42ff09ebb33e6512ff3b3e301aa0c6b99e243f5c6
|
|
@@ -20,3 +20,9 @@ severity: minor
|
|
|
20
20
|
fix:
|
|
21
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
22
|
status: review-only
|
|
23
|
+
review:
|
|
24
|
+
applicability: A project has an explicit theme scale and source values may diverge from it.
|
|
25
|
+
exceptions: No theme means unknown; repeated occurrences of one value are not distinct decisions; legitimate dynamic values need separate review.
|
|
26
|
+
evidence: The real theme and distinct arbitrary values with file locations; count and compare values in code, never infer drift from @theme alone.
|
|
27
|
+
verification: Compare each value against the existing scale and inspect affected states after replacement, preserving values with a documented purpose.
|
|
28
|
+
sourceHash: f4a22bd2f0b14b278c5ee26fc13b7781ab786b0435b93992c3bd3f5c16903a72
|
|
@@ -22,3 +22,9 @@ severity: major
|
|
|
22
22
|
fix:
|
|
23
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
24
|
status: review-only
|
|
25
|
+
review:
|
|
26
|
+
applicability: A table or grid presents data with row or column headers.
|
|
27
|
+
exceptions: Layout tables and shared components that supply header associations internally.
|
|
28
|
+
evidence: Rendered table semantics and associations, including imported table components.
|
|
29
|
+
verification: Navigate the table with assistive technology and confirm each data cell has its relevant header context.
|
|
30
|
+
sourceHash: 0db48967cd4251a7cdd56324405a30923cd27bfcb4a6a688bcb3ec5e8ac1fdc6
|
|
@@ -21,3 +21,9 @@ severity: minor
|
|
|
21
21
|
fix:
|
|
22
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
23
|
status: review-only
|
|
24
|
+
review:
|
|
25
|
+
applicability: A disabled control relies on a tooltip to explain why it is unavailable.
|
|
26
|
+
exceptions: Focusable aria-disabled controls or a focusable wrapper with an associated explanation.
|
|
27
|
+
evidence: The actual trigger, focusability, accessible description, and behavior on touch.
|
|
28
|
+
verification: Reach the explanation by keyboard and touch while confirming the disabled action cannot execute.
|
|
29
|
+
sourceHash: 986e701811386c84b55d59af666e943125eb5121a94610ea7fb5fb5af1de4f1a
|
|
@@ -22,3 +22,9 @@ severity: major
|
|
|
22
22
|
fix:
|
|
23
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
24
|
status: review-only
|
|
25
|
+
review:
|
|
26
|
+
applicability: The served document root lacks its language declaration.
|
|
27
|
+
exceptions: Attribute spreads, locale middleware, or the framework may supply lang at runtime.
|
|
28
|
+
evidence: The served html element for each supported locale.
|
|
29
|
+
verification: Inspect the served document and confirm lang matches its primary language, including localized routes.
|
|
30
|
+
sourceHash: 170c27f2745858d2ab62fa116a14db11bb9991ddce12280d38742816a2998ee0
|
|
@@ -21,3 +21,9 @@ severity: minor
|
|
|
21
21
|
fix:
|
|
22
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
23
|
status: review-only
|
|
24
|
+
review:
|
|
25
|
+
applicability: An icon-only button has no accessible name.
|
|
26
|
+
exceptions: An icon title, visually hidden text, or a wrapper may provide the name.
|
|
27
|
+
evidence: The rendered accessibility tree and the icon or button wrapper implementation.
|
|
28
|
+
verification: Confirm the button has an accurate accessible name and still performs the intended action.
|
|
29
|
+
sourceHash: ac90c783c8b12d706c8697164ed16643d7c2c23ea9cf77448f2c05af425fca5a
|
|
@@ -22,3 +22,9 @@ severity: major
|
|
|
22
22
|
fix:
|
|
23
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
24
|
status: review-only
|
|
25
|
+
review:
|
|
26
|
+
applicability: Media conveys speech or meaning-critical sound.
|
|
27
|
+
exceptions: Silent decorative loops and players that provide captions or accessible alternatives internally.
|
|
28
|
+
evidence: The actual media content, player configuration, caption tracks, and transcript alternatives.
|
|
29
|
+
verification: Play the media with captions enabled and verify meaningful audio has an equivalent accessible representation.
|
|
30
|
+
sourceHash: e644ef7f5fecf25a6ecd18494a9c158a4a60a6e6887fa35fae8eb43aebf9645f
|
|
@@ -21,3 +21,9 @@ severity: major
|
|
|
21
21
|
fix:
|
|
22
22
|
hint: Use semantic HTML controls first; only add ARIA when native elements cannot express intent. Rebuilding those semantics in ARIA reimplements keyboard behaviour, role, and state by hand, and any gap leaves assistive tech reporting the wrong thing.
|
|
23
23
|
status: review-only
|
|
24
|
+
review:
|
|
25
|
+
applicability: A generic element is the only way to invoke an action.
|
|
26
|
+
exceptions: Redundant pointer shortcuts, such as a dismissible backdrop with a real close button and Escape support.
|
|
27
|
+
evidence: The handler, alternate controls, keyboard path, and rendered semantics.
|
|
28
|
+
verification: Complete the action with keyboard and pointer; preserve legitimate backdrop dismissal.
|
|
29
|
+
sourceHash: 7a04ac7b7d825ebeaa9b35ce026c893cf0c6940a66d971e66811797acb7a8444
|
|
@@ -21,3 +21,9 @@ severity: minor
|
|
|
21
21
|
fix:
|
|
22
22
|
hint: A hover tooltip that contains a link, button, or input cannot be used with a keyboard or a touch screen, and it vanishes when the pointer moves to reach the inner control. Users see an action they cannot activate reliably.
|
|
23
23
|
status: review-only
|
|
24
|
+
review:
|
|
25
|
+
applicability: Hover tooltip content contains an interactive control.
|
|
26
|
+
exceptions: Keyboard-operable popovers and menus, or decorative noninteractive icons.
|
|
27
|
+
evidence: The primitive implementation, open mechanism, focus handling, and nested controls.
|
|
28
|
+
verification: Reach and operate every nested action by keyboard and touch, with predictable dismissal.
|
|
29
|
+
sourceHash: 2767c22f7d256217e842ed75b4c843b69f7679d5733048bf85cf40d3df039af2
|
|
@@ -23,3 +23,9 @@ fix:
|
|
|
23
23
|
A hand-rolled dialog: Tab and Shift+Tab must cycle inside it and Esc must close it. Use a primitive that traps focus (Radix Dialog, React Aria, Headless UI, focus-trap-react) or wire the trap yourself.
|
|
24
24
|
status: review-only
|
|
25
25
|
handWritten: [mechanical, fix, status]
|
|
26
|
+
review:
|
|
27
|
+
applicability: An open modal requires focus containment.
|
|
28
|
+
exceptions: Nonmodal dialogs and shared primitives that already contain focus.
|
|
29
|
+
evidence: The dialog wrapper, modal setting, and actual Tab and Shift+Tab behavior.
|
|
30
|
+
verification: Open the modal from its trigger; cycle focus in both directions and confirm Escape and focus restoration work.
|
|
31
|
+
sourceHash: ecb41c42149ff38a0fc49b4e3abfcb49a6e727113f87bd6c6478d6f8bd4e44d1
|
|
@@ -21,3 +21,9 @@ severity: minor
|
|
|
21
21
|
fix:
|
|
22
22
|
hint: Avoid handlers that prevent paste or aggressively filter keystrokes. Blocking paste breaks password managers and assistive input, and keystroke filters swallow the composition events IME users type with.
|
|
23
23
|
status: review-only
|
|
24
|
+
review:
|
|
25
|
+
applicability: A paste handler prevents the default input operation.
|
|
26
|
+
exceptions: Sanitizing handlers that read clipboardData and correctly insert the accepted value.
|
|
27
|
+
evidence: The complete paste handler and resulting input value, including composition handling.
|
|
28
|
+
verification: Paste representative values and use an input method editor; verify accepted input is retained.
|
|
29
|
+
sourceHash: 54ff41fb901d2cb32ab28cb64aac9b46a29a69b59b2424aebb4789f5bc2b5703
|
|
@@ -22,3 +22,9 @@ severity: major
|
|
|
22
22
|
fix:
|
|
23
23
|
hint: Tie each error to its input via `aria-describedby`, mark the field `aria-invalid`, and announce it through a live region (`role="alert"`). A message only visually near the field is invisible to screen readers. Complements `forms-inline-errors-first-focus`, which covers placement and focus.
|
|
24
24
|
status: review-only
|
|
25
|
+
review:
|
|
26
|
+
applicability: An input displays a validation error.
|
|
27
|
+
exceptions: Shared field wrappers may supply accessible associations and announcements.
|
|
28
|
+
evidence: Rendered input and error IDs, aria-describedby, invalid state, and wrapper implementation.
|
|
29
|
+
verification: Trigger validation, focus the field, and confirm its error is associated and announced without clearing input.
|
|
30
|
+
sourceHash: ad17085d74839a35f7a1532207bcb9223739ffe0c5357803b7cd8fbf58750316
|
|
@@ -23,3 +23,9 @@ severity: major
|
|
|
23
23
|
fix:
|
|
24
24
|
hint: Never remove outlines without a clear `:focus-visible` replacement. With the outline gone, keyboard users cannot see where they are and the interface stops being navigable.
|
|
25
25
|
status: review-only
|
|
26
|
+
review:
|
|
27
|
+
applicability: A keyboard-operable control removes its visible focus indicator.
|
|
28
|
+
exceptions: Global or inherited focus styles and containers focused only programmatically with tabIndex=-1.
|
|
29
|
+
evidence: The affected control's computed styles and focus appearance, including forced colors.
|
|
30
|
+
verification: Tab through the controls in normal and forced-colors modes and confirm focus remains visible.
|
|
31
|
+
sourceHash: 6a7becf6636a0271b441421decefed4629658f280d0318b5c87454cbc61eb723
|
|
@@ -21,3 +21,9 @@ severity: minor
|
|
|
21
21
|
fix:
|
|
22
22
|
hint: "A search icon, currency mark, or unit sitting beside the field as a sibling splits the hit target: clicks on the decoration do not focus the input, and the control reads as two widgets. Prefix and suffix decorations belong on top of the text input, with padding so the value does not collide, and the"
|
|
23
23
|
status: review-only
|
|
24
|
+
review:
|
|
25
|
+
applicability: A decorative affix appears to be part of a text input but does not focus it.
|
|
26
|
+
exceptions: Independent buttons for submit, clear, generation, or password visibility.
|
|
27
|
+
evidence: The affix role, pointer handling, label association, and actual hit area.
|
|
28
|
+
verification: Click or tap the decorative affix and confirm it focuses the input; preserve independent button actions.
|
|
29
|
+
sourceHash: 924cb987818a11e9687f38b9edd5a6108ce30bf01bf91464fecef0ec40b1f36c
|
|
@@ -21,3 +21,9 @@ severity: major
|
|
|
21
21
|
fix:
|
|
22
22
|
hint: Pointer-only handlers are not acceptable for critical actions. Anything reachable only by pointer cannot be completed by keyboard, switch, or screen reader users at all.
|
|
23
23
|
status: review-only
|
|
24
|
+
review:
|
|
25
|
+
applicability: A custom interactive element has a pointer action.
|
|
26
|
+
exceptions: Native controls and library prop spreads or wrappers can provide keyboard behavior.
|
|
27
|
+
evidence: The resolved props, primitive implementation, and keyboard activation path.
|
|
28
|
+
verification: Reach the control with Tab and invoke its action using the appropriate keys without duplicate activation.
|
|
29
|
+
sourceHash: e471538d463ad9f256db46946bd532aa90d718246ff161a03d298bb535618bb8
|
|
@@ -21,3 +21,9 @@ severity: major
|
|
|
21
21
|
fix:
|
|
22
22
|
hint: Inputs require explicit labels and appropriate `type`, `name`, and `autocomplete` values. Without them users retype data the browser already has, and assistive tech has no name to announce.
|
|
23
23
|
status: review-only
|
|
24
|
+
review:
|
|
25
|
+
applicability: An input appears to rely on its placeholder as its label.
|
|
26
|
+
exceptions: An enclosing label, generated ID, or shared component can provide a persistent accessible name.
|
|
27
|
+
evidence: The enclosing markup and rendered accessible name, plus the field's intended input purpose.
|
|
28
|
+
verification: Enter a value so the placeholder disappears; confirm the field remains labeled and relevant autofill still works.
|
|
29
|
+
sourceHash: ef503cfa74b78af3be45e6e7302ddee8c9a20f4b2e3296ffea8610c6bfb26f7f
|
|
@@ -22,3 +22,9 @@ severity: minor
|
|
|
22
22
|
fix:
|
|
23
23
|
hint: "Set input text to at least 16px on mobile and avoid autofocus on touch-first flows. iOS zooms the viewport in on any field below 16px and does not zoom back out, leaving the user panned into a form they now have to scroll sideways. Do not stop that zoom with `maximum-scale=1` or `user-scalable=no`: "
|
|
24
24
|
status: review-only
|
|
25
|
+
review:
|
|
26
|
+
applicability: An input used on touch devices may render with small text.
|
|
27
|
+
exceptions: Pointer-fine media queries and explicitly desktop-only surfaces.
|
|
28
|
+
evidence: Computed input size, media queries, supported devices, and viewport configuration.
|
|
29
|
+
verification: Focus the field on supported mobile Safari and verify unwanted zoom does not obscure the task.
|
|
30
|
+
sourceHash: 949bb935cb20213772fb475ae04e339ebb1412db925c61f6e7e1e49a2096de22
|
|
@@ -25,3 +25,9 @@ fix:
|
|
|
25
25
|
A thrown error in an async tree with no ancestor boundary unmounts the whole route. Wrap the fetching subtree in an ErrorBoundary (or add error.tsx to the route segment) so a failed query degrades in place.
|
|
26
26
|
status: review-only
|
|
27
27
|
handWritten: [mechanical, fix, status]
|
|
28
|
+
review:
|
|
29
|
+
applicability: A rendered async tree can throw an error requiring a recovery surface.
|
|
30
|
+
exceptions: Ancestor error boundaries, inherited route error files, and local nonthrowing error handling.
|
|
31
|
+
evidence: The component ancestry, route error conventions, and the actual failure propagation path.
|
|
32
|
+
verification: Force the relevant failure; confirm a usable recovery appears and unrelated UI and entered data are preserved.
|
|
33
|
+
sourceHash: d799e08e7c33ea18fee714de6639dc62feda4dc63de1a1b12e0aed8ca69ad8e9
|
|
@@ -21,3 +21,9 @@ severity: minor
|
|
|
21
21
|
fix:
|
|
22
22
|
hint: "In the App Router and React 19, an async server component with no enclosing `<Suspense>` boundary blocks the whole route from streaming. Users wait for the slowest fetch before seeing anything: no shell, no skeleton, no progressive paint. Wrap the slow async tree in `<Suspense fallback={...}>` to un"
|
|
23
23
|
status: review-only
|
|
24
|
+
review:
|
|
25
|
+
applicability: A component suspends while loading user-visible content.
|
|
26
|
+
exceptions: Ancestor Suspense or loading.tsx coverage and async functions that do not suspend in practice.
|
|
27
|
+
evidence: The awaited operation, route ancestry, loading files, and rendered pending behavior.
|
|
28
|
+
verification: Throttle the operation; verify useful pending feedback appears and the completed view remains usable.
|
|
29
|
+
sourceHash: 3c85a6e1acbffffb266c4b9a020aab49a622c83489e94948674f232dcab9cbcc
|
|
@@ -23,3 +23,9 @@ fix:
|
|
|
23
23
|
When a dialog closes, focus returns to the element that opened it. Keep the trigger ref and focus it on close (onCloseAutoFocus, finalFocus, returnFocusOnDeactivate) instead of leaving focus on body.
|
|
24
24
|
status: review-only
|
|
25
25
|
handWritten: [mechanical, fix, status]
|
|
26
|
+
review:
|
|
27
|
+
applicability: Closing a modal or sheet should return the user to a meaningful focus location.
|
|
28
|
+
exceptions: Radix or react-aria defaults, explicit alternative focus targets, and navigation to a new route.
|
|
29
|
+
evidence: The primitive implementation, close handlers, trigger lifetime, and destination focus.
|
|
30
|
+
verification: Open and close by keyboard, Escape, and successful submission; confirm focus lands on the intended available control.
|
|
31
|
+
sourceHash: 47241f8dddee12c74d18ef8cd1c140a16aa6d9dd90184225de5e1e52508751eb
|
|
@@ -23,3 +23,9 @@ fix:
|
|
|
23
23
|
A client-side navigation never moves focus or announces the new content. After router.push, focus the new page heading or main region, or announce the change in an aria-live region.
|
|
24
24
|
status: review-only
|
|
25
25
|
handWritten: [mechanical, fix, status]
|
|
26
|
+
review:
|
|
27
|
+
applicability: A dynamic update changes information the user needs to perceive.
|
|
28
|
+
exceptions: Framework route focus, shared live-region providers, and updates that should not move focus.
|
|
29
|
+
evidence: The rendered update, focus management, announcement provider, and task context.
|
|
30
|
+
verification: Trigger the update with a screen reader; confirm it is perceivable without unnecessary focus theft or duplicate announcements.
|
|
31
|
+
sourceHash: a048816b3767ff40513c80315cb70cd10bda43e2ff431c105c4b1d5cd3510d85
|
|
@@ -21,3 +21,9 @@ severity: major
|
|
|
21
21
|
fix:
|
|
22
22
|
hint: "`useOptimistic` shows the post-action state immediately (liked post, cart item added, renamed file). Contract: on server reject (422, 500, network error) the UI must roll back to real server state, or it lies and a reload reveals the truth, costing trust. Second half of the contract: optimistic upda"
|
|
23
23
|
status: review-only
|
|
24
|
+
review:
|
|
25
|
+
applicability: An optimistic update presents an unconfirmed server result.
|
|
26
|
+
exceptions: Framework action reversion or a query library may own rollback and error feedback.
|
|
27
|
+
evidence: The action or transition, authoritative state, failure path, and visible feedback.
|
|
28
|
+
verification: Force the server operation to fail; confirm the UI returns to truthful state and offers useful feedback.
|
|
29
|
+
sourceHash: 3a8fdc4ddf83d905d92c3955b1c544ba4fcb221223425c4122f7687b88268df1
|
|
@@ -25,3 +25,9 @@ fix:
|
|
|
25
25
|
Two keystrokes fire two requests and the slow one can land last, showing stale results under fresh input. Cancel the in-flight request with an AbortController (or pass signal), or debounce through useDeferredValue.
|
|
26
26
|
status: review-only
|
|
27
27
|
handWritten: [mechanical, fix, status]
|
|
28
|
+
review:
|
|
29
|
+
applicability: Successive input-driven requests can update the same visible result.
|
|
30
|
+
exceptions: Fire-and-forget mutations and correctly keyed query libraries or stale-result guards.
|
|
31
|
+
evidence: Request keys, cancellation or sequence guards, response commits, and the displayed result.
|
|
32
|
+
verification: Make an older request finish after a newer one; confirm the UI retains the result for the current input.
|
|
33
|
+
sourceHash: 293ab4d682398a772dd7b593cc32ee5dc925ac8f02d6ef9e6af715709ca32a71
|
|
@@ -24,3 +24,9 @@ fix:
|
|
|
24
24
|
Touch targets need 44 by 44px; 24px is the floor only for dense desktop UI under pointer: fine. Give the button at least size-11, or pad the hit area while keeping the icon small.
|
|
25
25
|
status: review-only
|
|
26
26
|
handWritten: [mechanical, fix, status]
|
|
27
|
+
review:
|
|
28
|
+
applicability: A control may have an inadequately small pointer target.
|
|
29
|
+
exceptions: Small glyphs inside larger controls, expanded hit areas, and applicable spacing or inline-text exceptions.
|
|
30
|
+
evidence: Rendered interactive bounds, neighboring targets, platform expectations, and the applicable accessibility requirement.
|
|
31
|
+
verification: Measure the control rather than its icon and exercise nearby targets without accidental activation.
|
|
32
|
+
sourceHash: cbaf893d74a3cd7cc218b55032b531aba51de7e0b38ead9324d2704eb240cc41
|
|
@@ -21,3 +21,9 @@ severity: major
|
|
|
21
21
|
fix:
|
|
22
22
|
hint: "`useFormStatus` returns the **parent** `<form>`'s status. Called in the same component that renders the `<form>`, it returns `{ pending: false }` forever (no parent form to inspect). It compiles, runs, never warns, and silently breaks everything depending on it (disabled submit, pending label, optim"
|
|
23
23
|
status: review-only
|
|
24
|
+
review:
|
|
25
|
+
applicability: A component calls useFormStatus to reflect a form's pending state.
|
|
26
|
+
exceptions: A child component can correctly call the hook even when defined in the same file as the form.
|
|
27
|
+
evidence: The calling component and its rendered parent form, including imported wrappers.
|
|
28
|
+
verification: Submit with a delayed action; confirm pending state activates and resets after both success and failure.
|
|
29
|
+
sourceHash: ef214d29e4b8d8be0fabfbaf3bb0e86b20b24d4a5afea70655eac79f75e2de00
|
|
@@ -24,3 +24,9 @@ fix:
|
|
|
24
24
|
# a modal opened from a handler; the finding is a candidate for a person.
|
|
25
25
|
status: review-only
|
|
26
26
|
handWritten: [mechanical, fix, status]
|
|
27
|
+
review:
|
|
28
|
+
applicability: Opening a modal can leave another modal open underneath it.
|
|
29
|
+
exceptions: JSX nesting alone does not prove simultaneous visibility; mutually exclusive steps and nonmodal surfaces are exceptions.
|
|
30
|
+
evidence: Both open-state transitions, modal primitive implementations, and focus and Escape behavior.
|
|
31
|
+
verification: Open the second step using the keyboard; confirm one modal owns focus and Escape closes the intended surface.
|
|
32
|
+
sourceHash: e575cc8c2c06ae38542bd068a9f4d1871714391c543b5caf0dbb8ae1847a2f77
|
|
@@ -22,3 +22,9 @@ fix:
|
|
|
22
22
|
# a modal opened from a handler; the finding is a candidate for a person.
|
|
23
23
|
status: review-only
|
|
24
24
|
handWritten: [mechanical, fix, status]
|
|
25
|
+
review:
|
|
26
|
+
applicability: A select presents two or three static, mutually exclusive choices.
|
|
27
|
+
exceptions: Dynamically loaded options, dependent choices, or a documented platform constraint can require a select.
|
|
28
|
+
evidence: The complete option source, dynamic branches, and the task requiring comparison.
|
|
29
|
+
verification: Confirm all choices remain available and selection, keyboard input, and form submission work.
|
|
30
|
+
sourceHash: e575cc8c2c06ae38542bd068a9f4d1871714391c543b5caf0dbb8ae1847a2f77
|
|
@@ -238,6 +238,33 @@
|
|
|
238
238
|
"items": {
|
|
239
239
|
"type": "string"
|
|
240
240
|
}
|
|
241
|
+
},
|
|
242
|
+
"review": {
|
|
243
|
+
"type": "object",
|
|
244
|
+
"additionalProperties": false,
|
|
245
|
+
"required": ["applicability", "exceptions", "evidence", "verification"],
|
|
246
|
+
"properties": {
|
|
247
|
+
"applicability": {
|
|
248
|
+
"type": "string",
|
|
249
|
+
"minLength": 1
|
|
250
|
+
},
|
|
251
|
+
"exceptions": {
|
|
252
|
+
"type": "string",
|
|
253
|
+
"minLength": 1
|
|
254
|
+
},
|
|
255
|
+
"evidence": {
|
|
256
|
+
"type": "string",
|
|
257
|
+
"minLength": 1
|
|
258
|
+
},
|
|
259
|
+
"verification": {
|
|
260
|
+
"type": "string",
|
|
261
|
+
"minLength": 1
|
|
262
|
+
},
|
|
263
|
+
"sourceHash": {
|
|
264
|
+
"type": "string",
|
|
265
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
266
|
+
}
|
|
267
|
+
}
|
|
241
268
|
}
|
|
242
269
|
},
|
|
243
270
|
"additionalProperties": false,
|
|
@@ -22,3 +22,9 @@ severity: minor
|
|
|
22
22
|
fix:
|
|
23
23
|
hint: Bumping `font-weight` on hover, focus, or selected nav items makes the label wider, so neighbours shift. The user sees the row jump rather than a state change. Use color, opacity, background, or underline for selected and hover; keep the weight stable.
|
|
24
24
|
status: review-only
|
|
25
|
+
review:
|
|
26
|
+
applicability: Interactive labels change font weight on hover or selection.
|
|
27
|
+
exceptions: Reserved label width, intentional selection indicators without reflow, and noninteractive body copy.
|
|
28
|
+
evidence: The control bounds and adjacent items in both states, including the actual loaded font.
|
|
29
|
+
verification: Hover and select with the font loaded; confirm labels and neighboring controls do not move.
|
|
30
|
+
sourceHash: 95d6ae7b2d3319f43af63e7f1a2c4416f680c78483c528eda0308eb3e4e4f271
|
|
@@ -21,3 +21,9 @@ severity: minor
|
|
|
21
21
|
fix:
|
|
22
22
|
hint: Use body sizes and weights readable across desktop and mobile. Undersized or underweight body text slows scanning and tires readers.
|
|
23
23
|
status: review-only
|
|
24
|
+
review:
|
|
25
|
+
applicability: Base reading text sets a small size or light weight.
|
|
26
|
+
exceptions: Large-format prose can use a lighter weight; rem values depend on the resolved root size.
|
|
27
|
+
evidence: Computed size and weight of the affected body text at supported viewport sizes.
|
|
28
|
+
verification: Check the rendered paragraph with the real font; preserve intentional large display text.
|
|
29
|
+
sourceHash: b7c93aab2b0922786d9ff18c53ccefe9d127f7e8be14f7f2a10fb1f2b9c50086
|