accessibility-compliance
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAccessibility compliance
可访问性合规
Practical accessibility patterns for journalism and academic web publishing.
面向新闻和学术Web发布的实用可访问性方案。
When to activate
适用场景
- Building or auditing news websites
- Writing alt text for article images
- Creating accessible data visualizations
- Developing tools that journalists use
- Ensuring multimedia content is accessible
- Meeting legal accessibility requirements
- Publishing academic content online
- 构建或审核新闻网站
- 为文章图片编写alt text
- 创建可访问的数据可视化内容
- 开发新闻工作者使用的工具
- 确保多媒体内容可访问
- 满足可访问性相关法律要求
- 在线发布学术内容
WCAG essentials for news sites
新闻网站WCAG核心要求
The four principles (POUR)
四大原则(POUR)
markdown
undefinedmarkdown
undefinedWCAG 2.1 AA checklist (journalism focus)
WCAG 2.1 AA checklist (journalism focus)
Perceivable
Perceivable
- Images have meaningful alt text
- Videos have captions
- Audio has transcripts
- Color isn't the only way to convey info
- Text can be resized to 200% without breaking
- Images have meaningful alt text
- Videos have captions
- Audio has transcripts
- Color isn't the only way to convey info
- Text can be resized to 200% without breaking
Operable
Operable
- All functions work with keyboard only
- No keyboard traps
- Skip links to main content
- Page titles describe content
- Focus visible on all interactive elements
- All functions work with keyboard only
- No keyboard traps
- Skip links to main content
- Page titles describe content
- Focus visible on all interactive elements
Understandable
Understandable
- Language is declared in HTML
- Navigation is consistent
- Error messages are clear
- Labels describe form fields
- Language is declared in HTML
- Navigation is consistent
- Error messages are clear
- Labels describe form fields
Robust
Robust
- Valid HTML
- ARIA used correctly (or not at all)
- Works with screen readers
- Doesn't break with zoom/text resize
undefined- Valid HTML
- ARIA used correctly (or not at all)
- Works with screen readers
- Doesn't break with zoom/text resize
undefinedImage accessibility
图片可访问性
Alt text for journalism
新闻场景下的alt text
markdown
undefinedmarkdown
undefinedAlt text decision tree
Alt text decision tree
News photos
News photos
- WHO is in the image (if identifiable and relevant)
- WHAT is happening (the action or situation)
- WHERE (if location matters to story)
- Don't: Repeat caption text verbatim
- WHO is in the image (if identifiable and relevant)
- WHAT is happening (the action or situation)
- WHERE (if location matters to story)
- Don't: Repeat caption text verbatim
Examples
Examples
PHOTO: Protesters holding signs outside courthouse
BAD: "Protesters"
BAD: "Image of protest" (redundant "image of")
GOOD: "Approximately 50 protesters hold signs reading 'Justice Now' outside the federal courthouse in downtown Seattle"
PHOTO: Headshot of interview subject
BAD: "Photo"
GOOD: "Dr. Sarah Chen, epidemiologist at Johns Hopkins"
PHOTO: Chart embedded as image
BAD: "Chart showing data"
GOOD: "Bar chart showing unemployment rising from 3.5% to 8.2% between March and June 2020. Full data in table below."
undefinedPHOTO: Protesters holding signs outside courthouse
BAD: "Protesters"
BAD: "Image of protest" (redundant "image of")
GOOD: "Approximately 50 protesters hold signs reading 'Justice Now' outside the federal courthouse in downtown Seattle"
PHOTO: Headshot of interview subject
BAD: "Photo"
GOOD: "Dr. Sarah Chen, epidemiologist at Johns Hopkins"
PHOTO: Chart embedded as image
BAD: "Chart showing data"
GOOD: "Bar chart showing unemployment rising from 3.5% to 8.2% between March and June 2020. Full data in table below."
undefinedAlt text Python helper
Alt text Python辅助工具
python
def generate_alt_text_prompt(context: dict) -> str:
"""Generate prompt for AI alt text assistance."""
return f"""
Write alt text for a news image.
Story context: {context.get('headline', 'Unknown')}
Image type: {context.get('image_type', 'photo')}
Caption (if any): {context.get('caption', 'None')}
Guidelines:
- Be concise (under 125 characters if possible)
- Don't start with "Image of" or "Photo of"
- Include relevant details for story context
- Don't duplicate caption exactly
- Describe what's visually important
If this is decorative only, respond: ""
"""
def is_decorative(image_context: str) -> bool:
"""Check if image is purely decorative (empty alt appropriate)."""
decorative_indicators = [
'decorative',
'separator',
'background',
'spacer',
'border'
]
return any(ind in image_context.lower() for ind in decorative_indicators)python
def generate_alt_text_prompt(context: dict) -> str:
"""Generate prompt for AI alt text assistance."""
return f"""
Write alt text for a news image.
Story context: {context.get('headline', 'Unknown')}
Image type: {context.get('image_type', 'photo')}
Caption (if any): {context.get('caption', 'None')}
Guidelines:
- Be concise (under 125 characters if possible)
- Don't start with "Image of" or "Photo of"
- Include relevant details for story context
- Don't duplicate caption exactly
- Describe what's visually important
If this is decorative only, respond: ""
"""
def is_decorative(image_context: str) -> bool:
"""Check if image is purely decorative (empty alt appropriate)."""
decorative_indicators = [
'decorative',
'separator',
'background',
'spacer',
'border'
]
return any(ind in image_context.lower() for ind in decorative_indicators)Accessible data visualization
可访问数据可视化
Chart accessibility checklist
图表可访问性检查清单
markdown
undefinedmarkdown
undefinedMaking charts accessible
Making charts accessible
Essential elements
Essential elements
- Text alternative describing the key insight
- Data table available (visible or linked)
- Colors have sufficient contrast
- Patterns/textures supplement color coding
- Labels directly on chart (not legend-only)
- Title describes what chart shows
- Text alternative describing the key insight
- Data table available (visible or linked)
- Colors have sufficient contrast
- Patterns/textures supplement color coding
- Labels directly on chart (not legend-only)
- Title describes what chart shows
Interactive charts
Interactive charts
- Keyboard navigable
- Focus indicators visible
- Screen reader announces data points
- Tooltips accessible via keyboard
- Zooming doesn't break layout
undefined- Keyboard navigable
- Focus indicators visible
- Screen reader announces data points
- Tooltips accessible via keyboard
- Zooming doesn't break layout
undefinedAccessible chart component
可访问图表组件
html
<!-- Accessible chart pattern -->
<figure role="figure" aria-labelledby="chart-title" aria-describedby="chart-desc">
<figcaption>
<h3 id="chart-title">Unemployment Rate 2020-2024</h3>
<p id="chart-desc">
Line chart showing unemployment starting at 3.5% in January 2020,
spiking to 14.7% in April 2020, and gradually declining to 3.9% by 2024.
</p>
</figcaption>
<!-- The chart itself -->
<div id="chart" role="img" aria-label="Interactive line chart. Data table available below.">
<!-- Chart renders here -->
</div>
<!-- Always provide data table -->
<details>
<summary>View data table</summary>
<table>
<caption>Monthly unemployment rate data</caption>
<thead>
<tr>
<th scope="col">Month</th>
<th scope="col">Unemployment Rate (%)</th>
</tr>
</thead>
<tbody>
<tr><td>Jan 2020</td><td>3.5</td></tr>
<tr><td>Apr 2020</td><td>14.7</td></tr>
<!-- etc -->
</tbody>
</table>
</details>
</figure>html
<!-- Accessible chart pattern -->
<figure role="figure" aria-labelledby="chart-title" aria-describedby="chart-desc">
<figcaption>
<h3 id="chart-title">Unemployment Rate 2020-2024</h3>
<p id="chart-desc">
Line chart showing unemployment starting at 3.5% in January 2020,
spiking to 14.7% in April 2020, and gradually declining to 3.9% by 2024.
</p>
</figcaption>
<!-- The chart itself -->
<div id="chart" role="img" aria-label="Interactive line chart. Data table available below.">
<!-- Chart renders here -->
</div>
<!-- Always provide data table -->
<details>
<summary>View data table</summary>
<table>
<caption>Monthly unemployment rate data</caption>
<thead>
<tr>
<th scope="col">Month</th>
<th scope="col">Unemployment Rate (%)</th>
</tr>
</thead>
<tbody>
<tr><td>Jan 2020</td><td>3.5</td></tr>
<tr><td>Apr 2020</td><td>14.7</td></tr>
<!-- etc -->
</tbody>
</table>
</details>
</figure>Color-blind safe palettes
色弱友好调色板
python
undefinedpython
undefinedSafe color palettes for data visualization
Safe color palettes for data visualization
COLOR_PALETTES = {
# Paul Tol's color schemes - widely tested for accessibility
'bright': [
'#4477AA', # Blue
'#EE6677', # Red
'#228833', # Green
'#CCBB44', # Yellow
'#66CCEE', # Cyan
'#AA3377', # Purple
'#BBBBBB', # Grey
],
# Categorical (safe for most color blindness)
'categorical': [
'#332288', # Indigo
'#88CCEE', # Cyan
'#44AA99', # Teal
'#117733', # Green
'#999933', # Olive
'#DDCC77', # Sand
'#CC6677', # Rose
'#882255', # Wine
],
# Sequential (single hue)
'sequential_blue': [
'#f7fbff',
'#deebf7',
'#c6dbef',
'#9ecae1',
'#6baed6',
'#4292c6',
'#2171b5',
'#084594',
],
# Diverging (for data with meaningful midpoint)
'diverging': [
'#d73027', # Red (negative)
'#f46d43',
'#fdae61',
'#fee08b',
'#ffffbf', # Neutral
'#d9ef8b',
'#a6d96a',
'#66bd63',
'#1a9850', # Green (positive)
]}
def validate_contrast(color1: str, color2: str) -> float:
"""Calculate WCAG contrast ratio between two colors."""
def hex_to_rgb(hex_color):
hex_color = hex_color.lstrip('#')
return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
def relative_luminance(rgb):
r, g, b = [x / 255.0 for x in rgb]
r = r / 12.92 if r <= 0.03928 else ((r + 0.055) / 1.055) ** 2.4
g = g / 12.92 if g <= 0.03928 else ((g + 0.055) / 1.055) ** 2.4
b = b / 12.92 if b <= 0.03928 else ((b + 0.055) / 1.055) ** 2.4
return 0.2126 * r + 0.7152 * g + 0.0722 * b
l1 = relative_luminance(hex_to_rgb(color1))
l2 = relative_luminance(hex_to_rgb(color2))
lighter = max(l1, l2)
darker = min(l1, l2)
return (lighter + 0.05) / (darker + 0.05)COLOR_PALETTES = {
# Paul Tol's color schemes - widely tested for accessibility
'bright': [
'#4477AA', # Blue
'#EE6677', # Red
'#228833', # Green
'#CCBB44', # Yellow
'#66CCEE', # Cyan
'#AA3377', # Purple
'#BBBBBB', # Grey
],
# Categorical (safe for most color blindness)
'categorical': [
'#332288', # Indigo
'#88CCEE', # Cyan
'#44AA99', # Teal
'#117733', # Green
'#999933', # Olive
'#DDCC77', # Sand
'#CC6677', # Rose
'#882255', # Wine
],
# Sequential (single hue)
'sequential_blue': [
'#f7fbff',
'#deebf7',
'#c6dbef',
'#9ecae1',
'#6baed6',
'#4292c6',
'#2171b5',
'#084594',
],
# Diverging (for data with meaningful midpoint)
'diverging': [
'#d73027', # Red (negative)
'#f46d43',
'#fdae61',
'#fee08b',
'#ffffbf', # Neutral
'#d9ef8b',
'#a6d96a',
'#66bd63',
'#1a9850', # Green (positive)
]}
def validate_contrast(color1: str, color2: str) -> float:
"""Calculate WCAG contrast ratio between two colors."""
def hex_to_rgb(hex_color):
hex_color = hex_color.lstrip('#')
return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
def relative_luminance(rgb):
r, g, b = [x / 255.0 for x in rgb]
r = r / 12.92 if r <= 0.03928 else ((r + 0.055) / 1.055) ** 2.4
g = g / 12.92 if g <= 0.03928 else ((g + 0.055) / 1.055) ** 2.4
b = b / 12.92 if b <= 0.03928 else ((b + 0.055) / 1.055) ** 2.4
return 0.2126 * r + 0.7152 * g + 0.0722 * b
l1 = relative_luminance(hex_to_rgb(color1))
l2 = relative_luminance(hex_to_rgb(color2))
lighter = max(l1, l2)
darker = min(l1, l2)
return (lighter + 0.05) / (darker + 0.05)WCAG requirements:
WCAG requirements:
Normal text: 4.5:1 minimum (AA), 7:1 enhanced (AAA)
Normal text: 4.5:1 minimum (AA), 7:1 enhanced (AAA)
Large text (18pt+): 3:1 minimum (AA), 4.5:1 enhanced (AAA)
Large text (18pt+): 3:1 minimum (AA), 4.5:1 enhanced (AAA)
UI components: 3:1 minimum
UI components: 3:1 minimum
undefinedundefinedVideo and audio accessibility
视频与音频可访问性
Caption requirements
字幕要求
markdown
undefinedmarkdown
undefinedVideo caption checklist
Video caption checklist
Quality standards
Quality standards
- 99%+ accuracy
- Synchronized with audio (within 1 second)
- Speaker identification for multiple speakers
- Sound effects described [applause] [music]
- Non-speech audio described when relevant
- 99%+ accuracy
- Synchronized with audio (within 1 second)
- Speaker identification for multiple speakers
- Sound effects described [applause] [music]
- Non-speech audio described when relevant
Technical requirements
Technical requirements
- Captions available in player controls
- Can be toggled on/off
- Styling doesn't overlap video content
- Readable font size and contrast
- Captions available in player controls
- Can be toggled on/off
- Styling doesn't overlap video content
- Readable font size and contrast
Caption format (SRT example)
Caption format (SRT example)
1
00:00:01,000 --> 00:00:04,500
[NEWS ANCHOR] Good evening. Breaking news tonight
from downtown where protesters have gathered.
2
00:00:04,600 --> 00:00:08,200
We're going live to reporter Jane Smith
at the scene. Jane?
undefined1
00:00:01,000 --> 00:00:04,500
[NEWS ANCHOR] Good evening. Breaking news tonight
from downtown where protesters have gathered.
2
00:00:04,600 --> 00:00:08,200
We're going live to reporter Jane Smith
at the scene. Jane?
undefinedAudio description for video
视频音频描述
markdown
undefinedmarkdown
undefinedWhen audio description is needed
When audio description is needed
Required
Required
- Key visual information not in dialogue
- Actions crucial to understanding story
- Text on screen not read aloud
- Identifying information for speakers
- Key visual information not in dialogue
- Actions crucial to understanding story
- Text on screen not read aloud
- Identifying information for speakers
Example script
Example script
ORIGINAL VIDEO: [Reporter stands in front of burning building]
DIALOGUE: "The fire started around 3am..."
AUDIO DESCRIPTION VERSION:
[A reporter in a red jacket stands before a five-story
apartment building engulfed in flames. Fire trucks
visible in background]
DIALOGUE: "The fire started around 3am..."
undefinedORIGINAL VIDEO: [Reporter stands in front of burning building]
DIALOGUE: "The fire started around 3am..."
AUDIO DESCRIPTION VERSION:
[A reporter in a red jacket stands before a five-story
apartment building engulfed in flames. Fire trucks
visible in background]
DIALOGUE: "The fire started around 3am..."
undefinedKeyboard accessibility
键盘可访问性
Focus management patterns
焦点管理方案
javascript
// Skip link implementation
document.addEventListener('DOMContentLoaded', () => {
const skipLink = document.querySelector('.skip-link');
const mainContent = document.querySelector('main');
skipLink.addEventListener('click', (e) => {
e.preventDefault();
mainContent.setAttribute('tabindex', '-1');
mainContent.focus();
});
});
// Keyboard trap prevention in modals
function createAccessibleModal(modalElement) {
const focusableElements = modalElement.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstFocusable = focusableElements[0];
const lastFocusable = focusableElements[focusableElements.length - 1];
modalElement.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === firstFocusable) {
e.preventDefault();
lastFocusable.focus();
} else if (!e.shiftKey && document.activeElement === lastFocusable) {
e.preventDefault();
firstFocusable.focus();
}
}
if (e.key === 'Escape') {
closeModal();
}
});
}
// Focus indicator styles (never remove outlines without replacement)
/*
:focus {
outline: 2px solid #005fcc;
outline-offset: 2px;
}
:focus:not(:focus-visible) {
outline: none; // Remove for mouse users
}
:focus-visible {
outline: 2px solid #005fcc; // Keep for keyboard users
outline-offset: 2px;
}
*/javascript
// Skip link implementation
document.addEventListener('DOMContentLoaded', () => {
const skipLink = document.querySelector('.skip-link');
const mainContent = document.querySelector('main');
skipLink.addEventListener('click', (e) => {
e.preventDefault();
mainContent.setAttribute('tabindex', '-1');
mainContent.focus();
});
});
// Keyboard trap prevention in modals
function createAccessibleModal(modalElement) {
const focusableElements = modalElement.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstFocusable = focusableElements[0];
const lastFocusable = focusableElements[focusableElements.length - 1];
modalElement.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === firstFocusable) {
e.preventDefault();
lastFocusable.focus();
} else if (!e.shiftKey && document.activeElement === lastFocusable) {
e.preventDefault();
firstFocusable.focus();
}
}
if (e.key === 'Escape') {
closeModal();
}
});
}
// Focus indicator styles (never remove outlines without replacement)
/*
:focus {
outline: 2px solid #005fcc;
outline-offset: 2px;
}
:focus:not(:focus-visible) {
outline: none; // Remove for mouse users
}
:focus-visible {
outline: 2px solid #005fcc; // Keep for keyboard users
outline-offset: 2px;
}
*/Forms and error handling
表单与错误处理
Accessible form patterns
可访问表单方案
html
<!-- Accessible form field -->
<div class="form-group">
<label for="email">
Email address
<span class="required" aria-hidden="true">*</span>
<span class="visually-hidden">(required)</span>
</label>
<input
type="email"
id="email"
name="email"
required
aria-describedby="email-hint email-error"
aria-invalid="false"
>
<p id="email-hint" class="hint">
We'll use this to send you the newsletter.
</p>
<p id="email-error" class="error" role="alert" hidden>
Please enter a valid email address.
</p>
</div>
<style>
/* Visually hidden but accessible to screen readers */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
</style>html
<!-- Accessible form field -->
<div class="form-group">
<label for="email">
Email address
<span class="required" aria-hidden="true">*</span>
<span class="visually-hidden">(required)</span>
</label>
<input
type="email"
id="email"
name="email"
required
aria-describedby="email-hint email-error"
aria-invalid="false"
>
<p id="email-hint" class="hint">
We'll use this to send you the newsletter.
</p>
<p id="email-error" class="error" role="alert" hidden>
Please enter a valid email address.
</p>
</div>
<style>
/* Visually hidden but accessible to screen readers */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
</style>Error message patterns
错误提示方案
javascript
function showError(inputElement, message) {
const errorElement = document.getElementById(
inputElement.getAttribute('aria-describedby').split(' ').find(id =>
id.includes('error')
)
);
inputElement.setAttribute('aria-invalid', 'true');
errorElement.textContent = message;
errorElement.hidden = false;
// Announce error to screen readers
errorElement.setAttribute('role', 'alert');
}
function clearError(inputElement) {
const errorId = inputElement.getAttribute('aria-describedby')
.split(' ')
.find(id => id.includes('error'));
const errorElement = document.getElementById(errorId);
inputElement.setAttribute('aria-invalid', 'false');
errorElement.hidden = true;
errorElement.removeAttribute('role');
}javascript
function showError(inputElement, message) {
const errorElement = document.getElementById(
inputElement.getAttribute('aria-describedby').split(' ').find(id =>
id.includes('error')
)
);
inputElement.setAttribute('aria-invalid', 'true');
errorElement.textContent = message;
errorElement.hidden = false;
// Announce error to screen readers
errorElement.setAttribute('role', 'alert');
}
function clearError(inputElement) {
const errorId = inputElement.getAttribute('aria-describedby')
.split(' ')
.find(id => id.includes('error'));
const errorElement = document.getElementById(errorId);
inputElement.setAttribute('aria-invalid', 'false');
errorElement.hidden = true;
errorElement.removeAttribute('role');
}Testing tools
测试工具
Automated testing
自动化测试
python
undefinedpython
undefinedAccessibility audit with axe-core (via Playwright)
Accessibility audit with axe-core (via Playwright)
from playwright.sync_api import sync_playwright
def run_accessibility_audit(url: str) -> dict:
"""Run automated accessibility tests."""
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto(url)
# Inject axe-core
page.add_script_tag(
url='https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.7.2/axe.min.js'
)
# Run audit
results = page.evaluate('''
async () => {
return await axe.run();
}
''')
browser.close()
return {
'violations': results['violations'],
'passes': len(results['passes']),
'incomplete': results['incomplete'],
'url': url
}def format_violations(results: dict) -> str:
"""Format violations for review."""
output = []
for v in results['violations']:
output.append(f"\n## {v['id']}: {v['description']}")
output.append(f"Impact: {v['impact']}")
output.append(f"WCAG: {', '.join(v.get('tags', []))}")
for node in v['nodes'][:3]: # First 3 examples
output.append(f" - {node['html'][:100]}")
return '\n'.join(output)
undefinedfrom playwright.sync_api import sync_playwright
def run_accessibility_audit(url: str) -> dict:
"""Run automated accessibility tests."""
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto(url)
# Inject axe-core
page.add_script_tag(
url='https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.7.2/axe.min.js'
)
# Run audit
results = page.evaluate('''
async () => {
return await axe.run();
}
''')
browser.close()
return {
'violations': results['violations'],
'passes': len(results['passes']),
'incomplete': results['incomplete'],
'url': url
}def format_violations(results: dict) -> str:
"""Format violations for review."""
output = []
for v in results['violations']:
output.append(f"\n## {v['id']}: {v['description']}")
output.append(f"Impact: {v['impact']}")
output.append(f"WCAG: {', '.join(v.get('tags', []))}")
for node in v['nodes'][:3]: # First 3 examples
output.append(f" - {node['html'][:100]}")
return '\n'.join(output)
undefinedManual testing checklist
手动测试检查清单
markdown
undefinedmarkdown
undefinedManual accessibility tests
Manual accessibility tests
Keyboard navigation
Keyboard navigation
- Tab through entire page
- Can reach all interactive elements
- Focus order makes sense
- No keyboard traps
- Skip link works
- Tab through entire page
- Can reach all interactive elements
- Focus order makes sense
- No keyboard traps
- Skip link works
Screen reader testing
Screen reader testing
- Headings announce in logical order
- Images have meaningful descriptions
- Form labels announce correctly
- Error messages announced
- Dynamic content updates announced
- Headings announce in logical order
- Images have meaningful descriptions
- Form labels announce correctly
- Error messages announced
- Dynamic content updates announced
Zoom testing
Zoom testing
- 200% zoom, no horizontal scrolling
- 400% zoom, content still usable
- Text spacing adjustments don't break layout
- 200% zoom, no horizontal scrolling
- 400% zoom, content still usable
- Text spacing adjustments don't break layout
Color and contrast
Color and contrast
- Works in grayscale
- Links distinguishable from text
- Error states not color-only
- Contrast checker passes (4.5:1 minimum)
undefined- Works in grayscale
- Links distinguishable from text
- Error states not color-only
- Contrast checker passes (4.5:1 minimum)
undefinedLegal requirements
法律要求
markdown
undefinedmarkdown
undefinedAccessibility law summary
Accessibility law summary
United States
United States
- Section 508: Federal agencies must be accessible
- ADA: Increasingly applied to websites
- State laws: Many states have additional requirements
- Section 508: Federal agencies must be accessible
- ADA: Increasingly applied to websites
- State laws: Many states have additional requirements
European Union
European Union
- European Accessibility Act: From 2025
- EN 301 549: Technical standard
- European Accessibility Act: From 2025
- EN 301 549: Technical standard
Best practice
Best practice
WCAG 2.1 AA is the global standard. Meet this and you'll
likely satisfy most legal requirements.
undefinedWCAG 2.1 AA is the global standard. Meet this and you'll
likely satisfy most legal requirements.
undefinedRelated skills
相关技能
- zero-build-frontend - Build accessible static sites
- data-journalism - Create accessible visualizations
- web-scraping - Ensure scraped content preserves accessibility
- zero-build-frontend - 构建可访问静态站点
- data-journalism - 创建可访问可视化内容
- web-scraping - 确保爬取的内容保留可访问性
Skill metadata
技能元数据
| Field | Value |
|---|---|
| Version | 1.0.0 |
| Created | 2025-12-26 |
| Author | Claude Skills for Journalism |
| Domain | Development, Publishing |
| Complexity | Intermediate |
| 字段 | 值 |
|---|---|
| Version | 1.0.0 |
| Created | 2025-12-26 |
| Author | Claude Skills for Journalism |
| Domain | Development, Publishing |
| Complexity | Intermediate |