diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 86b819b..dc43b17 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,6 +16,9 @@ or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any addi ## Table of Contents [Dev Environment](#dev-environment) +[Rule Factory System](#rule-factory-system) +[Creating New Rules](#creating-new-rules) +[Utility Functions](#utility-functions) [Pull requests](#pull-requests) ## Dev Environment @@ -61,6 +64,205 @@ To ensure a consistent and productive development environment, install the follo - [Prettier ESLint](https://marketplace.visualstudio.com/items?itemName=rvest.vs-code-prettier-eslint) — Format code with Prettier and ESLint integration. - [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint) — Linting and style checks for Markdown files. +## Rule Factory System + +This plugin uses a powerful rule factory system that provides consistent behavior across accessibility rules. The factory system is built around the `ruleFactory` function in `lib/util/ruleFactory.ts` and several utility functions for validating accessible labeling. + +### Core Concept + +The rule factory centralizes common accessibility validation patterns, making it easy to create new rules with consistent behavior. Instead of implementing validation logic from scratch, rules can leverage the factory's built-in utilities. + +### Architecture + +```sh +ruleFactory(config) → ESLint Rule + ↓ +hasAccessibleLabel(opening, element, context, config) → boolean + ↓ +Utility Functions: +├── hasAssociatedLabelViaAriaLabelledBy(opening, context) +├── hasAssociatedLabelViaHtmlFor(opening, context) +├── hasAssociatedLabelViaAriaDescribedby(opening, context) +├── hasLabeledChild(opening, context) +├── hasTextContentChild(element) +└── isInsideLabelTag(context) +``` + +## Creating New Rules + +### Using the Rule Factory + +For most accessibility rules, use the rule factory: + +```typescript +import { ruleFactory, LabeledControlConfig } from '../util/ruleFactory'; + +const rule = ruleFactory({ + component: 'YourComponent', // string or regex pattern + message: 'YourComponent needs accessible labeling', + + // Validation options (all optional, default false) + allowTextContentChild: true, // Allow text content in children + allowLabeledChild: true, // Allow images with alt, icons, etc. + allowHtmlFor: true, // Allow htmlFor/id label association + allowLabelledBy: true, // Allow aria-labelledby + allowDescribedBy: false, // Allow aria-describedby (discouraged as primary) + allowWrappingLabel: true, // Allow wrapping in