Skip to content

a11y.label missing control

github-actions[bot] edited this page Sep 6, 2026 · 2 revisions

a11y.label-missing-control

Rule ID: a11y.label-missing-control Severity: ERROR Category: a11y Target Standards: WCAG 2.2 Success Criterion 1.3.1 (Info and Relationships - Level A), W3C WAI-ARIA Authoring Practices (Form Labeling), HTML5 Specification (Section 4.10.4 The label element)


1. Overview & Core Invariant

Ensures label htmlFor attributes match an existing input control ID in the same document (WCAG 1.3.1)

Core Invariant:

"Every declaring an 'htmlFor' (or 'for') attribute must match the 'id' of an existing element in the same file."


2. Technical Grounding & Engine Realities

HTML form labels use the htmlFor (or for) attribute to create a programmatic bond between text instructions and form controls.

When developers introduce typos (e.g. htmlFor="user_id" vs <input id="userId">) or delete inputs without updating labels:

  1. Broken Click Targets: Clicking the label fails to focus or toggle the control, frustrating desktop and mobile touch users alike.
  2. Screen Reader Disconnect: Screen readers read the label as isolated static text, leaving the actual input unannounced and unlabelled.
  3. Linter False Negatives: Conventional linters only check that htmlFor exists as a string, completely ignoring that the target ID is missing.

Charites traverses the document AST symbol table to confirm that every declared htmlFor target actually exists.


3. Vulnerability & Risk Taxonomy

Risk Vector Severity Impact
Broken Label Association HIGH Screen reader users receive unlabelled inputs; clicking labels fails to focus controls.
WCAG 1.3.1 Non-Compliance HIGH Critical Level A accessibility non-compliance.

4. Non-Compliant Code Patterns (Bad Examples)

TSX (Typo in htmlFor target (user_id vs userId)):

<label htmlFor="user_id">ID Pengguna</label><input id="userId" className="border px-3 py-2" />

ASTRO (Label referencing non-existent control ID):

<label for="missing-input">Nama Lengkap</label><input id="fullname" class="border p-2" />

5. Compliant Implementation Patterns (Good Examples)

TSX (Matching htmlFor and input id):

<label htmlFor="userId">ID Pengguna</label><input id="userId" className="border px-3 py-2" />

ASTRO (Accurate for reference matching input element):

<label for="fullname">Nama Lengkap</label><input id="fullname" class="border p-2" />

6. Detection & Verification Pipeline (How The Rule Evaluates Code)

This a11y rule evaluates template markup and cross-references resolved design tokens for accessibility:

flowchart TD
    Node["AST Node (Astro / TSX element)"] --> Inspect["1. Inspect Interactive & Semantic Attributes"]
    Inspect --> BindCheck["2. Cross-Reference Form IDs / Labels / ARIA"]
    Inspect --> ContrastCheck["3. Query Token Graph for Foreground & Background Color"]
    ContrastCheck --> Luminance["4. Compute WCAG 2.2 Contrast Ratio"]
    BindCheck --> Invariant{"5. Validate Accessibility Invariant"}
    Luminance --> Invariant
    Invariant -- "Compliant" --> Safe["Pass"]
    Invariant -- "Violation" --> IgnoreCheck{"6. Check charites:ignore directive"}
    IgnoreCheck -- "Ignored" --> Safe
    IgnoreCheck -- "Not Ignored" --> Diag["7. Emit Diagnostic: a11y.label-missing-control"]
Loading

Step-by-Step Evaluation:

  1. AST Traversal: Inspects semantic elements, form controls, images, and landmark containers.
  2. Structural & Attribute Invariant Check: Verifies accessible names, heading hierarchies, label/input bindings, and positive tabindex avoidance.
  3. Token-Aware Contrast Verification: If analyzing color classes, queries token.Context to resolve foreground and background values, calculating relative luminance ratios.
  4. Directive Suppression Check: Inspects preceding comments for charites:ignore a11y.label-missing-control.
  5. Diagnostic Emission: Emits WCAG-grounded diagnostics for non-compliant patterns.

7. Verification & Test Harness (How The Test Works: 1-SSOT Tri-Corpus)

This rule is rigorously tested and validated across the canonical 1-SSOT Tri-Corpus in tests/correctness/a11y.label-missing-control/:

flowchart TD
    subgraph GoldenCorpus ["1-SSOT Tri-Corpus Test Matrix for a11y.label-missing-control"]
        subgraph P ["Positive Corpus (tests/correctness/a11y.label-missing-control/positive/)"]
            P1["P1: Obvious Direct Violation"]
            P2["P2: Indirect / Variant Concatenation"]
            P3["P3: Helper / clsx / cn Wrapper"]
            P4["P4: Deeply Nested Elements"]
            P5["P5: Aliased Imports / Re-exports"]
        end
        subgraph N ["Negative Corpus (tests/correctness/a11y.label-missing-control/negative/)"]
            N1["N1: Valid Design Tokens"]
            N2["N2: Explicit charites:ignore Directive"]
            N3["N3: Third-Party / Vendor Components"]
            N4["N4: Clean Semantic HTML"]
            N5["N5: Untokenized Custom Values (Banana Test)"]
        end
        subgraph A ["Adversarial Corpus (tests/correctness/a11y.label-missing-control/adversarial/)"]
            A1["A1: Template Literal Interpolations"]
            A2["A2: Ternary Conditional Expressions"]
            A3["A3: Spread Properties & Dynamic Overrides"]
            A4["A4: Dynamic Object Class Syntax"]
            A5["A5: Shadowed Variable Identifiers"]
            A6["A6: Nested Closures & HOC Wrappers"]
            A7["A7: Obfuscated Classes & Cyclic Tokens"]
        end
    end

    P --> TestRunner["Automated Runner (rule_test.go)"]
    N --> TestRunner
    A --> TestRunner
    TestRunner --> Gates["Quality Gates: Zero Panic, Zero False-Positive, Zero Bypass"]
Loading
  • Positive Fixtures (P1-P5): Verified to trigger diagnostics at exact lines and column spans.
  • Negative Fixtures (N1-N5): Verified to produce zero diagnostics on valid tokens and legitimate exemptions.
  • Adversarial Fixtures (A1-A7): Verified to prevent evasion across dynamic expressions, string interpolations, and cyclic references.

8. How to Suppress (Ignore Directives)

If this pattern is required for an intentional exception, suppress the diagnostic using the canonical Charites Rule ID:

<!-- charites:ignore a11y.label-missing-control intentional exception -->
// charites:ignore a11y.label-missing-control intentional exception

9. Configuration Reference (charites.yaml)

rules:
  a11y.label-missing-control:
    severity: error # error | warn | info | off

Rule Categories

A11y (16 rules)
Browser (12 rules)
Cls (16 rules)
Design (1 rules)
Ergonomy (5 rules)
Inp (16 rules)
Lcp (16 rules)
Mobile (5 rules)
Performance (16 rules)
Pwa (10 rules)
Responsive (18 rules)
Semantic (1 rules)
Theme (32 rules)
Ux (20 rules)

Clone this wiki locally