tailwind-a11y 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -78,6 +78,7 @@ When a case can't be resolved with confidence, it's skipped rather than guessed:
78
78
 
79
79
  - [`eslint-plugin-tailwind-a11y`](https://github.com/chamroro/tailwind-a11y/tree/main/packages/eslint-plugin-tailwind-a11y) — same checks as ESLint rules
80
80
  - [`vscode-tailwind-a11y`](https://github.com/chamroro/tailwind-a11y/tree/main/packages/vscode-tailwind-a11y) — same checks as live editor diagnostics
81
+ - [`github-action-tailwind-a11y`](https://github.com/chamroro/tailwind-a11y/tree/main/packages/github-action-tailwind-a11y) — same checks as inline PR annotations
81
82
 
82
83
  ## License
83
84
 
@@ -5,6 +5,7 @@ export interface ContrastCheck {
5
5
  bgColorClass: string;
6
6
  bgSource: "self" | "parent";
7
7
  }
8
+ export declare const COLOR_TOKEN: RegExp;
8
9
  export declare function lastColorToken(className: string, prefix: "text" | "bg"): string | null;
9
10
  export declare function extractChecks(code: string, filePath: string): ContrastCheck[];
10
11
  export interface ContrastSkip {
@@ -9,7 +9,11 @@ import { getStaticClassName, parseJSX, traverse } from "./babelInterop.js";
9
9
  // an extremely common real idiom, more so than the named-scale case, and
10
10
  // dropping it here would make the contrast checker's opacity support (see
11
11
  // rules/checkContrast.ts) silently inapplicable to the most common case.
12
- const COLOR_TOKEN = /^\[(#[0-9a-fA-F]{3,8})\](\/\d{1,3})?$|^[a-z]+-\d{2,3}(\/\d{1,3})?$|^(white|black|transparent|current|inherit)(\/\d{1,3})?$/;
12
+ // Exported for reuse in checkFocusIndicator.ts, which needs the exact same
13
+ // "does this suffix look like a color value" test to detect shadow-{color}/
14
+ // ring-{color} decoys (see that file for why) -- one definition, not a
15
+ // second copy that could silently drift out of sync.
16
+ export const COLOR_TOKEN = /^\[(#[0-9a-fA-F]{3,8})\](\/\d{1,3})?$|^[a-z]+-\d{2,3}(\/\d{1,3})?$|^(white|black|transparent|current|inherit)(\/\d{1,3})?$/;
13
17
  // opacity-{N} (e.g. bg-opacity-50, text-opacity-50) matches the same
14
18
  // "word-number" shape as a color token but isn't one — without this
15
19
  // exclusion it can silently overwrite a real color match via the
@@ -1,9 +1,17 @@
1
+ import { COLOR_TOKEN } from "../parser/extractClasses.js";
1
2
  const REMOVAL_BASE = "outline-none";
2
3
  // Utilities that match the "replacement" shape but are semantically no-ops —
3
4
  // the same failure mode as bg-opacity-50 masking a real color match: a
4
5
  // same-prefix decoy that would silently hide a real violation if we only
5
- // checked the prefix.
6
- const DEGENERATE_BASES = new Set(["outline-none", "ring-0", "border-0", "shadow-none", "bg-transparent"]);
6
+ // checked the prefix. border-none/border-hidden only set border-style, not
7
+ // border-width -- verified against a real Tailwind v4 build that preflight
8
+ // resets every element to `border: 0 solid`, so border-width stays 0
9
+ // regardless of style and no border is ever drawn, same failure mode as
10
+ // border-0.
11
+ const DEGENERATE_BASES = new Set([
12
+ "outline-none", "ring-0", "border-0", "shadow-none", "bg-transparent",
13
+ "border-none", "border-hidden",
14
+ ]);
7
15
  // Modifier-only utilities (opacity/offset/inset) don't set a concrete value
8
16
  // on their own — e.g. bg-opacity-50 with no bg-* color, or ring-offset-4
9
17
  // with no ring-* width, renders nothing visible by itself. Same failure
@@ -31,8 +39,32 @@ const NON_VISUAL_BASES = new Set([
31
39
  ]);
32
40
  // bg-blend-{mode} (e.g. bg-blend-multiply) sets a blend mode, not a color --
33
41
  // suffix-varying like MODIFIER_ONLY above, so pattern-matched instead of
34
- // enumerated.
35
- const NON_VISUAL_PATTERN = /^bg-blend-/;
42
+ // enumerated. border-spacing-*/-x-*/-y-* sets the CSS border-spacing
43
+ // property, which only affects the gap between <table> cells -- verified
44
+ // against a real Tailwind v4 build that it has zero visual effect on any
45
+ // non-table element (the only elements this check ever fires on), even
46
+ // with a real numeric value applied.
47
+ const NON_VISUAL_PATTERN = /^bg-blend-|^border-spacing(-[xy])?-/;
48
+ // shadow-{color}/ring-{color} alone (e.g. shadow-red-500, ring-blue-400/50,
49
+ // an arbitrary hex) only sets the --tw-shadow-color/--tw-ring-color CSS
50
+ // variable -- the actual box-shadow property is set by a separate *size*
51
+ // utility (shadow-lg, ring-2, etc.) that references that variable. Verified
52
+ // against a real Tailwind v4 build: shadow-red-500 alone computes to
53
+ // `box-shadow: none`; shadow-lg shadow-red-500 together produce a real,
54
+ // colored shadow. Same underlying mechanism as MODIFIER_ONLY's opacity/
55
+ // offset cases above, but shaped like a color token rather than a fixed
56
+ // suffix, so it reuses COLOR_TOKEN (the exact same "is this a color value"
57
+ // test extractClasses.ts uses) instead of a third, drifting definition of
58
+ // what a color looks like.
59
+ function isColorOnlyShadowOrRing(base) {
60
+ const shadowMatch = /^shadow-(.+)$/.exec(base);
61
+ if (shadowMatch && COLOR_TOKEN.test(shadowMatch[1]))
62
+ return true;
63
+ const ringMatch = /^ring-(.+)$/.exec(base);
64
+ if (ringMatch && COLOR_TOKEN.test(ringMatch[1]))
65
+ return true;
66
+ return false;
67
+ }
36
68
  function baseUtility(raw) {
37
69
  return raw.slice(raw.lastIndexOf(":") + 1);
38
70
  }
@@ -42,6 +74,8 @@ function isReplacement(raw) {
42
74
  return false;
43
75
  if (NON_VISUAL_BASES.has(base) || NON_VISUAL_PATTERN.test(base))
44
76
  return false;
77
+ if (isColorOnlyShadowOrRing(base))
78
+ return false;
45
79
  return /^(ring|border|shadow|bg|outline)(-|$)/.test(base);
46
80
  }
47
81
  export function checkFocusIndicators(checks) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tailwind-a11y",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Static analysis CLI that catches WCAG accessibility violations — color contrast, touch target size, and focus indicator removal — in Tailwind CSS class combinations before they ship.",
5
5
  "type": "module",
6
6
  "bin": {