tailwind-a11y 0.8.0 → 0.9.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
@@ -25,6 +25,7 @@ npm install --save-dev tailwind-a11y
25
25
  npx tailwind-a11y # scans **/*.{jsx,tsx}
26
26
  npx tailwind-a11y "src/**/*.tsx" # custom glob
27
27
  npx tailwind-a11y --verbose # also reports what couldn't be checked, and why
28
+ npx tailwind-a11y --strict # touch targets must meet WCAG 2.5.5 (AAA, 44x44px), not just 2.5.8 (AA, 24x24px)
28
29
  npx tailwind-a11y --config ./tw.config.cjs # use a specific config instead of auto-detecting
29
30
  npx tailwind-a11y --version # print the installed version
30
31
  npx tailwind-a11y --help # usage and all options
@@ -53,7 +54,7 @@ Exits `1` on violations — safe to use as a CI gate.
53
54
  | Check | WCAG | Detects |
54
55
  |---|---|---|
55
56
  | Contrast | 1.4.3 (AA) | `text-*`/`bg-*` pairs below 4.5:1, same-element or direct-parent, including a text-side opacity modifier (`text-gray-400/50`) composited against the background; suggests the nearest passing shade |
56
- | Touch target | 2.5.8 (AA) | Interactive elements under 24×24px |
57
+ | Touch target | 2.5.8 (AA) | Interactive elements under 24×24px — or 44×44px with `--strict` (2.5.5, AAA) |
57
58
  | Focus indicator | 2.4.7 (AA) | `focus:outline-none` with no visible replacement |
58
59
 
59
60
  ## Scope
package/dist/cli.js CHANGED
@@ -20,8 +20,10 @@ function formatViolation(v) {
20
20
  const base = `${v.line}: ${v.textClass} on ${v.bgClass} — ratio ${v.ratio.toFixed(2)}, needs ${v.required} (${v.level})`;
21
21
  return v.suggestion ? `${base}; try ${v.suggestion} (${v.suggestedRatio.toFixed(2)})` : base;
22
22
  }
23
- case "touch-target":
24
- return `${v.line}: <${v.tagName}> is ${v.widthPx}×${v.heightPx}px (${v.widthClass} ${v.heightClass}) WCAG 2.5.8 requires >= 24×24px`;
23
+ case "touch-target": {
24
+ const sc = v.level === "AAA" ? "2.5.5" : "2.5.8";
25
+ return `${v.line}: <${v.tagName}> is ${v.widthPx}×${v.heightPx}px (${v.widthClass} ${v.heightClass}) — WCAG ${sc} requires >= ${v.required}×${v.required}px`;
26
+ }
25
27
  case "focus-indicator":
26
28
  return `${v.line}: <${v.tagName}> removes the focus outline (${v.removalClass}) with no visible replacement (focus:ring-*/border-*/shadow-*/bg-*/outline-*)`;
27
29
  }
@@ -40,7 +42,7 @@ function groupByFile(items) {
40
42
  return byFile;
41
43
  }
42
44
  async function main() {
43
- const { help, version, verbose, config, configError: usageError, patterns } = parseArgs(process.argv.slice(2));
45
+ const { help, version, verbose, strict, config, configError: usageError, patterns } = parseArgs(process.argv.slice(2));
44
46
  if (help) {
45
47
  console.log(getHelpText());
46
48
  return;
@@ -77,7 +79,7 @@ async function main() {
77
79
  try {
78
80
  const code = readFileSync(absPath, "utf8");
79
81
  const contrastChecks = extractChecks(code, file);
80
- violations.push(...checkContrast(contrastChecks, palette), ...checkTouchTargets(extractTouchTargetChecks(code, file, spacing)), ...checkFocusIndicators(extractFocusIndicatorChecks(code, file)));
82
+ violations.push(...checkContrast(contrastChecks, palette), ...checkTouchTargets(extractTouchTargetChecks(code, file, spacing), strict), ...checkFocusIndicators(extractFocusIndicatorChecks(code, file)));
81
83
  if (verbose) {
82
84
  skips.push(...extractContrastSkips(code, file), ...checkContrastValueSkips(contrastChecks, palette), ...extractTouchTargetSkips(code, file, spacing));
83
85
  }
package/dist/cliArgs.d.ts CHANGED
@@ -2,6 +2,7 @@ export interface ParsedArgs {
2
2
  help: boolean;
3
3
  version: boolean;
4
4
  verbose: boolean;
5
+ strict: boolean;
5
6
  config: string | null;
6
7
  configError: string | null;
7
8
  patterns: string[];
package/dist/cliArgs.js CHANGED
@@ -9,6 +9,8 @@ Options:
9
9
  -v, --verbose Also report what couldn't be checked, and why
10
10
  -V, --version Print the version number
11
11
  -h, --help Print this help message
12
+ --strict Touch targets must meet WCAG 2.5.5 (AAA, 44x44px)
13
+ instead of the default 2.5.8 (AA, 24x24px)
12
14
  --config <path> Path to a tailwind.config.js/.cjs (v3) or a CSS
13
15
  @theme file like app/globals.css (v4) to read
14
16
  custom theme colors/spacing from (default:
@@ -18,6 +20,7 @@ Examples:
18
20
  tailwind-a11y Scan **/*.{jsx,tsx} from the current directory
19
21
  tailwind-a11y "src/**/*.tsx" Scan a custom glob pattern
20
22
  tailwind-a11y --verbose Also report skipped/unresolvable cases
23
+ tailwind-a11y --strict Enforce the stricter 44x44px touch target minimum
21
24
  tailwind-a11y --config ./tailwind.config.cjs
22
25
  `;
23
26
  export function getHelpText() {
@@ -25,7 +28,7 @@ export function getHelpText() {
25
28
  }
26
29
  // -v/--verbose already existed before --version was added; -V (uppercase)
27
30
  // avoids colliding with it, matching a common CLI convention.
28
- const FLAGS = new Set(["--verbose", "-v", "--version", "-V", "--help", "-h"]);
31
+ const FLAGS = new Set(["--verbose", "-v", "--version", "-V", "--help", "-h", "--strict"]);
29
32
  export function parseArgs(argv) {
30
33
  let config = null;
31
34
  let configError = null;
@@ -53,6 +56,7 @@ export function parseArgs(argv) {
53
56
  help: argv.includes("--help") || argv.includes("-h"),
54
57
  version: argv.includes("--version") || argv.includes("-V"),
55
58
  verbose: argv.includes("--verbose") || argv.includes("-v"),
59
+ strict: argv.includes("--strict"),
56
60
  config,
57
61
  configError,
58
62
  patterns,
@@ -8,5 +8,7 @@ export interface TouchTargetViolation {
8
8
  heightClass: string;
9
9
  widthPx: number;
10
10
  heightPx: number;
11
+ required: number;
12
+ level: "AA" | "AAA";
11
13
  }
12
- export declare function checkTouchTargets(checks: TouchTargetCheck[]): TouchTargetViolation[];
14
+ export declare function checkTouchTargets(checks: TouchTargetCheck[], strict?: boolean): TouchTargetViolation[];
@@ -1,8 +1,17 @@
1
1
  // WCAG 2.5.8 Target Size (Minimum), Level AA: interactive targets must be
2
2
  // at least 24x24 CSS pixels. "Minimum" is inclusive, so exactly 24x24 passes.
3
3
  const MIN_TARGET_PX = 24;
4
- export function checkTouchTargets(checks) {
4
+ // WCAG 2.5.5 Target Size (Enhanced), Level AAA: 44x44 CSS pixels -- opt-in
5
+ // via `strict`, not the default. Same "target in a sentence/text block"
6
+ // exemption as 2.5.8 (verified against the W3C Understanding doc), so
7
+ // extractTouchTargets.ts's isInlineInText() exemption logic applies
8
+ // unchanged to both thresholds -- this file only changes which number is
9
+ // compared against, not how targets are found or exempted.
10
+ const MIN_TARGET_PX_STRICT = 44;
11
+ export function checkTouchTargets(checks, strict = false) {
12
+ const required = strict ? MIN_TARGET_PX_STRICT : MIN_TARGET_PX;
13
+ const level = strict ? "AAA" : "AA";
5
14
  return checks
6
- .filter((c) => c.widthPx < MIN_TARGET_PX || c.heightPx < MIN_TARGET_PX)
7
- .map((c) => ({ type: "touch-target", ...c }));
15
+ .filter((c) => c.widthPx < required || c.heightPx < required)
16
+ .map((c) => ({ type: "touch-target", ...c, required, level }));
8
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tailwind-a11y",
3
- "version": "0.8.0",
3
+ "version": "0.9.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": {