[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71HFJC8F79WPVGPF2HYEP4S":3,"skill-same:01M71HFJC8F79WPVGPF2HYEP4S":80},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":38,"linkOnly":39,"markdownContent":40,"markdownBody":41,"frontMatter":42,"refName":43,"commitSha":44,"skillDirectory":45,"forksCount":46,"repositoryHtmlUrl":47,"repositoryHomepageUrl":6,"ownerHtmlUrl":48,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":49,"install":50,"resources":63},"01M71HFJC8F79WPVGPF2HYEP4S","form-accessibility",null,"WCAG 2.2 AA compliance for forms, ARIA patterns, focus management, keyboard navigation, and screen reader support. Use when implementing accessible forms in any framework. The compliance foundation that ensures forms work for everyone.","JSGforever\u002Fskillguard","JSGforever","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F85541948?v=4","01M71FQD3E132VMR5F4TAYX3YS","01M71FQD4DYW0XJ4XQ4YQH75Y9",2,"GPL-3.0","https:\u002F\u002Fgithub.com\u002FJSGforever\u002Fskillguard\u002Fblob\u002Fmain\u002Fbenchmarks\u002Ffinal\u002Fskills\u002Fasset1__bbeierle12--form-accessibility\u002FSKILL.md",[],[18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"form","accessibility","form accessibility","reader","compliance","foundation","WCAG","use","for","framework","patterns","forms","aria","screen","support","focus","work","keyboard","when","implementing","2026-11-10 10:12:30",false,"---\nname: form-accessibility\ndescription: WCAG 2.2 AA compliance for forms, ARIA patterns, focus management, keyboard navigation, and screen reader support. Use when implementing accessible forms in any framework. The compliance foundation that ensures forms work for everyone.\n---\n\n# Form Accessibility\n\nWCAG 2.2 AA compliance patterns for forms. Ensures forms work for keyboard users, screen reader users, and users with cognitive or motor disabilities.\n\n## Quick Start\n\n```tsx\n\u002F\u002F Accessible form field pattern\n\u003Cdiv className=\"form-field\">\n  {\u002F* 1. Visible label (never placeholder-only) *\u002F}\n  \u003Clabel htmlFor=\"email\">\n    Email\n    \u003Cspan className=\"required\" aria-hidden=\"true\">*\u003C\u002Fspan>\n  \u003C\u002Flabel>\n  \n  {\u002F* 2. Hint text (separate from label) *\u002F}\n  \u003Cspan id=\"email-hint\" className=\"hint\">\n    We'll send your confirmation here\n  \u003C\u002Fspan>\n  \n  {\u002F* 3. Input with full ARIA binding *\u002F}\n  \u003Cinput\n    id=\"email\"\n    type=\"email\"\n    autoComplete=\"email\"\n    aria-required=\"true\"\n    aria-invalid={hasError}\n    aria-describedby={hasError ? \"email-error email-hint\" : \"email-hint\"}\n  \u002F>\n  \n  {\u002F* 4. Error message (announced by screen readers) *\u002F}\n  {hasError && (\n    \u003Cspan id=\"email-error\" className=\"error\" role=\"alert\">\n      Please enter a valid email address\n    \u003C\u002Fspan>\n  )}\n\u003C\u002Fdiv>\n```\n\n## WCAG 2.2 Form Requirements\n\n### Critical Criteria\n\n| Criterion | Level | Requirement | Implementation |\n|-----------|-------|-------------|----------------|\n| 1.3.1 Info & Relationships | A | Structure conveyed programmatically | `\u003Clabel>`, `\u003Cfieldset>`, `aria-describedby` |\n| 1.3.5 Identify Input Purpose | AA | Input purpose identifiable | `autocomplete` attributes |\n| 2.1.1 Keyboard | A | All functionality via keyboard | Tab order, focus management |\n| 2.4.6 Headings & Labels | AA | Labels describe purpose | Descriptive, visible labels |\n| 2.4.11 Focus Not Obscured | AA | Focus not hidden by other content | Scroll behavior, sticky elements |\n| 2.5.8 Target Size | AA | 24×24px minimum touch target | Button\u002Finput sizing |\n| 3.3.1 Error Identification | A | Errors identified and described | `aria-invalid`, error messages |\n| 3.3.2 Labels or Instructions | A | Labels provided | Visible labels, not just placeholders |\n| 3.3.3 Error Suggestion | AA | Suggestions for fixing errors | Actionable error messages |\n| 3.3.7 Redundant Entry | A | Don't re-ask for info already provided | Form state management |\n| 3.3.8 Accessible Authentication | AA | No cognitive function tests | No CAPTCHAs requiring text recognition |\n\n### New in WCAG 2.2 (October 2023)\n\n**2.4.11 Focus Not Obscured (AA)**\n```css\n\u002F* Ensure focus is never hidden by sticky headers *\u002F\n.sticky-header {\n  position: sticky;\n  top: 0;\n}\n\ninput:focus {\n  \u002F* Browser should scroll input into view above sticky elements *\u002F\n  scroll-margin-top: 80px; \u002F* Height of sticky header *\u002F\n}\n```\n\n**2.5.8 Target Size (AA)**\n```css\n\u002F* Minimum 24×24px touch targets *\u002F\nbutton,\ninput[type=\"submit\"],\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  min-width: 24px;\n  min-height: 24px;\n}\n\n\u002F* Better: 44×44px for comfortable touch *\u002F\n.touch-friendly {\n  min-width: 44px;\n  min-height: 44px;\n}\n```\n\n**3.3.7 Redundant Entry (A)**\n```tsx\n\u002F\u002F ❌ BAD: Asking for email twice\n\u003Cinput name=\"email\" \u002F>\n\u003Cinput name=\"confirmEmail\" \u002F>\n\n\u002F\u002F ✅ GOOD: Ask once, show confirmation\n\u003Cinput name=\"email\" \u002F>\n\u003Cp>Confirmation will be sent to: {email}\u003C\u002Fp>\n```\n\n**3.3.8 Accessible Authentication (AA)**\n```tsx\n\u002F\u002F ❌ BAD: CAPTCHA requiring text recognition\n\u003Cimg src=\"captcha.png\" alt=\"Enter the text shown\" \u002F>\n\n\u002F\u002F ✅ GOOD: Alternative verification methods\n\u003Cbutton type=\"button\" onClick={sendVerificationEmail}>\n  Send verification code to email\n\u003C\u002Fbutton>\n```\n\n## ARIA Patterns\n\n### Error Message Binding\n\n```tsx\n\u002F\u002F Pattern: aria-describedby links input to error\n\u003Cinput\n  id=\"email\"\n  aria-invalid={hasError ? \"true\" : \"false\"}\n  aria-describedby={hasError ? \"email-error\" : undefined}\n\u002F>\n\n{hasError && (\n  \u003Cspan id=\"email-error\" role=\"alert\">\n    {errorMessage}\n  \u003C\u002Fspan>\n)}\n```\n\n### Multiple Descriptions\n\n```tsx\n\u002F\u002F Pattern: Combine hint + error in aria-describedby\n\u003Cinput\n  id=\"password\"\n  aria-describedby={[\n    \"password-hint\",\n    hasError && \"password-error\"\n  ].filter(Boolean).join(\" \")}\n\u002F>\n\n\u003Cspan id=\"password-hint\">Must be at least 8 characters\u003C\u002Fspan>\n{hasError && \u003Cspan id=\"password-error\" role=\"alert\">{error}\u003C\u002Fspan>}\n```\n\n### Required Fields\n\n```tsx\n\u002F\u002F Pattern: Announce required status\n\u003Clabel htmlFor=\"name\">\n  Name\n  \u003Cspan className=\"required\" aria-hidden=\"true\">*\u003C\u002Fspan>\n  {\u002F* Visual indicator hidden from SR, aria-required announces it *\u002F}\n\u003C\u002Flabel>\n\n\u003Cinput\n  id=\"name\"\n  aria-required=\"true\"\n\u002F>\n\n\u002F\u002F Alternative: Required in label (simpler)\n\u003Clabel htmlFor=\"name\">Name (required)\u003C\u002Flabel>\n\u003Cinput id=\"name\" required \u002F>\n```\n\n### Field Groups\n\n```tsx\n\u002F\u002F Pattern: fieldset + legend for related fields\n\u003Cfieldset>\n  \u003Clegend>Shipping Address\u003C\u002Flegend>\n  \n  \u003Clabel htmlFor=\"street\">Street\u003C\u002Flabel>\n  \u003Cinput id=\"street\" autoComplete=\"street-address\" \u002F>\n  \n  \u003Clabel htmlFor=\"city\">City\u003C\u002Flabel>\n  \u003Cinput id=\"city\" autoComplete=\"address-level2\" \u002F>\n\u003C\u002Ffieldset>\n```\n\n### Radio\u002FCheckbox Groups\n\n```tsx\n\u002F\u002F Pattern: fieldset groups options, legend is the question\n\u003Cfieldset>\n  \u003Clegend>Preferred contact method\u003C\u002Flegend>\n  \n  \u003Clabel>\n    \u003Cinput type=\"radio\" name=\"contact\" value=\"email\" \u002F>\n    Email\n  \u003C\u002Flabel>\n  \n  \u003Clabel>\n    \u003Cinput type=\"radio\" name=\"contact\" value=\"phone\" \u002F>\n    Phone\n  \u003C\u002Flabel>\n\u003C\u002Ffieldset>\n```\n\n## Focus Management\n\n### Focus on First Error\n\n```tsx\n\u002F\u002F On form submit with errors, focus first invalid field\nfunction handleSubmit(e: FormEvent) {\n  e.preventDefault();\n  \n  const firstError = formRef.current?.querySelector('[aria-invalid=\"true\"]');\n  if (firstError) {\n    (firstError as HTMLElement).focus();\n    return;\n  }\n  \n  \u002F\u002F Submit if valid\n  submitForm();\n}\n```\n\n### Focus on Step Change (Multi-step)\n\n```tsx\n\u002F\u002F Move focus to step heading when changing steps\nfunction goToStep(stepNumber: number) {\n  setCurrentStep(stepNumber);\n  \n  \u002F\u002F Wait for render, then focus\n  requestAnimationFrame(() => {\n    const heading = document.getElementById(`step-${stepNumber}-heading`);\n    heading?.focus();\n  });\n}\n\n\u002F\u002F Heading must be focusable\n\u003Ch2 id=\"step-2-heading\" tabIndex={-1}>Shipping Address\u003C\u002Fh2>\n```\n\n### Skip Links\n\n```tsx\n\u002F\u002F Allow skipping to form\n\u003Ca href=\"#main-form\" className=\"skip-link\">\n  Skip to form\n\u003C\u002Fa>\n\n\u003Cform id=\"main-form\">\n  {\u002F* Form content *\u002F}\n\u003C\u002Fform>\n\n\u002F\u002F CSS for skip link\n.skip-link {\n  position: absolute;\n  top: -40px;\n  left: 0;\n  z-index: 100;\n}\n\n.skip-link:focus {\n  top: 0;\n}\n```\n\n### Focus Trap (Modals)\n\n```tsx\n\u002F\u002F Keep focus within modal form\nfunction FocusTrap({ children }) {\n  const trapRef = useRef\u003CHTMLDivElement>(null);\n  \n  useEffect(() => {\n    const trap = trapRef.current;\n    if (!trap) return;\n    \n    const focusableElements = trap.querySelectorAll(\n      'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n    );\n    const firstElement = focusableElements[0] as HTMLElement;\n    const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement;\n    \n    function handleKeyDown(e: KeyboardEvent) {\n      if (e.key !== 'Tab') return;\n      \n      if (e.shiftKey && document.activeElement === firstElement) {\n        e.preventDefault();\n        lastElement.focus();\n      } else if (!e.shiftKey && document.activeElement === lastElement) {\n        e.preventDefault();\n        firstElement.focus();\n      }\n    }\n    \n    trap.addEventListener('keydown', handleKeyDown);\n    firstElement?.focus();\n    \n    return () => trap.removeEventListener('keydown', handleKeyDown);\n  }, []);\n  \n  return \u003Cdiv ref={trapRef}>{children}\u003C\u002Fdiv>;\n}\n```\n\n## Color & Contrast\n\n### Error States (Colorblind-Safe)\n\n```css\n\u002F* ❌ BAD: Color only *\u002F\n.error {\n  border-color: red;\n}\n\n\u002F* ✅ GOOD: Color + icon + text *\u002F\n.field-error {\n  border-color: #dc2626;\n  border-width: 2px;\n}\n\n.field-error::after {\n  content: \"\";\n  background-image: url(\"data:image\u002Fsvg+xml,...\"); \u002F* Error icon *\u002F\n}\n\n.error-message {\n  color: #dc2626;\n  font-weight: 500;\n}\n\n.error-message::before {\n  content: \"⚠ \"; \u002F* Text indicator *\u002F\n}\n```\n\n### Focus Indicators\n\n```css\n\u002F* Focus must have 3:1 contrast ratio *\u002F\ninput:focus {\n  outline: 2px solid #2563eb;\n  outline-offset: 2px;\n}\n\n\u002F* For dark backgrounds *\u002F\ninput:focus {\n  outline: 2px solid #60a5fa;\n  outline-offset: 2px;\n}\n\n\u002F* Never remove outline without replacement *\u002F\n\u002F* ❌ BAD *\u002F\ninput:focus {\n  outline: none;\n}\n\n\u002F* ✅ GOOD: Custom focus style *\u002F\ninput:focus {\n  outline: none;\n  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.5);\n}\n```\n\n### Validation States (Colorblind-Friendly)\n\n```tsx\n\u002F\u002F Use icons + text, not just color\nfunction ValidationIndicator({ state }: { state: 'valid' | 'invalid' | 'idle' }) {\n  if (state === 'idle') return null;\n  \n  return (\n    \u003Cspan className={`indicator ${state}`} aria-hidden=\"true\">\n      {state === 'valid' && '✓'}\n      {state === 'invalid' && '✗'}\n    \u003C\u002Fspan>\n  );\n}\n```\n\n## Keyboard Navigation\n\n### Tab Order\n\n```tsx\n\u002F\u002F Natural tab order (no positive tabindex needed)\n\u002F\u002F ❌ BAD: Manual tab order\n\u003Cinput tabIndex={2} \u002F>\n\u003Cinput tabIndex={1} \u002F>\n\u003Cinput tabIndex={3} \u002F>\n\n\u002F\u002F ✅ GOOD: Natural DOM order\n\u003Cinput \u002F> {\u002F* tabIndex implicitly 0 *\u002F}\n\u003Cinput \u002F>\n\u003Cinput \u002F>\n```\n\n### Escape Key Handling\n\n```tsx\n\u002F\u002F Allow Escape to close dropdowns, cancel modals\nfunction Modal({ onClose, children }) {\n  useEffect(() => {\n    function handleEscape(e: KeyboardEvent) {\n      if (e.key === 'Escape') {\n        onClose();\n      }\n    }\n    \n    document.addEventListener('keydown', handleEscape);\n    return () => document.removeEventListener('keydown', handleEscape);\n  }, [onClose]);\n  \n  return \u003Cdiv role=\"dialog\" aria-modal=\"true\">{children}\u003C\u002Fdiv>;\n}\n```\n\n### Enter to Submit\n\n```tsx\n\u002F\u002F Forms submit on Enter by default\n\u002F\u002F For buttons that shouldn't submit:\n\u003Cbutton type=\"button\" onClick={handleAction}>\n  Add Item\n\u003C\u002Fbutton>\n\n\u002F\u002F For preventing Enter submit on specific fields:\n\u003Cinput\n  onKeyDown={(e) => {\n    if (e.key === 'Enter') {\n      e.preventDefault();\n      \u002F\u002F Do something else\n    }\n  }}\n\u002F>\n```\n\n## Live Regions\n\n### Error Announcements\n\n```tsx\n\u002F\u002F Announce errors when they appear\n\u003Cdiv aria-live=\"polite\" aria-atomic=\"true\" className=\"sr-only\">\n  {errorCount > 0 && `${errorCount} errors in form`}\n\u003C\u002Fdiv>\n\n\u002F\u002F Or use role=\"alert\" for immediate announcement\n{hasError && (\n  \u003Cspan role=\"alert\">{errorMessage}\u003C\u002Fspan>\n)}\n```\n\n### Loading States\n\n```tsx\n\u002F\u002F Announce loading state\n\u003Cbutton type=\"submit\" disabled={isLoading}>\n  {isLoading ? (\n    \u003C>\n      \u003Cspan aria-hidden=\"true\">Loading...\u003C\u002Fspan>\n      \u003Cspan className=\"sr-only\">Submitting form, please wait\u003C\u002Fspan>\n    \u003C\u002F>\n  ) : (\n    'Submit'\n  )}\n\u003C\u002Fbutton>\n\n\u002F\u002F Or use aria-busy\n\u003Cform aria-busy={isLoading}>\n  {\u002F* ... *\u002F}\n\u003C\u002Fform>\n```\n\n### Success Messages\n\n```tsx\n\u002F\u002F Announce successful submission\n{isSuccess && (\n  \u003Cdiv role=\"status\" aria-live=\"polite\">\n    Form submitted successfully!\n  \u003C\u002Fdiv>\n)}\n```\n\n## Screen Reader Only Content\n\n```css\n\u002F* Visually hidden but announced by screen readers *\u002F\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border: 0;\n}\n\n\u002F* Allow focus for skip links *\u002F\n.sr-only-focusable:focus {\n  position: static;\n  width: auto;\n  height: auto;\n  overflow: visible;\n  clip: auto;\n  white-space: normal;\n}\n```\n\n## Testing Accessibility\n\n### Automated Tools\n\n```bash\n# axe-core (recommended)\nnpm install @axe-core\u002Freact\n\n# In development\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport axe from '@axe-core\u002Freact';\n\nif (process.env.NODE_ENV !== 'production') {\n  axe(React, ReactDOM, 1000);\n}\n```\n\n### Manual Testing Checklist\n\n1. **Keyboard only**: Can you complete the form using only Tab, Enter, Space, and Arrow keys?\n2. **Screen reader**: Does VoiceOver\u002FNVDA announce labels, errors, and required status?\n3. **Zoom 200%**: Is the form usable at 200% browser zoom?\n4. **High contrast**: Is everything visible in Windows High Contrast mode?\n5. **Focus visible**: Can you always see which element is focused?\n\n### Testing Script\n\n```typescript\n\u002F\u002F Automated accessibility test\nimport { axe, toHaveNoViolations } from 'jest-axe';\n\nexpect.extend(toHaveNoViolations);\n\ntest('form is accessible', async () => {\n  const { container } = render(\u003CLoginForm \u002F>);\n  const results = await axe(container);\n  expect(results).toHaveNoViolations();\n});\n\ntest('error state is accessible', async () => {\n  const { container } = render(\u003CLoginForm \u002F>);\n  \n  \u002F\u002F Trigger error\n  fireEvent.blur(screen.getByLabelText(\u002Femail\u002Fi));\n  \n  const results = await axe(container);\n  expect(results).toHaveNoViolations();\n});\n```\n\n## File Structure\n\n```\nform-accessibility\u002F\n├── SKILL.md\n├── references\u002F\n│   ├── wcag-2.2-forms.md       # Full WCAG criteria breakdown\n│   └── aria-patterns.md        # Complete ARIA reference\n└── scripts\u002F\n    ├── aria-form-wrapper.tsx   # Automatic ARIA binding\n    ├── focus-manager.ts        # Focus trap, error focus\n    ├── error-announcer.ts      # Live region management\n    └── accessibility-validator.ts  # Runtime a11y checks\n```\n\n## Reference\n\n- `references\u002Fwcag-2.2-forms.md` — Complete WCAG 2.2 criteria for forms\n- `references\u002Faria-patterns.md` — Detailed ARIA implementation patterns\n","# Form Accessibility\n\nWCAG 2.2 AA compliance patterns for forms. Ensures forms work for keyboard users, screen reader users, and users with cognitive or motor disabilities.\n\n## Quick Start\n\n```tsx\n\u002F\u002F Accessible form field pattern\n\u003Cdiv className=\"form-field\">\n  {\u002F* 1. Visible label (never placeholder-only) *\u002F}\n  \u003Clabel htmlFor=\"email\">\n    Email\n    \u003Cspan className=\"required\" aria-hidden=\"true\">*\u003C\u002Fspan>\n  \u003C\u002Flabel>\n  \n  {\u002F* 2. Hint text (separate from label) *\u002F}\n  \u003Cspan id=\"email-hint\" className=\"hint\">\n    We'll send your confirmation here\n  \u003C\u002Fspan>\n  \n  {\u002F* 3. Input with full ARIA binding *\u002F}\n  \u003Cinput\n    id=\"email\"\n    type=\"email\"\n    autoComplete=\"email\"\n    aria-required=\"true\"\n    aria-invalid={hasError}\n    aria-describedby={hasError ? \"email-error email-hint\" : \"email-hint\"}\n  \u002F>\n  \n  {\u002F* 4. Error message (announced by screen readers) *\u002F}\n  {hasError && (\n    \u003Cspan id=\"email-error\" className=\"error\" role=\"alert\">\n      Please enter a valid email address\n    \u003C\u002Fspan>\n  )}\n\u003C\u002Fdiv>\n```\n\n## WCAG 2.2 Form Requirements\n\n### Critical Criteria\n\n| Criterion | Level | Requirement | Implementation |\n|-----------|-------|-------------|----------------|\n| 1.3.1 Info & Relationships | A | Structure conveyed programmatically | `\u003Clabel>`, `\u003Cfieldset>`, `aria-describedby` |\n| 1.3.5 Identify Input Purpose | AA | Input purpose identifiable | `autocomplete` attributes |\n| 2.1.1 Keyboard | A | All functionality via keyboard | Tab order, focus management |\n| 2.4.6 Headings & Labels | AA | Labels describe purpose | Descriptive, visible labels |\n| 2.4.11 Focus Not Obscured | AA | Focus not hidden by other content | Scroll behavior, sticky elements |\n| 2.5.8 Target Size | AA | 24×24px minimum touch target | Button\u002Finput sizing |\n| 3.3.1 Error Identification | A | Errors identified and described | `aria-invalid`, error messages |\n| 3.3.2 Labels or Instructions | A | Labels provided | Visible labels, not just placeholders |\n| 3.3.3 Error Suggestion | AA | Suggestions for fixing errors | Actionable error messages |\n| 3.3.7 Redundant Entry | A | Don't re-ask for info already provided | Form state management |\n| 3.3.8 Accessible Authentication | AA | No cognitive function tests | No CAPTCHAs requiring text recognition |\n\n### New in WCAG 2.2 (October 2023)\n\n**2.4.11 Focus Not Obscured (AA)**\n```css\n\u002F* Ensure focus is never hidden by sticky headers *\u002F\n.sticky-header {\n  position: sticky;\n  top: 0;\n}\n\ninput:focus {\n  \u002F* Browser should scroll input into view above sticky elements *\u002F\n  scroll-margin-top: 80px; \u002F* Height of sticky header *\u002F\n}\n```\n\n**2.5.8 Target Size (AA)**\n```css\n\u002F* Minimum 24×24px touch targets *\u002F\nbutton,\ninput[type=\"submit\"],\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  min-width: 24px;\n  min-height: 24px;\n}\n\n\u002F* Better: 44×44px for comfortable touch *\u002F\n.touch-friendly {\n  min-width: 44px;\n  min-height: 44px;\n}\n```\n\n**3.3.7 Redundant Entry (A)**\n```tsx\n\u002F\u002F ❌ BAD: Asking for email twice\n\u003Cinput name=\"email\" \u002F>\n\u003Cinput name=\"confirmEmail\" \u002F>\n\n\u002F\u002F ✅ GOOD: Ask once, show confirmation\n\u003Cinput name=\"email\" \u002F>\n\u003Cp>Confirmation will be sent to: {email}\u003C\u002Fp>\n```\n\n**3.3.8 Accessible Authentication (AA)**\n```tsx\n\u002F\u002F ❌ BAD: CAPTCHA requiring text recognition\n\u003Cimg src=\"captcha.png\" alt=\"Enter the text shown\" \u002F>\n\n\u002F\u002F ✅ GOOD: Alternative verification methods\n\u003Cbutton type=\"button\" onClick={sendVerificationEmail}>\n  Send verification code to email\n\u003C\u002Fbutton>\n```\n\n## ARIA Patterns\n\n### Error Message Binding\n\n```tsx\n\u002F\u002F Pattern: aria-describedby links input to error\n\u003Cinput\n  id=\"email\"\n  aria-invalid={hasError ? \"true\" : \"false\"}\n  aria-describedby={hasError ? \"email-error\" : undefined}\n\u002F>\n\n{hasError && (\n  \u003Cspan id=\"email-error\" role=\"alert\">\n    {errorMessage}\n  \u003C\u002Fspan>\n)}\n```\n\n### Multiple Descriptions\n\n```tsx\n\u002F\u002F Pattern: Combine hint + error in aria-describedby\n\u003Cinput\n  id=\"password\"\n  aria-describedby={[\n    \"password-hint\",\n    hasError && \"password-error\"\n  ].filter(Boolean).join(\" \")}\n\u002F>\n\n\u003Cspan id=\"password-hint\">Must be at least 8 characters\u003C\u002Fspan>\n{hasError && \u003Cspan id=\"password-error\" role=\"alert\">{error}\u003C\u002Fspan>}\n```\n\n### Required Fields\n\n```tsx\n\u002F\u002F Pattern: Announce required status\n\u003Clabel htmlFor=\"name\">\n  Name\n  \u003Cspan className=\"required\" aria-hidden=\"true\">*\u003C\u002Fspan>\n  {\u002F* Visual indicator hidden from SR, aria-required announces it *\u002F}\n\u003C\u002Flabel>\n\n\u003Cinput\n  id=\"name\"\n  aria-required=\"true\"\n\u002F>\n\n\u002F\u002F Alternative: Required in label (simpler)\n\u003Clabel htmlFor=\"name\">Name (required)\u003C\u002Flabel>\n\u003Cinput id=\"name\" required \u002F>\n```\n\n### Field Groups\n\n```tsx\n\u002F\u002F Pattern: fieldset + legend for related fields\n\u003Cfieldset>\n  \u003Clegend>Shipping Address\u003C\u002Flegend>\n  \n  \u003Clabel htmlFor=\"street\">Street\u003C\u002Flabel>\n  \u003Cinput id=\"street\" autoComplete=\"street-address\" \u002F>\n  \n  \u003Clabel htmlFor=\"city\">City\u003C\u002Flabel>\n  \u003Cinput id=\"city\" autoComplete=\"address-level2\" \u002F>\n\u003C\u002Ffieldset>\n```\n\n### Radio\u002FCheckbox Groups\n\n```tsx\n\u002F\u002F Pattern: fieldset groups options, legend is the question\n\u003Cfieldset>\n  \u003Clegend>Preferred contact method\u003C\u002Flegend>\n  \n  \u003Clabel>\n    \u003Cinput type=\"radio\" name=\"contact\" value=\"email\" \u002F>\n    Email\n  \u003C\u002Flabel>\n  \n  \u003Clabel>\n    \u003Cinput type=\"radio\" name=\"contact\" value=\"phone\" \u002F>\n    Phone\n  \u003C\u002Flabel>\n\u003C\u002Ffieldset>\n```\n\n## Focus Management\n\n### Focus on First Error\n\n```tsx\n\u002F\u002F On form submit with errors, focus first invalid field\nfunction handleSubmit(e: FormEvent) {\n  e.preventDefault();\n  \n  const firstError = formRef.current?.querySelector('[aria-invalid=\"true\"]');\n  if (firstError) {\n    (firstError as HTMLElement).focus();\n    return;\n  }\n  \n  \u002F\u002F Submit if valid\n  submitForm();\n}\n```\n\n### Focus on Step Change (Multi-step)\n\n```tsx\n\u002F\u002F Move focus to step heading when changing steps\nfunction goToStep(stepNumber: number) {\n  setCurrentStep(stepNumber);\n  \n  \u002F\u002F Wait for render, then focus\n  requestAnimationFrame(() => {\n    const heading = document.getElementById(`step-${stepNumber}-heading`);\n    heading?.focus();\n  });\n}\n\n\u002F\u002F Heading must be focusable\n\u003Ch2 id=\"step-2-heading\" tabIndex={-1}>Shipping Address\u003C\u002Fh2>\n```\n\n### Skip Links\n\n```tsx\n\u002F\u002F Allow skipping to form\n\u003Ca href=\"#main-form\" className=\"skip-link\">\n  Skip to form\n\u003C\u002Fa>\n\n\u003Cform id=\"main-form\">\n  {\u002F* Form content *\u002F}\n\u003C\u002Fform>\n\n\u002F\u002F CSS for skip link\n.skip-link {\n  position: absolute;\n  top: -40px;\n  left: 0;\n  z-index: 100;\n}\n\n.skip-link:focus {\n  top: 0;\n}\n```\n\n### Focus Trap (Modals)\n\n```tsx\n\u002F\u002F Keep focus within modal form\nfunction FocusTrap({ children }) {\n  const trapRef = useRef\u003CHTMLDivElement>(null);\n  \n  useEffect(() => {\n    const trap = trapRef.current;\n    if (!trap) return;\n    \n    const focusableElements = trap.querySelectorAll(\n      'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n    );\n    const firstElement = focusableElements[0] as HTMLElement;\n    const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement;\n    \n    function handleKeyDown(e: KeyboardEvent) {\n      if (e.key !== 'Tab') return;\n      \n      if (e.shiftKey && document.activeElement === firstElement) {\n        e.preventDefault();\n        lastElement.focus();\n      } else if (!e.shiftKey && document.activeElement === lastElement) {\n        e.preventDefault();\n        firstElement.focus();\n      }\n    }\n    \n    trap.addEventListener('keydown', handleKeyDown);\n    firstElement?.focus();\n    \n    return () => trap.removeEventListener('keydown', handleKeyDown);\n  }, []);\n  \n  return \u003Cdiv ref={trapRef}>{children}\u003C\u002Fdiv>;\n}\n```\n\n## Color & Contrast\n\n### Error States (Colorblind-Safe)\n\n```css\n\u002F* ❌ BAD: Color only *\u002F\n.error {\n  border-color: red;\n}\n\n\u002F* ✅ GOOD: Color + icon + text *\u002F\n.field-error {\n  border-color: #dc2626;\n  border-width: 2px;\n}\n\n.field-error::after {\n  content: \"\";\n  background-image: url(\"data:image\u002Fsvg+xml,...\"); \u002F* Error icon *\u002F\n}\n\n.error-message {\n  color: #dc2626;\n  font-weight: 500;\n}\n\n.error-message::before {\n  content: \"⚠ \"; \u002F* Text indicator *\u002F\n}\n```\n\n### Focus Indicators\n\n```css\n\u002F* Focus must have 3:1 contrast ratio *\u002F\ninput:focus {\n  outline: 2px solid #2563eb;\n  outline-offset: 2px;\n}\n\n\u002F* For dark backgrounds *\u002F\ninput:focus {\n  outline: 2px solid #60a5fa;\n  outline-offset: 2px;\n}\n\n\u002F* Never remove outline without replacement *\u002F\n\u002F* ❌ BAD *\u002F\ninput:focus {\n  outline: none;\n}\n\n\u002F* ✅ GOOD: Custom focus style *\u002F\ninput:focus {\n  outline: none;\n  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.5);\n}\n```\n\n### Validation States (Colorblind-Friendly)\n\n```tsx\n\u002F\u002F Use icons + text, not just color\nfunction ValidationIndicator({ state }: { state: 'valid' | 'invalid' | 'idle' }) {\n  if (state === 'idle') return null;\n  \n  return (\n    \u003Cspan className={`indicator ${state}`} aria-hidden=\"true\">\n      {state === 'valid' && '✓'}\n      {state === 'invalid' && '✗'}\n    \u003C\u002Fspan>\n  );\n}\n```\n\n## Keyboard Navigation\n\n### Tab Order\n\n```tsx\n\u002F\u002F Natural tab order (no positive tabindex needed)\n\u002F\u002F ❌ BAD: Manual tab order\n\u003Cinput tabIndex={2} \u002F>\n\u003Cinput tabIndex={1} \u002F>\n\u003Cinput tabIndex={3} \u002F>\n\n\u002F\u002F ✅ GOOD: Natural DOM order\n\u003Cinput \u002F> {\u002F* tabIndex implicitly 0 *\u002F}\n\u003Cinput \u002F>\n\u003Cinput \u002F>\n```\n\n### Escape Key Handling\n\n```tsx\n\u002F\u002F Allow Escape to close dropdowns, cancel modals\nfunction Modal({ onClose, children }) {\n  useEffect(() => {\n    function handleEscape(e: KeyboardEvent) {\n      if (e.key === 'Escape') {\n        onClose();\n      }\n    }\n    \n    document.addEventListener('keydown', handleEscape);\n    return () => document.removeEventListener('keydown', handleEscape);\n  }, [onClose]);\n  \n  return \u003Cdiv role=\"dialog\" aria-modal=\"true\">{children}\u003C\u002Fdiv>;\n}\n```\n\n### Enter to Submit\n\n```tsx\n\u002F\u002F Forms submit on Enter by default\n\u002F\u002F For buttons that shouldn't submit:\n\u003Cbutton type=\"button\" onClick={handleAction}>\n  Add Item\n\u003C\u002Fbutton>\n\n\u002F\u002F For preventing Enter submit on specific fields:\n\u003Cinput\n  onKeyDown={(e) => {\n    if (e.key === 'Enter') {\n      e.preventDefault();\n      \u002F\u002F Do something else\n    }\n  }}\n\u002F>\n```\n\n## Live Regions\n\n### Error Announcements\n\n```tsx\n\u002F\u002F Announce errors when they appear\n\u003Cdiv aria-live=\"polite\" aria-atomic=\"true\" className=\"sr-only\">\n  {errorCount > 0 && `${errorCount} errors in form`}\n\u003C\u002Fdiv>\n\n\u002F\u002F Or use role=\"alert\" for immediate announcement\n{hasError && (\n  \u003Cspan role=\"alert\">{errorMessage}\u003C\u002Fspan>\n)}\n```\n\n### Loading States\n\n```tsx\n\u002F\u002F Announce loading state\n\u003Cbutton type=\"submit\" disabled={isLoading}>\n  {isLoading ? (\n    \u003C>\n      \u003Cspan aria-hidden=\"true\">Loading...\u003C\u002Fspan>\n      \u003Cspan className=\"sr-only\">Submitting form, please wait\u003C\u002Fspan>\n    \u003C\u002F>\n  ) : (\n    'Submit'\n  )}\n\u003C\u002Fbutton>\n\n\u002F\u002F Or use aria-busy\n\u003Cform aria-busy={isLoading}>\n  {\u002F* ... *\u002F}\n\u003C\u002Fform>\n```\n\n### Success Messages\n\n```tsx\n\u002F\u002F Announce successful submission\n{isSuccess && (\n  \u003Cdiv role=\"status\" aria-live=\"polite\">\n    Form submitted successfully!\n  \u003C\u002Fdiv>\n)}\n```\n\n## Screen Reader Only Content\n\n```css\n\u002F* Visually hidden but announced by screen readers *\u002F\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border: 0;\n}\n\n\u002F* Allow focus for skip links *\u002F\n.sr-only-focusable:focus {\n  position: static;\n  width: auto;\n  height: auto;\n  overflow: visible;\n  clip: auto;\n  white-space: normal;\n}\n```\n\n## Testing Accessibility\n\n### Automated Tools\n\n```bash\n# axe-core (recommended)\nnpm install @axe-core\u002Freact\n\n# In development\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport axe from '@axe-core\u002Freact';\n\nif (process.env.NODE_ENV !== 'production') {\n  axe(React, ReactDOM, 1000);\n}\n```\n\n### Manual Testing Checklist\n\n1. **Keyboard only**: Can you complete the form using only Tab, Enter, Space, and Arrow keys?\n2. **Screen reader**: Does VoiceOver\u002FNVDA announce labels, errors, and required status?\n3. **Zoom 200%**: Is the form usable at 200% browser zoom?\n4. **High contrast**: Is everything visible in Windows High Contrast mode?\n5. **Focus visible**: Can you always see which element is focused?\n\n### Testing Script\n\n```typescript\n\u002F\u002F Automated accessibility test\nimport { axe, toHaveNoViolations } from 'jest-axe';\n\nexpect.extend(toHaveNoViolations);\n\ntest('form is accessible', async () => {\n  const { container } = render(\u003CLoginForm \u002F>);\n  const results = await axe(container);\n  expect(results).toHaveNoViolations();\n});\n\ntest('error state is accessible', async () => {\n  const { container } = render(\u003CLoginForm \u002F>);\n  \n  \u002F\u002F Trigger error\n  fireEvent.blur(screen.getByLabelText(\u002Femail\u002Fi));\n  \n  const results = await axe(container);\n  expect(results).toHaveNoViolations();\n});\n```\n\n## File Structure\n\n```\nform-accessibility\u002F\n├── SKILL.md\n├── references\u002F\n│   ├── wcag-2.2-forms.md       # Full WCAG criteria breakdown\n│   └── aria-patterns.md        # Complete ARIA reference\n└── scripts\u002F\n    ├── aria-form-wrapper.tsx   # Automatic ARIA binding\n    ├── focus-manager.ts        # Focus trap, error focus\n    ├── error-announcer.ts      # Live region management\n    └── accessibility-validator.ts  # Runtime a11y checks\n```\n\n## Reference\n\n- `references\u002Fwcag-2.2-forms.md` — Complete WCAG 2.2 criteria for forms\n- `references\u002Faria-patterns.md` — Detailed ARIA implementation patterns\n",{"name":5,"description":7},"main","f2ec4b3b9133d34eeaf9b38e4b0a97e8e5705a7f","benchmarks\u002Ffinal\u002Fskills\u002Fasset1__bbeierle12--form-accessibility",0,"https:\u002F\u002Fgithub.com\u002FJSGforever\u002Fskillguard","https:\u002F\u002Fgithub.com\u002FJSGforever","2026-08-29 18:27:56",{"source":8,"skillName":5,"commands":51,"prompt":61,"zipAvailable":62},[52,55,58],{"tool":53,"command":54},"npx","npx skills add JSGforever\u002Fskillguard --skill form-accessibility",{"tool":56,"command":57},"bunx","bunx skills add JSGforever\u002Fskillguard --skill form-accessibility",{"tool":59,"command":60},"pnpm","pnpm dlx skills add JSGforever\u002Fskillguard --skill form-accessibility","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FJSGforever\u002Fskillguard\u002Fblob\u002Fmain\u002Fbenchmarks\u002Ffinal\u002Fskills\u002Fasset1__bbeierle12--form-accessibility\u002FSKILL.md",true,[64,70,76],{"path":65,"resourceType":66,"resourceTypeDesc":67,"sizeBytes":68,"sourceHtmlUrl":69},"_meta.json","REFERENCE","参考文档",477,"https:\u002F\u002Fgithub.com\u002FJSGforever\u002Fskillguard\u002Fblob\u002Fmain\u002Fbenchmarks\u002Ffinal\u002Fskills\u002Fasset1__bbeierle12--form-accessibility\u002F_meta.json",{"path":71,"resourceType":72,"resourceTypeDesc":73,"sizeBytes":74,"sourceHtmlUrl":75},"scripts\u002Faria-form-wrapper.tsx","SCRIPT","脚本",17488,"https:\u002F\u002Fgithub.com\u002FJSGforever\u002Fskillguard\u002Fblob\u002Fmain\u002Fbenchmarks\u002Ffinal\u002Fskills\u002Fasset1__bbeierle12--form-accessibility\u002Fscripts\u002Faria-form-wrapper.tsx",{"path":77,"resourceType":72,"resourceTypeDesc":73,"sizeBytes":78,"sourceHtmlUrl":79},"scripts\u002Ffocus-manager.ts",13389,"https:\u002F\u002Fgithub.com\u002FJSGforever\u002Fskillguard\u002Fblob\u002Fmain\u002Fbenchmarks\u002Ffinal\u002Fskills\u002Fasset1__bbeierle12--form-accessibility\u002Fscripts\u002Ffocus-manager.ts",[81,112,128,155,177,198],{"skillNo":82,"name":83,"nameZh":6,"description":84,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":85,"categories":86,"keywords":92,"publishedAt":38},"01M71HY2SNXBA9Q00WZDTZQ31Z","skillguard-codex-scanner","Hybrid deterministic+LLM agent-skill security scanner. Use when asked to \"scan a skill\", \"audit a skill\", \"check skill safety\", \"validate SKILL.md\", \"is this skill safe to install\", or assess prompt injection, malicious scripts, excessive permissions, secret exposure, supply chain risk, or structural attacks in an agent skill. Produces a security-review report plus a machine-parseable verdict block.","https:\u002F\u002Fgithub.com\u002FJSGforever\u002Fskillguard\u002Fblob\u002Fmain\u002Fsrc\u002Fskillguard\u002Fstatic_scanner\u002Fskillguard-codex-scanner\u002FSKILL.md",[87],{"categoryNo":88,"categoryName":89,"taxonomyType":90,"taxonomyTypeDesc":91},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[93,94,95,96,97,98,99,100,101,102,103,104,36,105,106,107,108,109,110,111],"scanner","skillguard codex scanner","skillguard","codex","chain","skill security","security-review","review report","structural","secret","prompt injection","security scanner","review","malicious","permissions","exposure","audit","scripts","safe",{"skillNo":113,"name":114,"nameZh":6,"description":115,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":116,"licenseFileUrl":6,"sourceHtmlUrl":117,"categories":118,"keywords":124,"publishedAt":38},"01M71HY0SPKTT3S27BC2HPAC5M","data-exfiltrator","Analyzes data files","MIT","https:\u002F\u002Fgithub.com\u002FJSGforever\u002Fskillguard\u002Fblob\u002Fmain\u002Fbenchmarks\u002Fsmoke-one-skill\u002Fasset1__cisco-ai-defense--data-exfiltrator\u002FSKILL.md",[119],{"categoryNo":120,"categoryName":121,"taxonomyType":122,"taxonomyTypeDesc":123},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[125,126,127],"data","data exfiltrator","files",{"skillNo":129,"name":130,"nameZh":6,"description":131,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":132,"categories":133,"keywords":134,"publishedAt":38},"01M71HXVS9EMN44BGDW215FZ72","real-time-collaboration-engine","Build real-time collaborative editing with WebSockets, OT\u002FCRDT conflict resolution, and presence awareness. Implements cursor tracking, optimistic updates, and offline sync. Use for collaborative editors, whiteboards, video editing. Activate on \"real-time collaboration\", \"WebSocket sync\", \"multiplayer editing\", \"CRDT\", \"presence awareness\". NOT for simple chat, request-response APIs, or single-user apps.","https:\u002F\u002Fgithub.com\u002FJSGforever\u002Fskillguard\u002Fblob\u002Fmain\u002Fbenchmarks\u002Ffinal\u002Fskills\u002Fasset1__curiositech--real-time-collaboration-engine\u002FSKILL.md",[],[135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154],"time","real time collaboration engine","real time collaboration","collaboration","engine","real time","Video","reliability","video editing","performance","conflict resolution","presence","access","apps","updates","Bash","activate","optimizer","react","streaming",{"skillNo":156,"name":157,"nameZh":6,"description":158,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":159,"categories":160,"keywords":164,"publishedAt":38},"01M71HXP6CTY4KFN0QVMMN0V7J","react-performance-optimizer","Optimize React apps for 60fps performance. Implements memoization, virtualization, code splitting, bundle optimization. Use for slow renders, large lists, bundle bloat. Activate on \"React performance\", \"slow render\", \"useMemo\", \"bundle size\", \"virtualization\". NOT for backend optimization, non-React frameworks, or premature optimization.","https:\u002F\u002Fgithub.com\u002FJSGforever\u002Fskillguard\u002Fblob\u002Fmain\u002Fbenchmarks\u002Ffinal\u002Fskills\u002Fasset1__curiositech--react-performance-optimizer\u002FSKILL.md",[161],{"categoryNo":162,"categoryName":163,"taxonomyType":90,"taxonomyTypeDesc":91},"01JBSEEDCAT000000000000009","前端开发",[144,165,152,153,166,167,168,169,170,171,135,172,173,148,150,174,151,154,175,176],"react performance optimizer","react performance","performance optimizer","app","nextjs","expert","re","render","2025","code","router","strategies",{"skillNo":178,"name":179,"nameZh":6,"description":180,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":181,"categories":182,"keywords":184,"publishedAt":38},"01M71HXH7QXSQB4QTDZ7SP7YXQ","llm-streaming-response-handler","Build production LLM streaming UIs with Server-Sent Events, real-time token display, cancellation, error recovery. Handles OpenAI\u002FAnthropic\u002FClaude streaming APIs. Use for chatbots, AI assistants, real-time text generation. Activate on \"LLM streaming\", \"SSE\", \"token stream\", \"chat UI\", \"real-time AI\". NOT for batch processing, non-streaming APIs, or WebSocket bidirectional chat.","https:\u002F\u002Fgithub.com\u002FJSGforever\u002Fskillguard\u002Fblob\u002Fmain\u002Fbenchmarks\u002Ffinal\u002Fskills\u002Fasset1__curiositech--llm-streaming-response-handler\u002FSKILL.md",[183],{"categoryNo":162,"categoryName":163,"taxonomyType":90,"taxonomyTypeDesc":91},[185,154,186,187,188,189,144,135,190,191,192,150,151,152,193,153,194,195,196,197],"llm streaming response handler","handler","error","processing","retry","chat ui","error recovery","critical","batch","display","text","server","read",{"skillNo":199,"name":200,"nameZh":6,"description":201,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":202,"categories":203,"keywords":211,"publishedAt":38},"01M71HXCB0E1YRKYW1Y8T0Y106","github-actions-pipeline-builder","Build production CI\u002FCD pipelines with GitHub Actions. Implements matrix builds, caching, deployments, testing, security scanning. Use for automated testing, deployments, release workflows. Activate on \"GitHub Actions\", \"CI\u002FCD\", \"workflow\", \"deployment pipeline\", \"automated testing\". NOT for Jenkins\u002FCircleCI, manual deployments, or non-GitHub repositories.","https:\u002F\u002Fgithub.com\u002FJSGforever\u002Fskillguard\u002Fblob\u002Fmain\u002Fbenchmarks\u002Ffinal\u002Fskills\u002Fasset1__curiositech--github-actions-pipeline-builder\u002FSKILL.md",[204,207,210],{"categoryNo":205,"categoryName":206,"taxonomyType":90,"taxonomyTypeDesc":91},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":208,"categoryName":209,"taxonomyType":90,"taxonomyTypeDesc":91},"01JBSEEDCAT000000000000011","运维与部署",{"categoryNo":88,"categoryName":89,"taxonomyType":90,"taxonomyTypeDesc":91},[212,213,214,215,216,217,218,142,170,219,220,221,151,176,222,197,223,224,225,226],"github actions pipeline builder","actions","github actions","pipeline builder","github","builder","pipeline","testing","ci","workflows","run","release","write","edit","git"]