use super::{
dimensions::{Dimension, Subcategory},
issue_class::IssueClass,
score::{Scaling, ScoreImpact},
severity::Severity,
};
pub struct Rule {
pub id: &'static str,
pub dimension: Dimension,
pub subcategory: Subcategory,
pub issue_class: IssueClass,
pub severity: Severity,
pub external_ref: Option<&'static str>,
pub external_level: Option<&'static str>,
pub axe_id: Option<&'static str>,
pub title: &'static str,
pub title_en: &'static str,
pub description: &'static str,
pub user_impact: &'static str,
pub user_impact_en: &'static str,
pub technical_impact: &'static str,
pub technical_impact_en: &'static str,
pub score_impact: ScoreImpact,
pub report_visibility: ReportVisibility,
}
pub struct ReportVisibility {
pub executive: bool,
pub standard: bool,
pub technical: bool,
}
const VIS_ALL: ReportVisibility = ReportVisibility {
executive: true,
standard: true,
technical: true,
};
const VIS_STANDARD: ReportVisibility = ReportVisibility {
executive: false,
standard: true,
technical: true,
};
const VIS_TECHNICAL: ReportVisibility = ReportVisibility {
executive: false,
standard: false,
technical: true,
};
pub struct RuleLookup;
impl RuleLookup {
pub fn by_id(id: &str) -> Option<&'static Rule> {
RULES.iter().find(|r| r.id == id)
}
pub fn by_wcag(criterion: &str) -> Option<&'static Rule> {
let search = format!("WCAG {}", criterion);
RULES
.iter()
.find(|r| r.external_ref == Some(search.as_str()))
}
pub fn by_legacy_wcag_id(wcag_id: &str) -> Option<&'static Rule> {
LEGACY_WCAG_MAP
.iter()
.find(|(wid, _)| *wid == wcag_id)
.and_then(|(_, rule_id)| Self::by_id(rule_id))
}
pub fn by_dimension(dim: Dimension) -> Vec<&'static Rule> {
RULES.iter().filter(|r| r.dimension == dim).collect()
}
}
static LEGACY_WCAG_MAP: &[(&str, &str)] = &[
("1.1.1", "a11y.alt_text.missing"),
("1.2.1", "a11y.media.alternative"),
("1.2.2", "a11y.captions.missing"),
("1.2.8", "a11y.media_alternative.missing"),
("1.3.1", "a11y.structure.missing"),
("1.3.2", "a11y.meaningful_sequence.invalid"),
("1.3.4", "a11y.orientation.restricted"),
("1.3.6", "a11y.identify_purpose.missing"),
("1.3.5", "a11y.input_purpose.missing"),
("1.4.1", "a11y.color.link_indicator"),
("1.4.3", "a11y.contrast.weak"),
("1.4.4", "a11y.resize_text.weak"),
("1.4.7", "a11y.background_audio.uncontrolled"),
("1.4.8", "a11y.visual_presentation.weak"),
("1.4.10", "a11y.reflow.missing"),
("1.4.11", "a11y.non_text_contrast.weak"),
("1.4.12", "a11y.text_spacing.clipped"),
("1.4.13", "a11y.hover.content_visibility"),
("2.1.1", "a11y.keyboard.missing"),
("2.1.2", "a11y.keyboard_trap.risk"),
("2.2.1", "a11y.timing.unadjustable"),
("2.2.2", "a11y.pause_stop_hide.no_control"),
("2.2.3", "a11y.timing.essential"),
("2.2.4", "a11y.interruptions.uncontrollable"),
("2.2.5", "a11y.re_authenticate.data_loss"),
("2.2.6", "a11y.timeouts.unwarned"),
("2.4.1", "a11y.bypass_blocks.missing"),
("2.4.2", "a11y.page_title.missing"),
("2.4.3", "a11y.focus_order.weak"),
("2.4.4", "a11y.link_purpose.weak"),
("2.4.6", "a11y.headings.missing"),
("2.4.7", "a11y.focus_visible.missing"),
("2.4.8", "a11y.location.missing"),
("2.4.9", "a11y.link_purpose_only.weak"),
("2.3.3", "a11y.motion.reduced_motion"),
("2.4.10", "a11y.section_headings.missing"),
("2.5.1", "a11y.pointer_gestures.missing_alternative"),
("2.5.2", "a11y.pointer_cancellation.invalid"),
("2.5.3", "a11y.label_in_name.invalid"),
("2.5.4", "a11y.motion_actuation.missing_alternative"),
("2.5.5", "a11y.target_size_enhanced.small"),
("2.4.11", "a11y.focus_not_obscured_minimum.hidden"),
("2.4.12", "a11y.focus_not_obscured_enhanced.hidden"),
("2.5.8", "a11y.target_size_minimum.small"),
("3.1.1", "a11y.language.missing"),
("3.1.3", "a11y.unusual_words.missing_definition"),
("3.1.4", "a11y.abbreviations.missing"),
("3.2.1", "a11y.on_focus.risk"),
("3.2.2", "a11y.on_input.risk"),
("3.3.1", "a11y.error_id.missing_description"),
("3.3.2", "a11y.form_labels.missing"),
("3.3.5", "a11y.help.missing"),
("3.3.7", "a11y.redundant_entry.missing_reuse"),
("3.3.8", "a11y.accessible_auth.paste_blocked"),
("4.1.1", "a11y.parsing.invalid"),
("4.1.2", "a11y.name_role.missing"),
("4.1.3", "a11y.status_messages.broken"),
("aria-hidden-focus", "a11y.aria_hidden_focus.invalid"),
("aria-required-attr", "a11y.aria_required_attr.missing"),
("aria-required-parent", "a11y.aria_required_parent.invalid"),
("aria-prohibited-attr", "a11y.aria_prohibited_attr.invalid"),
("aria-allowed-attr", "a11y.aria_allowed_attr.invalid"),
(
"aria-valid-attr-value",
"a11y.aria_valid_attr_value.invalid",
),
("aria-valid-attr", "a11y.aria_relationships.invalid"),
("aria-roles", "a11y.aria_roles.invalid"),
("aria-attr-name-invalid", "a11y.aria_attr_name.invalid"),
("html-xml-lang-mismatch", "a11y.language_mismatch.invalid"),
("aria-command-name", "a11y.command_name.missing"),
("aria-input-field-name", "a11y.input_field_name.missing"),
("aria-meter-name", "a11y.meter_name.missing"),
("aria-progressbar-name", "a11y.progressbar_name.missing"),
("aria-toggle-field-name", "a11y.toggle_field_name.missing"),
("aria-dialog-name", "a11y.dialog_name.missing"),
("aria-treeitem-name", "a11y.treeitem_name.missing"),
("td-headers-attr", "a11y.table_headers_ref.invalid"),
("th-has-data-cells", "a11y.table_header_data.missing"),
("aria-tablist-tabpanel", "a11y.tablist_tabpanel.missing"),
("aria-combobox-options", "a11y.combobox_options.missing"),
("frame-tested", "a11y.frame_tested.cross_origin"),
("frame-title", "a11y.frame_title.missing"),
("form-no-submit", "a11y.form_no_submit.missing"),
("landmark-main-present", "a11y.landmark_main.missing"),
("landmark-unique", "a11y.landmark_unique.invalid"),
("modern-attribute-misuse", "a11y.modern_attributes.invalid"),
(
"presentation-semantic-children",
"a11y.presentation_semantic_children.invalid",
),
(
"server-side-image-map",
"a11y.image_map_server_side.invalid",
),
("svg-img-alt", "a11y.svg_alt.missing"),
(
"accessible-auth-paste-blocked",
"a11y.accessible_auth.paste_blocked",
),
(
"accessible-auth-captcha",
"a11y.accessible_auth.captcha_review",
),
("area-alt", "a11y.area_alt.missing"),
("input-image-alt", "a11y.input_image_alt.missing"),
("object-alt", "a11y.object_alt.missing"),
("html-content-model", "a11y.html_content_model.invalid"),
("form-field-group", "a11y.form_field_group.missing"),
(
"landmark-banner-is-top-level",
"a11y.landmark_banner_nested.invalid",
),
(
"landmark-contentinfo-is-top-level",
"a11y.landmark_contentinfo_nested.invalid",
),
(
"landmark-main-is-top-level",
"a11y.landmark_main_nested.invalid",
),
(
"landmark-no-duplicate-banner",
"a11y.landmark_banner_duplicate.invalid",
),
(
"landmark-no-duplicate-contentinfo",
"a11y.landmark_contentinfo_duplicate.invalid",
),
(
"landmark-no-duplicate-main",
"a11y.landmark_main_duplicate.invalid",
),
("landmark-banner-present", "a11y.landmark_banner.missing"),
(
"landmark-navigation-present",
"a11y.landmark_navigation.missing",
),
(
"landmark-contentinfo-present",
"a11y.landmark_contentinfo.missing",
),
("label-title-only", "a11y.label_title_only.invalid"),
(
"click-events-have-key-events",
"a11y.click_handler_keyboard.missing",
),
("landmark-one-main", "a11y.bypass_main_landmark.missing"),
("skip-link", "a11y.skip_link.missing"),
(
"focus-visible-outline-none",
"a11y.focus_indicator_suppressed.invalid",
),
(
"scrollable-region-focusable",
"a11y.scrollable_region_focus.missing",
),
("input-error-message", "a11y.error_description.missing"),
("duplicate-id-aria", "a11y.duplicate_id_aria.invalid"),
("aria-label", "a11y.interactive_name.missing"),
("title-only-description", "a11y.title_only_description.weak"),
(
"description-duplicates-name",
"a11y.description_duplicates_name.redundant",
),
(
"aria-required-children",
"a11y.aria_required_children.missing",
),
("dialog-name", "a11y.dialog_missing_name.invalid"),
("control-missing-label", "a11y.control_name.missing"),
("link-as-button", "a11y.link_as_button.invalid"),
("redundant-role", "a11y.redundant_role.invalid"),
("summary-name", "a11y.summary_name.missing"),
("region", "a11y.landmark_region.missing"),
("focusable-no-role", "a11y.focusable_no_role.invalid"),
("link-name-context", "a11y.link_purpose_context.weak"),
("image-alt", "a11y.alt_text.missing"),
("keyboard", "a11y.keyboard.missing"),
("meta-viewport", "a11y.resize_text.weak"),
("bypass", "a11y.bypass_blocks.missing"),
(
"aria-invalid-without-describedby",
"a11y.error_id.missing_description",
),
("ids/duplicate", "a11y.parsing.invalid"),
(
"keyboard/positive-tabindex",
"a11y.positive_tabindex.invalid",
),
("headings/skip-level", "a11y.heading_level.skipped"),
("headings/h1-missing", "a11y.main_heading.missing"),
("headings/h1-multiple", "a11y.main_heading_multiple.weak"),
("headings/empty", "a11y.heading_empty.invalid"),
("lists/invalid-structure", "a11y.list_children.invalid"),
("lists/empty", "a11y.list_empty.invalid"),
("lists/item-outside-list", "a11y.list_structure.missing"),
(
"lists/term-without-definition",
"a11y.definition_description.missing",
),
("tables/header-missing", "a11y.table_headers.missing"),
("tables/name-missing", "a11y.table_name.missing"),
(
"tables/presentational-with-headers",
"a11y.table_presentational_headers.invalid",
),
("document/lang-invalid", "a11y.page_language.invalid"),
(
"zoom/viewport-scale-limited",
"a11y.viewport_zoom.restricted",
),
("zoom/viewport-locked", "a11y.resize_text.weak"),
("zoom/viewport-missing", "a11y.viewport_meta.missing"),
("document/title-missing", "a11y.page_title.missing"),
("document/title-empty", "a11y.page_title.missing"),
("aria/reference-missing", "a11y.aria_relationships.invalid"),
(
"aria/required-attribute-missing",
"a11y.aria_required_attr.missing",
),
(
"keyboard/hidden-focusable",
"a11y.aria_hidden_focus.invalid",
),
(
"landmarks/main-missing",
"a11y.bypass_main_landmark.missing",
),
(
"landmarks/main-duplicate",
"a11y.landmark_main_duplicate.invalid",
),
("landmarks/banner-missing", "a11y.landmark_banner.missing"),
(
"landmarks/contentinfo-missing",
"a11y.landmark_contentinfo.missing",
),
(
"landmarks/navigation-missing",
"a11y.landmark_navigation.missing",
),
("svg/name-missing", "a11y.svg_alt.missing"),
("images/alt-missing", "a11y.alt_text.missing"),
("images/alt-suspicious", "a11y.alt_text.suspicious"),
("links/name-missing", "a11y.control_name.missing"),
("buttons/name-missing", "a11y.control_name.missing"),
("keyboard/skip-link-missing", "a11y.skip_link.missing"),
("aria/role-invalid", "a11y.aria_roles.invalid"),
("aria/role-abstract", "a11y.aria_roles.invalid"),
("aria/attribute-unknown", "a11y.aria_attr_name.invalid"),
(
"aria/attribute-value-invalid",
"a11y.aria_valid_attr_value.invalid",
),
("aria/owns-conflict", "a11y.duplicate_id_aria.invalid"),
("aria/tab-selected-missing", "a11y.tab_selected.missing"),
("aria/tabpanel-missing", "a11y.tablist_tabpanel.missing"),
(
"aria/combobox-popup-missing",
"a11y.combobox_options.missing",
),
("popover/target-missing", "a11y.modern_attributes.invalid"),
("popover/target-invalid", "a11y.modern_attributes.invalid"),
("inert/dialog-inert", "a11y.modern_attributes.invalid"),
(
"aria/attribute-not-allowed",
"a11y.aria_allowed_attr.invalid",
),
(
"aria/attribute-prohibited",
"a11y.aria_prohibited_attr.invalid",
),
(
"aria/required-parent-missing",
"a11y.aria_required_parent.invalid",
),
(
"aria/required-children-missing",
"a11y.aria_required_children.missing",
),
("names/required-missing", "a11y.interactive_name.missing"),
("names/symbol-only", "a11y.interactive_name.missing"),
("dialog/name-missing", "a11y.dialog_missing_name.invalid"),
("dialog/modal-unmarked", "a11y.dialog_modal.missing"),
("summary/name-missing", "a11y.summary_name.missing"),
("status/live-overridden", "a11y.status_messages.broken"),
("label-in-name/mismatch", "a11y.label_in_name.invalid"),
("focus-visible", "a11y.focus_visible.missing"),
("link-name", "a11y.link_purpose.weak"),
("display/toggle-missing", "a11y.display_toggle.missing"),
("display/init-missing", "a11y.display_init.late"),
(
"display/text-media-visible",
"a11y.display_text_media.visible",
),
(
"display/text-not-visible",
"a11y.display_text_layer.missing",
),
("display/text-hidden", "a11y.display_text_layer.hidden"),
];
pub static RULES: &[Rule] = &[
Rule {
id: "a11y.alt_text.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::ContentAlternatives,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 1.1.1"),
external_level: Some("A"),
axe_id: Some("image-alt"),
title: "Fehlende Alternativtexte bei Bildern",
title_en: "Missing alternative text on images",
description: "Bilder haben keinen beschreibenden Alternativtext.",
user_impact: "Screenreader-Nutzer erhalten keine Bildinformation.",
user_impact_en: "Screen reader users receive no image information.",
technical_impact: "Nicht-konforme Bildauszeichnung im Markup.",
technical_impact_en: "Non-conformant image markup.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.alt_text.suspicious",
dimension: Dimension::Accessibility,
subcategory: Subcategory::ContentAlternatives,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: Some("WCAG 1.1.1"),
external_level: Some("A"),
axe_id: Some("images/alt-suspicious"),
title: "Vermutlich nichtssagender Alternativtext",
title_en: "Alternative text probably not descriptive",
description: "Der Alternativtext eines Bildes ist ein Dateiname, ein Füllwort wie „Bild\" oder „Logo\" oder kürzer als drei Zeichen.",
user_impact: "Screenreader-Nutzer hören einen Text, der den Bildinhalt nicht vermittelt.",
user_impact_en: "Screen reader users hear text that does not convey the image content.",
technical_impact: "alt-Attribut vorhanden, aber ohne beschreibenden Inhalt.",
technical_impact_en: "alt attribute present but without descriptive content.",
score_impact: ScoreImpact {
base_penalty: 1.0,
max_penalty: 4.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.media.alternative",
dimension: Dimension::Accessibility,
subcategory: Subcategory::ContentAlternatives,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 1.2.1"),
external_level: Some("A"),
axe_id: Some("video-caption"),
title: "Fehlende Alternativen für reine Audio- oder Videoinhalte",
title_en: "Missing alternatives for audio-only or video-only content",
description: "Reine Audio- oder Videoinhalte haben keine alternative Textdarstellung oder Tonspur.",
user_impact: "Menschen mit Seh- oder Hörbehinderung können den Inhalt der Medien nicht erfassen.",
user_impact_en: "People with visual or hearing impairments cannot access the media content.",
technical_impact: "Fehlende alternative Medienbeschreibung.",
technical_impact_en: "Missing alternative media description.",
score_impact: ScoreImpact {
base_penalty: 2.0,
max_penalty: 5.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.captions.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::ContentAlternatives,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 1.2.2"),
external_level: Some("A"),
axe_id: Some("video-caption"),
title: "Fehlende Untertitel für Videos",
title_en: "Missing captions for videos",
description: "Für aufgezeichnete Videoinhalte sind keine Untertitel vorhanden.",
user_impact: "Gehörlose oder schwerhörige Nutzer können gesprochene Inhalte nicht wahrnehmen.",
user_impact_en: "Deaf or hard-of-hearing users cannot perceive spoken content.",
technical_impact: "Fehlendes track-Element für Untertitel.",
technical_impact_en: "Missing track element for captions.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.media_alternative.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::ContentAlternatives,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 1.2.8"),
external_level: Some("AAA"),
axe_id: Some("media-alt"),
title: "Fehlende Medienalternative (AAA)",
title_en: "Missing media alternative (AAA)",
description: "Für aufgezeichnete Videos oder Audios ist keine vollständige Medienalternative vorhanden.",
user_impact: "Nutzer, die keine Videos oder Audios abspielen können, erhalten keine Textalternative.",
user_impact_en: "Users who cannot play video or audio receive no text alternative.",
technical_impact: "Fehlendes Transkript oder Audiobeschreibung.",
technical_impact_en: "Missing transcript or audio description.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.structure.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: None,
title: "Fehlende semantische Struktur",
title_en: "Missing semantic structure",
description: "Struktur ist visuell, aber nicht im HTML-Code hinterlegt.",
user_impact: "Screenreader können Beziehungen zwischen Inhalten nicht erkennen.",
user_impact_en: "Screen readers cannot detect relationships between content elements.",
technical_impact: "Fehlende HTML5-Semantik und ARIA-Rollen.",
technical_impact_en: "Missing HTML5 semantics and ARIA roles.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.presentation_semantic_children.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("presentation-semantic-children"),
title: "Semantische Kinder in presentational Container",
title_en: "Semantic children inside presentational container",
description: "Ein Element mit role=\"presentation\" oder role=\"none\" enthält semantische Nachfahren.",
user_impact: "Screenreader können relevante Struktur oder Bedienobjekte verlieren.",
user_impact_en: "Screen readers may lose relevant structure or interactive controls.",
technical_impact: "role=\"presentation\"/\"none\" verdeckt semantische Kindelemente.",
technical_impact_en: "role=\"presentation\"/\"none\" hides semantic child elements.",
score_impact: ScoreImpact {
base_penalty: 1.5,
max_penalty: 4.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.meaningful_sequence.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.2"),
external_level: Some("A"),
axe_id: Some("css-order-reading-sequence"),
title: "Visuelle Reihenfolge weicht von der Vorlese-Reihenfolge ab",
title_en: "Visual order diverges from the reading order",
description: "Ein Flex-Container nutzt CSS `order`, um Kindelemente visuell umzusortieren, ohne dies per `aria-owns` im Accessibility-Baum nachzuziehen.",
user_impact: "Screenreader-Nutzer hören Inhalte in einer anderen Reihenfolge, als sie sehende Nutzer visuell wahrnehmen.",
user_impact_en: "Screen reader users hear content in a different sequence than sighted users perceive visually.",
technical_impact: "CSS `order` verändert die visuelle Reihenfolge, der DOM- und Accessibility-Baum bleiben unverändert.",
technical_impact_en: "CSS `order` changes the visual sequence while the DOM and accessibility tree stay unchanged.",
score_impact: ScoreImpact {
base_penalty: 1.0,
max_penalty: 3.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.headings.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 2.4.6"),
external_level: Some("AA"),
axe_id: Some("heading-order"),
title: "Fehlende oder unzureichende Überschriftenstruktur",
title_en: "Missing or insufficient heading structure",
description: "Keine oder unlogische Überschriftenhierarchie.",
user_impact: "Screenreader-Navigation per Überschrift nicht möglich.",
user_impact_en: "Heading-based screen reader navigation is not possible.",
technical_impact: "Fehlende Heading-Hierarchie im DOM.",
technical_impact_en: "Missing heading hierarchy in the DOM.",
score_impact: ScoreImpact {
base_penalty: 20.0,
max_penalty: 20.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.section_headings.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Missing,
severity: Severity::Low,
external_ref: Some("WCAG 2.4.10"),
external_level: Some("AAA"),
axe_id: Some("heading-order"),
title: "Fehlende Abschnittsüberschriften",
title_en: "Missing section headings",
description: "Längere Inhalte sind nicht durch Abschnittsüberschriften gegliedert.",
user_impact: "Nutzer können Inhalte nicht gezielt ansteuern.",
user_impact_en: "Users cannot jump directly to specific content.",
technical_impact: "Fehlende Sub-Headings in langen Inhaltsblöcken.",
technical_impact_en: "Missing sub-headings in long content blocks.",
score_impact: ScoreImpact {
base_penalty: 1.0,
max_penalty: 3.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.orientation.restricted",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Weak,
severity: Severity::High,
external_ref: Some("WCAG 1.3.4"),
external_level: Some("AA"),
axe_id: Some("css-orientation-lock"),
title: "Eingeschränkte Bildschirmausrichtung",
title_en: "Restricted screen orientation",
description: "Die Seite erzwingt eine bestimmte Bildschirmausrichtung (Hoch- oder Querformat).",
user_impact: "Nutzer mit fest montierten Bildschirmen (z. B. am Rollstuhl) können die Seite nicht lesen.",
user_impact_en: "Users with fixed-mount screens (e.g. wheelchair-mounted) cannot read the page.",
technical_impact: "CSS-Medienabfragen blockieren die Ausrichtung.",
technical_impact_en: "CSS media queries lock the orientation.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.keyboard.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Missing,
severity: Severity::Critical,
external_ref: Some("WCAG 2.1.1"),
external_level: Some("A"),
axe_id: Some("keyboard"),
title: "Inhalte nicht per Tastatur bedienbar",
title_en: "Content not operable by keyboard",
description: "Funktionen nur mit Maus bedienbar.",
user_impact: "Tastaturnutzer sind von Funktionen ausgeschlossen.",
user_impact_en: "Keyboard users are excluded from functionality.",
technical_impact: "Click-Handler ohne Tastatur-Alternative.",
technical_impact_en: "Click handler without keyboard alternative.",
score_impact: ScoreImpact {
base_penalty: 5.0,
max_penalty: 15.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.keyboard_trap.risk",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Risk,
severity: Severity::Critical,
external_ref: Some("WCAG 2.1.2"),
external_level: Some("A"),
axe_id: Some("keyboard-trap"),
title: "Tastaturfalle — Fokus kann nicht verlassen werden",
title_en: "Keyboard trap — focus cannot leave the element",
description: "Tastaturfokus bleibt an bestimmten Stellen hängen.",
user_impact: "Seite wird faktisch unbenutzbar für Tastaturnutzer.",
user_impact_en: "The page becomes effectively unusable for keyboard users.",
technical_impact: "Fehlende Fokus-Verwaltung in modalen Elementen.",
technical_impact_en: "Missing focus management in modal elements.",
score_impact: ScoreImpact {
base_penalty: 10.0,
max_penalty: 15.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.bypass_blocks.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 2.4.1"),
external_level: Some("A"),
axe_id: Some("bypass"),
title: "Fehlende Sprungnavigation",
title_en: "Missing bypass navigation",
description: "Kein Skip-Link oder Mechanismus zum Überspringen wiederkehrender Blöcke.",
user_impact: "Tastaturnutzer müssen bei jedem Seitenwechsel durch die Navigation tabben.",
user_impact_en: "Keyboard users must tab through the navigation on every page change.",
technical_impact: "Fehlender Skip-Link, fehlende HTML5-Landmarks.",
technical_impact_en: "Missing skip link, missing HTML5 landmarks.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 8.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.page_title.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 2.4.2"),
external_level: Some("A"),
axe_id: Some("document-title"),
title: "Fehlender oder unzureichender Seitentitel",
title_en: "Missing or insufficient page title",
description: "Seite hat keinen aussagekräftigen Title-Tag.",
user_impact: "Screenreader-Nutzer können die Seite nicht identifizieren.",
user_impact_en: "Screen reader users cannot identify the page.",
technical_impact: "Leeres oder generisches <title>-Tag.",
technical_impact_en: "Empty or generic <title> tag.",
score_impact: ScoreImpact {
base_penalty: 5.0,
max_penalty: 5.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.landmark_main.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("landmark-main-present"),
title: "Fehlender Hauptbereich",
title_en: "Missing main landmark",
description: "Die Seite hat kein <main>-Element oder role=\"main\".",
user_impact: "Screenreader- und Tastaturnutzer können nicht direkt zum Hauptinhalt springen.",
user_impact_en: "Screen reader and keyboard users cannot jump directly to the main content.",
technical_impact: "Fehlendes main-Landmark im DOM oder Accessibility Tree.",
technical_impact_en: "Missing main landmark in the DOM or accessibility tree.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 8.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.landmark_unique.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("landmark-unique"),
title: "Landmarks nicht eindeutig benannt",
title_en: "Landmarks are not uniquely named",
description: "Mehrere Landmarks desselben Typs haben denselben zugänglichen Namen.",
user_impact: "Screenreader-Nutzer können gleichartige Bereiche nicht unterscheiden.",
user_impact_en: "Screen reader users cannot distinguish similar regions.",
technical_impact: "Mehrfach verwendete Landmark-Rollen ohne eindeutige aria-label/aria-labelledby-Werte.",
technical_impact_en: "Repeated landmark roles without unique aria-label/aria-labelledby values.",
score_impact: ScoreImpact {
base_penalty: 1.5,
max_penalty: 4.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.focus_order.weak",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Weak,
severity: Severity::High,
external_ref: Some("WCAG 2.4.3"),
external_level: Some("A"),
axe_id: Some("focus-order-semantics"),
title: "Unlogische Fokus-Reihenfolge",
title_en: "Illogical focus order",
description: "Tab-Reihenfolge entspricht nicht der visuellen Reihenfolge.",
user_impact: "Verwirrende Navigation für Tastaturnutzer.",
user_impact_en: "Confusing navigation for keyboard users.",
technical_impact: "DOM-Reihenfolge weicht von visueller Reihenfolge ab.",
technical_impact_en: "DOM order diverges from the visual order.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.link_purpose.weak",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: Some("WCAG 2.4.4"),
external_level: Some("A"),
axe_id: Some("link-name"),
title: "Unklare oder generische Linktexte",
title_en: "Unclear or generic link text",
description: "Links mit Texten wie 'hier', 'mehr', 'weiterlesen'.",
user_impact: "Screenreader-Linklisten zeigen nichtssagende Texte.",
user_impact_en: "Screen reader link lists show meaningless text.",
technical_impact: "Generische Link-Texte ohne Kontext.",
technical_impact_en: "Generic link text without context.",
score_impact: ScoreImpact {
base_penalty: 1.0,
max_penalty: 5.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.focus_visible.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 2.4.7"),
external_level: Some("AA"),
axe_id: Some("focus-visible"),
title: "Fokus-Indikator nicht sichtbar",
title_en: "Focus indicator not visible",
description: "Kein visueller Fokus-Rahmen bei Tastaturnavigation.",
user_impact: "Tastaturnutzer sehen nicht, welches Element fokussiert ist.",
user_impact_en: "Keyboard users cannot see which element is focused.",
technical_impact: "CSS outline:none ohne Alternative.",
technical_impact_en: "CSS outline:none without an alternative.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 8.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.focus_not_obscured_minimum.hidden",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: Some("WCAG 2.4.11"),
external_level: Some("AA"),
axe_id: Some("focus-not-obscured-minimum"),
title: "Fokussiertes Element vollständig verdeckt",
title_en: "Focused element entirely obscured",
description: "Ein fokussierbares Element wird an seiner aktuellen Layout-Position vollständig von einem fixierten oder sticky-positionierten Element überdeckt.",
user_impact: "Tastaturnutzer springen per Tab auf ein Element, das durch einen Sticky-Header, -Footer, ein Cookie-Banner o. ä. vollständig verdeckt ist und daher nicht sichtbar ist.",
user_impact_en: "Keyboard users tab to an element that is entirely covered by a sticky header, footer, cookie banner, or similar overlay and is therefore not visible.",
technical_impact: "position: fixed/sticky-Element überlappt vollständig mit einem fokussierbaren Element.",
technical_impact_en: "A position: fixed/sticky element fully overlaps a focusable element.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 2.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.focus_not_obscured_enhanced.hidden",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: Some("WCAG 2.4.12"),
external_level: Some("AAA"),
axe_id: Some("focus-not-obscured-enhanced"),
title: "Fokussiertes Element teilweise verdeckt",
title_en: "Focused element partially obscured",
description: "Ein fokussierbares Element wird an seiner aktuellen Layout-Position zumindest teilweise von einem fixierten oder sticky-positionierten Element überdeckt.",
user_impact: "Tastaturnutzer springen per Tab auf ein Element, dessen sichtbarer Bereich durch einen Sticky-Header, -Footer, ein Cookie-Banner o. ä. teilweise verdeckt wird.",
user_impact_en: "Keyboard users tab to an element whose visible area is partially covered by a sticky header, footer, cookie banner, or similar overlay.",
technical_impact: "position: fixed/sticky-Element überlappt teilweise mit einem fokussierbaren Element.",
technical_impact_en: "A position: fixed/sticky element partially overlaps a focusable element.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 2.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.label_in_name.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Invalid,
severity: Severity::High,
external_ref: Some("WCAG 2.5.3"),
external_level: Some("A"),
axe_id: Some("label-in-name"),
title: "Sichtbarer Text ≠ zugänglicher Name",
title_en: "Visible text ≠ accessible name",
description: "Sichtbarer Text und aria-label stimmen nicht überein.",
user_impact: "Sprachsteuerungsnutzer können Element nicht ansprechen.",
user_impact_en: "Voice-control users cannot address the element.",
technical_impact: "aria-label weicht vom sichtbaren Text ab.",
technical_impact_en: "aria-label diverges from the visible text.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.timing.unadjustable",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Weak,
severity: Severity::High,
external_ref: Some("WCAG 2.2.1"),
external_level: Some("A"),
axe_id: Some("meta-refresh"),
title: "Nicht anpassbare Zeitbegrenzung",
title_en: "Non-adjustable time limit",
description: "Zeitbegrenzungen auf der Seite können nicht deaktiviert oder verlängert werden.",
user_impact: "Nutzer benötigen mehr Zeit für die Eingabe und können durch Timeouts Daten verlieren.",
user_impact_en: "Users need more time to enter data and may lose data through timeouts.",
technical_impact: "Fehlendes Refresh- oder Timeout-Handling im Script/Markup.",
technical_impact_en: "Missing refresh or timeout handling in the script/markup.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.pause_stop_hide.no_control",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 2.2.2"),
external_level: Some("A"),
axe_id: Some("pause-stop-hide"),
title: "Fehlende Pause-/Stopp-/Ausblenden-Kontrolle",
title_en: "Missing pause/stop/hide control",
description: "Automatisch startender, dauerhaft laufender Inhalt (z. B. `<marquee>` oder eine unendlich wiederholende CSS-Animation über 5 Sekunden pro Zyklus) bietet keine erkennbare Möglichkeit, ihn zu pausieren, zu stoppen oder auszublenden.",
user_impact: "Nutzer mit Aufmerksamkeits- oder kognitiven Einschränkungen sowie Screenreader-Nutzer werden durch dauerhaft laufende Bewegung abgelenkt oder gestört und können sie nicht deaktivieren.",
user_impact_en: "Users with attention or cognitive impairments, and screen reader users, are distracted or disrupted by continuously running motion they cannot turn off.",
technical_impact: "`<marquee>`-Element ohne Pause-Steuerung oder Element mit animation-iteration-count: infinite über 5 Sekunden pro Zyklus ohne erkennbare Pause-Kontrolle oder prefers-reduced-motion-Unterstützung.",
technical_impact_en: "`<marquee>` element without pause control, or an element with animation-iteration-count: infinite exceeding 5 seconds per cycle with no detectable pause control or prefers-reduced-motion support.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 2.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.timing.essential",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Risk,
severity: Severity::Medium,
external_ref: Some("WCAG 2.2.3"),
external_level: Some("AAA"),
axe_id: Some("no-timing"),
title: "Zeitbegrenzung vorhanden (AAA)",
title_en: "Timing present (AAA)",
description: "Die Seite verwendet essenzielle Zeitbegrenzungen ohne Möglichkeit der Deaktivierung.",
user_impact: "Nutzer können Aufgaben nicht in ihrem eigenen Tempo abschließen.",
user_impact_en: "Users cannot complete tasks at their own pace.",
technical_impact: "Aktive Zeitbegrenzungen auf der Seite.",
technical_impact_en: "Active time limits on the page.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.interruptions.uncontrollable",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Risk,
severity: Severity::Low,
external_ref: Some("WCAG 2.2.4"),
external_level: Some("AAA"),
axe_id: Some("no-interruptions"),
title: "Nicht unterdrückbare Unterbrechungen (AAA)",
title_en: "Non-suppressible interruptions (AAA)",
description: "Die Seite zeigt Warnungen oder Popups, die der Nutzer nicht verschieben kann.",
user_impact: "Nutzer mit kognitiven Einschränkungen oder Screenreader-Nutzer werden abgelenkt.",
user_impact_en: "Users with cognitive impairments or screen reader users are distracted.",
technical_impact: "Unerwartete Dialoge oder assertive Live-Regionen.",
technical_impact_en: "Unexpected dialogs or assertive live regions.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.re_authenticate.data_loss",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Risk,
severity: Severity::Low,
external_ref: Some("WCAG 2.2.5"),
external_level: Some("AAA"),
axe_id: Some("re-authenticate"),
title: "Keine Datenspeicherung bei Reauthentifizierung (AAA)",
title_en: "No data preservation on re-authentication (AAA)",
description: "Eingegebene Daten gehen verloren, wenn eine Session abläuft und sich der Nutzer neu anmeldet.",
user_impact: "Nutzer müssen alle Formulardaten erneut eingeben.",
user_impact_en: "Users must re-enter all form data.",
technical_impact: "Fehlende Session-Datensicherung.",
technical_impact_en: "Missing session data preservation.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.timeouts.unwarned",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Risk,
severity: Severity::Medium,
external_ref: Some("WCAG 2.2.6"),
external_level: Some("AAA"),
axe_id: Some("timeouts"),
title: "Keine Warnung vor Inaktivitäts-Timeout (AAA)",
title_en: "No warning before inactivity timeout (AAA)",
description: "Der Nutzer wird vor einem Timeout nicht über drohenden Datenverlust gewarnt.",
user_impact: "Nutzer verlieren ungespeicherte Daten ohne Vorwarnung.",
user_impact_en: "Users lose unsaved data without warning.",
technical_impact: "Fehlendes Timeout-Warnsystem.",
technical_impact_en: "Missing timeout warning system.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.location.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Missing,
severity: Severity::Low,
external_ref: Some("WCAG 2.4.8"),
external_level: Some("AAA"),
axe_id: Some("location"),
title: "Fehlende Positionsangabe (AAA)",
title_en: "Missing location indication (AAA)",
description: "Es gibt keine Orientierungshilfe (wie Breadcrumbs), um den aktuellen Ort in einem Webauftritt anzuzeigen.",
user_impact: "Nutzer verlieren die Orientierung auf verschachtelten Webseiten.",
user_impact_en: "Users lose orientation within nested websites.",
technical_impact: "Fehlende Breadcrumb-Navigation oder Sitemap-Verbindung.",
technical_impact_en: "Missing breadcrumb navigation or sitemap linkage.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.link_purpose_only.weak",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: Some("WCAG 2.4.9"),
external_level: Some("AAA"),
axe_id: Some("link-name-only"),
title: "Unklare Linkbeschriftung ohne Kontext (AAA)",
title_en: "Unclear link label without context (AAA)",
description: "Der Zweck eines Links geht nicht allein aus dem Linktext hervor.",
user_impact: "Nutzer müssen den gesamten Absatz lesen, um das Ziel eines Links zu verstehen.",
user_impact_en: "Users must read the entire paragraph to understand a link's destination.",
technical_impact: "Generischer Linktext wie 'Mehr lesen' oder 'Hier klicken'.",
technical_impact_en: "Generic link text such as 'read more' or 'click here'.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.pointer_gestures.missing_alternative",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 2.5.1"),
external_level: Some("A"),
axe_id: Some("pointer-gestures"),
title: "Fehlende Alternative für Zeigergesten",
title_en: "Missing alternative for pointer gestures",
description: "Funktionen erfordern komplexe Mehrfinger- oder Pfadgesten ohne einfache Klick-Alternative.",
user_impact: "Nutzer mit motorischen Einschränkungen können die Funktionen nicht bedienen.",
user_impact_en: "Users with motor impairments cannot operate the functionality.",
technical_impact: "Erforderliche Touch-Gesten ohne Klick-Fallback.",
technical_impact_en: "Required touch gestures without a click fallback.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.pointer_cancellation.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 2.5.2"),
external_level: Some("A"),
axe_id: Some("pointer-cancellation"),
title: "Auslösung von Aktionen bei Zeigerberührung",
title_en: "Action triggered on pointer down",
description: "Aktionen werden sofort beim Drücken (down-event) statt beim Loslassen (up-event) ausgeführt.",
user_impact: "Nutzer können versehentlich Aktionen auslösen, ohne diese abbrechen zu können.",
user_impact_en: "Users may trigger actions accidentally without being able to cancel them.",
technical_impact: "Event-Handler reagieren auf mousedown oder touchstart statt click oder mouseup.",
technical_impact_en: "Event handlers respond to mousedown or touchstart instead of click or mouseup.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.motion_actuation.missing_alternative",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 2.5.4"),
external_level: Some("A"),
axe_id: Some("motion-actuation"),
title: "Fehlende Alternative für Bewegungseingabe",
title_en: "Missing alternative for motion actuation",
description: "Aktionen, die durch Schütteln oder Bewegen des Geräts ausgelöst werden, haben keine klassische Bedienalternative.",
user_impact: "Nutzer, die das Gerät nicht bewegen können, sind von den Funktionen ausgeschlossen.",
user_impact_en: "Users who cannot move the device are excluded from the functionality.",
technical_impact: "Nutzen von Device-Motion-APIs ohne UI-Bedienelemente.",
technical_impact_en: "Use of device-motion APIs without UI controls.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.target_size_enhanced.small",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: Some("WCAG 2.5.5"),
external_level: Some("AAA"),
axe_id: Some("target-size"),
title: "Unzureichende Klickzielgröße (AAA)",
title_en: "Insufficient click target size (AAA)",
description: "Bedienelemente haben eine Klickfläche von weniger als 44x44 CSS-Pixeln.",
user_impact: "Nutzer mit zittrigen Händen oder auf Mobilgeräten klicken leicht auf falsche Elemente.",
user_impact_en: "Users with hand tremors or on mobile devices easily tap the wrong elements.",
technical_impact: "Zu kleine Abmessungen von Links, Buttons oder Formularelementen im CSS.",
technical_impact_en: "Links, buttons or form controls dimensioned too small in CSS.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.target_size_minimum.small",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: Some("WCAG 2.5.8"),
external_level: Some("AA"),
axe_id: Some("target-size-minimum"),
title: "Unzureichende Klickzielgröße",
title_en: "Insufficient click target size",
description: "Bedienelemente haben eine Klickfläche von weniger als 24x24 CSS-Pixeln.",
user_impact: "Nutzer mit zittrigen Händen oder auf Mobilgeräten klicken leicht auf falsche Elemente.",
user_impact_en: "Users with hand tremors or on mobile devices easily tap the wrong elements.",
technical_impact: "Zu kleine Abmessungen von Links, Buttons oder Formularelementen im CSS.",
technical_impact_en: "Links, buttons or form controls dimensioned too small in CSS.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.input_purpose.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::FormsInteraction,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.5"),
external_level: Some("AA"),
axe_id: Some("autocomplete-valid"),
title: "Fehlende Eingabezweck-Kennzeichnung",
title_en: "Missing input purpose identification",
description: "Formularfelder ohne autocomplete-Attribut.",
user_impact: "Kein Autofill möglich, Eingabe fehleranfälliger.",
user_impact_en: "No autofill possible, data entry more error-prone.",
technical_impact: "Fehlende autocomplete-Attribute.",
technical_impact_en: "Missing autocomplete attributes.",
score_impact: ScoreImpact {
base_penalty: 1.0,
max_penalty: 5.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.form_labels.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::FormsInteraction,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 3.3.2"),
external_level: Some("A"),
axe_id: Some("label"),
title: "Fehlende Formularbeschriftungen",
title_en: "Missing form labels",
description: "Formularfelder ohne sichtbare Labels.",
user_impact: "Nutzer wissen nicht, welche Eingaben erwartet werden.",
user_impact_en: "Users do not know what input is expected.",
technical_impact: "Fehlende <label>-Elemente.",
technical_impact_en: "Missing <label> elements.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.redundant_entry.missing_reuse",
dimension: Dimension::Accessibility,
subcategory: Subcategory::FormsInteraction,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: Some("WCAG 3.3.7"),
external_level: Some("A"),
axe_id: Some("redundant-entry"),
title: "Fehlende Wiederverwendung bereits eingegebener Daten",
title_en: "Missing reuse of previously entered data",
description: "Formular fragt dieselbe Information (z. B. E-Mail, Name, Adresse) mehrfach ab, ohne Autocomplete, Vorbefüllung oder eine \"wie oben\"-Option anzubieten.",
user_impact: "Nutzer müssen bereits eingegebene Daten erneut eintippen, was Zeit kostet und besonders für Menschen mit motorischen oder kognitiven Einschränkungen eine zusätzliche Hürde darstellt.",
user_impact_en: "Users must retype data they already entered, which costs time and is an extra burden especially for people with motor or cognitive impairments.",
technical_impact: "Formularfelder ohne autocomplete-Attribut, Vorbefüllung oder Wiederverwendungs-Option für bereits erfasste Werte.",
technical_impact_en: "Form fields without an autocomplete attribute, prefilled value, or reuse option for already-captured values.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 2.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.accessible_auth.paste_blocked",
dimension: Dimension::Accessibility,
subcategory: Subcategory::FormsInteraction,
issue_class: IssueClass::Invalid,
severity: Severity::High,
external_ref: Some("WCAG 3.3.8"),
external_level: Some("AA"),
axe_id: Some("accessible-auth-paste-blocked"),
title: "Einfügen in Passwort- oder Code-Feld blockiert",
title_en: "Paste blocked in password or code field",
description: "Ein Passwort- oder Einmalcode-Feld unterbindet das Einfügen, sodass Zugangsdaten abgetippt oder auswendig eingegeben werden müssen.",
user_impact: "Nutzer können weder einen Passwort-Manager noch die Zwischenablage verwenden. Wer sich Passwörter nicht merken oder fehlerfrei abschreiben kann, etwa wegen kognitiver oder motorischer Einschränkungen, scheitert an der Anmeldung.",
user_impact_en: "Users can use neither a password manager nor the clipboard. Anyone who cannot memorise passwords or transcribe them accurately, for example because of cognitive or motor impairments, fails to sign in.",
technical_impact: "paste-Handler mit preventDefault() oder return false am Eingabefeld.",
technical_impact_en: "A paste handler calling preventDefault() or returning false on the input field.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.accessible_auth.captcha_review",
dimension: Dimension::Accessibility,
subcategory: Subcategory::FormsInteraction,
issue_class: IssueClass::Risk,
severity: Severity::Medium,
external_ref: Some("WCAG 3.3.8"),
external_level: Some("AA"),
axe_id: Some("accessible-auth-captcha"),
title: "CAPTCHA im Anmeldeformular prüfen",
title_en: "Review CAPTCHA in authentication form",
description: "Ein Anmeldeformular enthält ein CAPTCHA. Ob es eine Objekterkennung ist oder eine Alternative ohne kognitiven Test angeboten wird, lässt sich nicht automatisch feststellen.",
user_impact: "Text- und Rätsel-CAPTCHAs sperren Menschen mit kognitiven Einschränkungen, Legasthenie oder Sehbehinderung von der Anmeldung aus, wenn es keine Alternative gibt.",
user_impact_en: "Text and puzzle CAPTCHAs lock people with cognitive impairments, dyslexia or low vision out of signing in when no alternative is offered.",
technical_impact: "Interaktives CAPTCHA-Widget (reCAPTCHA, hCaptcha, Bild-CAPTCHA) in einem Formular mit Passwort- oder Einmalcode-Feld.",
technical_impact_en: "Interactive CAPTCHA widget (reCAPTCHA, hCaptcha, image CAPTCHA) in a form with a password or one-time-code field.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 2.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.form_no_submit.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::FormsInteraction,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 3.2.2"),
external_level: Some("A"),
axe_id: Some("form-no-submit"),
title: "Formular ohne Absende-Button",
title_en: "Form without submit button",
description: "Formular mit Eingabefeldern hat keinen expliziten Submit-Button.",
user_impact: "Tastatur- und Screenreader-Nutzer erkennen den Abschluss der Eingabe schwerer.",
user_impact_en: "Keyboard and screen reader users have a harder time completing the form.",
technical_impact: "Fehlendes Submit-Control im Formular.",
technical_impact_en: "Missing submit control in the form.",
score_impact: ScoreImpact {
base_penalty: 1.5,
max_penalty: 4.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.error_id.missing_description",
dimension: Dimension::Accessibility,
subcategory: Subcategory::FormsInteraction,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 3.3.1"),
external_level: Some("A"),
axe_id: Some("aria-invalid-without-describedby"),
title: "Unzureichende Fehlermeldung bei Eingabefehlern",
title_en: "Insufficient error message on input errors",
description: "Fehlerhafte Formulareingaben werden nicht präzise identifiziert oder beschrieben.",
user_impact: "Nutzer können Fehler im Formular nicht korrigieren, weil sie nicht wissen, was falsch ist.",
user_impact_en: "Users cannot correct form errors because they do not know what is wrong.",
technical_impact: "Fehlendes aria-invalid-Attribut oder fehlerhaft verknüpfte aria-describedby-IDs.",
technical_impact_en: "Missing aria-invalid attribute or incorrectly linked aria-describedby IDs.",
score_impact: ScoreImpact {
base_penalty: 2.0,
max_penalty: 5.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.help.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::FormsInteraction,
issue_class: IssueClass::Missing,
severity: Severity::Low,
external_ref: Some("WCAG 3.3.5"),
external_level: Some("AAA"),
axe_id: Some("help"),
title: "Fehlende Ausfüllhilfe (AAA)",
title_en: "Missing input help (AAA)",
description: "Für Eingabefelder gibt es keine kontextsensitive Ausfüllhilfe.",
user_impact: "Nutzer wissen nicht, welches Format oder welche Daten in ein Feld eingegeben werden müssen.",
user_impact_en: "Users do not know what format or data to enter in a field.",
technical_impact: "Fehlendes Formular-Hilfesystem.",
technical_impact_en: "Missing form help system.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.language.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::LanguageClarity,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 3.1.1"),
external_level: Some("A"),
axe_id: Some("document/lang-missing"),
title: "Fehlende Sprachangabe",
title_en: "Missing language declaration",
description: "Kein lang-Attribut im HTML-Tag.",
user_impact: "Screenreader liest mit falscher Aussprache vor.",
user_impact_en: "Screen readers read aloud with incorrect pronunciation.",
technical_impact: "Fehlendes lang-Attribut.",
technical_impact_en: "Missing lang attribute.",
score_impact: ScoreImpact {
base_penalty: 10.0,
max_penalty: 12.5,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.on_focus.risk",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Risk,
severity: Severity::High,
external_ref: Some("WCAG 3.2.1"),
external_level: Some("A"),
axe_id: Some("focus-no-context-change"),
title: "Unerwartete Kontextänderung bei Fokus",
title_en: "Unexpected context change on focus",
description: "Fokuserhalt löst unerwartete Kontextänderung aus.",
user_impact: "Verwirrende Seitenänderungen für Tastaturnutzer.",
user_impact_en: "Confusing page changes for keyboard users.",
technical_impact: "onfocus-Handler lösen Navigation aus.",
technical_impact_en: "onfocus handlers trigger navigation.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 8.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.on_input.risk",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Risk,
severity: Severity::Medium,
external_ref: Some("WCAG 3.2.2"),
external_level: Some("A"),
axe_id: Some("input-no-context-change"),
title: "Unerwartete Kontextänderung bei Eingabe",
title_en: "Unexpected context change on input",
description: "Formularänderung löst unangekündigte Kontextänderung aus.",
user_impact: "Verwirrende Änderungen beim Ausfüllen.",
user_impact_en: "Confusing changes while filling out forms.",
technical_impact: "Auto-submit bei onchange.",
technical_impact_en: "Auto-submit on onchange.",
score_impact: ScoreImpact {
base_penalty: 2.0,
max_penalty: 5.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.unusual_words.missing_definition",
dimension: Dimension::Accessibility,
subcategory: Subcategory::LanguageClarity,
issue_class: IssueClass::Missing,
severity: Severity::Low,
external_ref: Some("WCAG 3.1.3"),
external_level: Some("AAA"),
axe_id: Some("unusual-words"),
title: "Fehlende Erklärung unüblicher Wörter (AAA)",
title_en: "Missing explanation of unusual words (AAA)",
description: "Für ungewöhnliche Wörter, Jargon oder Redewendungen gibt es keine Definitionen.",
user_impact: "Nutzer mit kognitiven Einschränkungen oder Fremdsprachler verstehen den Text nicht.",
user_impact_en: "Users with cognitive impairments or non-native speakers do not understand the text.",
technical_impact: "Fehlende Glossar-Verlinkung oder Erklärung im Text.",
technical_impact_en: "Missing glossary linkage or in-text explanation.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.abbreviations.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::LanguageClarity,
issue_class: IssueClass::Missing,
severity: Severity::Low,
external_ref: Some("WCAG 3.1.4"),
external_level: Some("AAA"),
axe_id: Some("abbreviations"),
title: "Fehlende Erklärung von Abkürzungen (AAA)",
title_en: "Missing explanation of abbreviations (AAA)",
description: "Abkürzungen werden nicht bei ihrer ersten Verwendung oder über ein Mechanismus erklärt.",
user_impact: "Nutzer verstehen den Inhalt nicht, wenn sie die Abkürzung nicht kennen.",
user_impact_en: "Users do not understand the content if they do not know the abbreviation.",
technical_impact: "Fehlendes abbr-Tag mit title-Attribut.",
technical_impact_en: "Missing abbr tag with title attribute.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.contrast.weak",
dimension: Dimension::Accessibility,
subcategory: Subcategory::VisualPresentation,
issue_class: IssueClass::Weak,
severity: Severity::High,
external_ref: Some("WCAG 1.4.3"),
external_level: Some("AA"),
axe_id: Some("color-contrast"),
title: "Unzureichender Farbkontrast",
title_en: "Insufficient color contrast",
description: "Text hat nicht genügend Kontrast zum Hintergrund.",
user_impact: "Text bei Sehschwäche oder ungünstigen Lichtverhältnissen schwer lesbar.",
user_impact_en: "Text is hard to read for people with low vision or under poor lighting conditions.",
technical_impact: "Kontrastverhältnis unter 4.5:1 für normalen Text.",
technical_impact_en: "Contrast ratio below 4.5:1 for normal text.",
score_impact: ScoreImpact {
base_penalty: 5.0,
max_penalty: 12.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.resize_text.weak",
dimension: Dimension::Accessibility,
subcategory: Subcategory::VisualPresentation,
issue_class: IssueClass::Weak,
severity: Severity::High,
external_ref: Some("WCAG 1.4.4"),
external_level: Some("AA"),
axe_id: Some("meta-viewport"),
title: "Text nicht ausreichend skalierbar",
title_en: "Text not sufficiently resizable",
description: "Texte können nicht auf 200% vergrößert werden.",
user_impact: "Nutzer mit Sehschwäche verlieren Inhalte bei Vergrößerung.",
user_impact_en: "Users with low vision lose content when zooming in.",
technical_impact: "Feste Pixelwerte, overflow:hidden.",
technical_impact_en: "Fixed pixel values, overflow:hidden.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.viewport_meta.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::VisualPresentation,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 1.4.4"),
external_level: Some("AA"),
axe_id: Some("zoom/viewport-missing"),
title: "Fehlende Viewport-Angabe",
title_en: "Missing viewport declaration",
description: "Das Dokument hat kein viewport-Meta-Tag. Mobile Browser legen dann eine Desktop-Breite zugrunde und verkleinern die Seite.",
user_impact: "Auf Mobilgeräten erscheint der Text unter jeder lesbaren Größe; wer vergrößert, muss waagerecht scrollen. Am Desktop ändert sich nichts.",
user_impact_en: "On mobile devices the text appears below any readable size; zooming in forces horizontal scrolling. Desktop browsers are not affected.",
technical_impact: "Kein <meta name=\"viewport\"> im Dokument.",
technical_impact_en: "No <meta name=\"viewport\"> in the document.",
score_impact: ScoreImpact {
base_penalty: 1.5,
max_penalty: 3.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.reflow.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::VisualPresentation,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 1.4.10"),
external_level: Some("AA"),
axe_id: Some("css-overflow-hidden"),
title: "Kein Reflow bei 320 CSS-Pixeln",
title_en: "No reflow at 320 CSS pixels",
description: "Seite erfordert horizontales Scrollen bei einer Ansichtsbreite von 320 CSS-Pixeln.",
user_impact: "Nutzer, die auf 400 % zoomen, müssen in zwei Richtungen scrollen — Inhalte werden unzugänglich.",
user_impact_en: "Users zooming to 400% must scroll in two directions — content becomes inaccessible.",
technical_impact: "Feste Breiten oder overflow:hidden verhindern responsives Reflow.",
technical_impact_en: "Fixed widths or overflow:hidden prevent responsive reflow.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.non_text_contrast.weak",
dimension: Dimension::Accessibility,
subcategory: Subcategory::VisualPresentation,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: Some("WCAG 1.4.11"),
external_level: Some("AA"),
axe_id: Some("non-text-contrast"),
title: "Unzureichender Kontrast bei UI-Elementen",
title_en: "Insufficient contrast for UI elements",
description: "Bedienelemente haben nicht genügend Kontrast.",
user_impact: "Interaktive Elemente schwer erkennbar.",
user_impact_en: "Interactive elements are hard to recognize.",
technical_impact: "Kontrastverhältnis UI-Elemente unter 3:1.",
technical_impact_en: "Contrast ratio of UI elements below 3:1.",
score_impact: ScoreImpact {
base_penalty: 1.5,
max_penalty: 5.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.text_spacing.clipped",
dimension: Dimension::Accessibility,
subcategory: Subcategory::VisualPresentation,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 1.4.12"),
external_level: Some("AA"),
axe_id: Some("text-spacing"),
title: "Inhalt bei erhöhtem Zeilen-/Wortabstand abgeschnitten",
title_en: "Content clipped when text spacing is increased",
description: "Ein Container mit overflow:hidden schneidet Text ab, sobald WCAG-Mindestabstände für Zeilenhöhe, Buchstaben- und Wortabstand angewendet werden.",
user_impact: "Nutzer, die Zeilen-/Wortabstand für bessere Lesbarkeit anpassen, verlieren Inhalte.",
user_impact_en: "Users who increase line/letter/word spacing for readability lose content.",
technical_impact: "Fixe Höhe/Breite mit overflow:hidden auf Textcontainern.",
technical_impact_en: "Fixed height/width with overflow:hidden on text containers.",
score_impact: ScoreImpact {
base_penalty: 1.5,
max_penalty: 5.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.color.link_indicator",
dimension: Dimension::Accessibility,
subcategory: Subcategory::VisualPresentation,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: Some("WCAG 1.4.1"),
external_level: Some("A"),
axe_id: Some("link-in-text-block"),
title: "Link nur durch Farbe unterscheidbar",
title_en: "Link distinguishable by color only",
description: "Inline-Link hebt sich vom Fließtext nur durch Farbe ab, ohne weitere visuelle Unterscheidung.",
user_impact: "Nutzer mit Farbsehschwäche können Links im Text nicht erkennen.",
user_impact_en: "Users with color vision deficiency cannot recognize in-text links.",
technical_impact: "Kein Unterstrich, Fettschrift oder anderes nicht-farbliches Merkmal vorhanden.",
technical_impact_en: "No underline, bold weight or other non-color cue present.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.motion.reduced_motion",
dimension: Dimension::Accessibility,
subcategory: Subcategory::VisualPresentation,
issue_class: IssueClass::Missing,
severity: Severity::Low,
external_ref: Some("WCAG 2.3.3"),
external_level: Some("AAA"),
axe_id: Some("prefers-reduced-motion"),
title: "Animationen ohne prefers-reduced-motion",
title_en: "Animations without prefers-reduced-motion",
description: "Seite definiert Animationen oder Übergänge, berücksichtigt aber nicht das Systemsetting prefers-reduced-motion.",
user_impact: "Nutzer mit vestibulären Störungen oder Photosensitivität können Bewegung nicht deaktivieren.",
user_impact_en: "Users with vestibular disorders or photosensitivity cannot disable motion.",
technical_impact: "Kein @media (prefers-reduced-motion) in den Stylesheets.",
technical_impact_en: "No @media (prefers-reduced-motion) in the stylesheets.",
score_impact: ScoreImpact {
base_penalty: 1.0,
max_penalty: 4.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.hover.content_visibility",
dimension: Dimension::Accessibility,
subcategory: Subcategory::VisualPresentation,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: Some("WCAG 1.4.13"),
external_level: Some("AA"),
axe_id: Some("content-on-hover-focus"),
title: "Unzugängliche Hover- oder Fokus-Inhalte (Tooltips)",
title_en: "Inaccessible hover or focus content (tooltips)",
description: "Zusatzinhalte bei Hover/Fokus (Tooltips) sind unzugänglich, nicht steuerbar oder unvollständig verknüpft.",
user_impact: "Nutzer können Tooltip-Inhalte nicht lesen, diese nicht per Tastatur schließen, oder sie werden von Screenreadern ignoriert.",
user_impact_en: "Users cannot read tooltip content, cannot dismiss it via keyboard, or it is ignored by screen readers.",
technical_impact: "Unvollständige ARIA-Verknüpfung (aria-describedby) oder fehlende Steuerungsmöglichkeiten.",
technical_impact_en: "Incomplete ARIA linkage (aria-describedby) or missing controls.",
score_impact: ScoreImpact {
base_penalty: 2.0,
max_penalty: 6.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.background_audio.uncontrolled",
dimension: Dimension::Accessibility,
subcategory: Subcategory::VisualPresentation,
issue_class: IssueClass::Risk,
severity: Severity::Medium,
external_ref: Some("WCAG 1.4.7"),
external_level: Some("AAA"),
axe_id: Some("background-audio"),
title: "Nicht regulierbare Hintergrundgeräusche (AAA)",
title_en: "Uncontrollable background audio (AAA)",
description: "Hintergrundgeräusche können nicht abgeschaltet oder leiser gestellt werden.",
user_impact: "Nutzer mit Hörgeräten oder kognitiven Einschränkungen werden stark abgelenkt.",
user_impact_en: "Users with hearing aids or cognitive impairments are strongly distracted.",
technical_impact: "Automatisch abspielendes Audio ohne Steuerungselemente.",
technical_impact_en: "Auto-playing audio without controls.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.visual_presentation.weak",
dimension: Dimension::Accessibility,
subcategory: Subcategory::VisualPresentation,
issue_class: IssueClass::Weak,
severity: Severity::Low,
external_ref: Some("WCAG 1.4.8"),
external_level: Some("AAA"),
axe_id: Some("visual-presentation"),
title: "Eingeschränkte visuelle Darstellung von Text (AAA)",
title_en: "Limited visual presentation of text (AAA)",
description: "Der Text hat unzureichenden Zeilenabstand oder ist im Blocksatz formatiert.",
user_impact: "Nutzer mit Sehbehinderungen oder Legasthenie haben Probleme beim Lesen.",
user_impact_en: "Users with visual impairments or dyslexia have difficulty reading.",
technical_impact: "Unzureichendes CSS-Styling für Zeilenabstand oder Blocksatz-Formatierung.",
technical_impact_en: "Insufficient CSS styling for line spacing or justified-text formatting.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.aria_hidden_focus.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Invalid,
severity: Severity::High,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria-hidden-focus"),
title: "Fokussierbares Element versteckt vor Screenreadern",
title_en: "Focusable element hidden from screen readers",
description: "Interaktives Element ist über aria-hidden=\"true\" vor AT versteckt, aber per Tastatur erreichbar.",
user_impact: "Tastaturnutzer erreichen Elemente, die Screenreader nicht ankündigen — unsichtbare Stolperfallen.",
user_impact_en: "Keyboard users reach elements that screen readers do not announce — invisible traps.",
technical_impact: "aria-hidden=\"true\" auf fokussierbarem Element oder seinem Vorfahren.",
technical_impact_en: "aria-hidden=\"true\" on a focusable element or its ancestor.",
score_impact: ScoreImpact {
base_penalty: 6.0,
max_penalty: 15.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.status_messages.broken",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 4.1.3"),
external_level: Some("AA"),
axe_id: Some("aria-live-region-role"),
title: "Statusmeldungen nicht programmatisch bestimmbar",
title_en: "Status messages not programmatically determinable",
description: "ARIA-Live-Regionen haben eine inkonsistente Kombination aus role und aria-live.",
user_impact: "Screenreader kündigt Statusmeldungen nicht korrekt an.",
user_impact_en: "Screen readers do not announce status messages correctly.",
technical_impact: "Konflikt zwischen role (alert/status/log/timer) und aria-live-Wert.",
technical_impact_en: "Conflict between role (alert/status/log/timer) and aria-live value.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.identify_purpose.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Missing,
severity: Severity::Low,
external_ref: Some("WCAG 1.3.6"),
external_level: Some("AAA"),
axe_id: None,
title: "Landmark ohne unterscheidbaren Namen",
title_en: "Landmark without distinguishable name",
description: "Mehrere gleichartige Landmarks ohne aria-label zur Unterscheidung.",
user_impact: "Screenreader-Nutzer können identische Landmarks nicht voneinander unterscheiden.",
user_impact_en: "Screen reader users cannot tell identical landmarks apart.",
technical_impact: "Fehlende aria-label/aria-labelledby auf doppelten Landmarks.",
technical_impact_en: "Missing aria-label/aria-labelledby on duplicate landmarks.",
score_impact: ScoreImpact {
base_penalty: 1.0,
max_penalty: 4.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.aria_required_attr.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Missing,
severity: Severity::Critical,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria-required-attr"),
title: "Pflicht-ARIA-Attribut fehlt",
title_en: "Required ARIA attribute missing",
description:
"Ein Element mit ARIA-Rolle besitzt nicht alle fuer diese Rolle erforderlichen Attribute.",
user_impact: "Assistive Technologien koennen Zustand oder Wert des Elements nicht verlaesslich vermitteln.",
user_impact_en:
"Assistive technologies cannot reliably expose the element state or value.",
technical_impact: "Rolle und ARIA-Attributsatz sind unvollstaendig.",
technical_impact_en: "Role and ARIA attribute set are incomplete.",
score_impact: ScoreImpact {
base_penalty: 6.0,
max_penalty: 15.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.name_role.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: None,
title: "Fehlende Name/Rolle bei Bedienelementen",
title_en: "Missing name/role on controls",
description: "Interaktive Elemente ohne zugänglichen Namen oder erkennbare Rolle.",
user_impact: "Screenreader kann Funktion nicht vermitteln.",
user_impact_en: "Screen readers cannot convey the function.",
technical_impact: "Fehlende ARIA-Labels und -Rollen.",
technical_impact_en: "Missing ARIA labels and roles.",
score_impact: ScoreImpact {
base_penalty: 5.0,
max_penalty: 12.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.aria_required_parent.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Missing,
severity: Severity::Critical,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria-required-parent"),
title: "Element außerhalb des erforderlichen Eltern-Kontexts",
title_en: "Element outside its required parent context",
description: "Ein Element mit einer ARIA-Rolle, die einen bestimmten Eltern-Kontext voraussetzt (z. B. tab, option, gridcell), hat keinen passenden Vorfahren mit dieser Rolle.",
user_impact: "Assistive Technologien können die Beziehung des Elements zu seiner Gruppe (z. B. Tab zu Tableiste) nicht vermitteln.",
user_impact_en:
"Assistive technologies cannot convey the element's relationship to its group (e.g. a tab to its tab list).",
technical_impact: "Fehlender struktureller Eltern-Kontext für die verwendete ARIA-Rolle.",
technical_impact_en: "Missing structural parent context for the ARIA role in use.",
score_impact: ScoreImpact {
base_penalty: 6.0,
max_penalty: 15.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.modern_attributes.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Invalid,
severity: Severity::High,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("modern-attribute-misuse"),
title: "Ungültige moderne Interaktionsattribute",
title_en: "Invalid modern interaction attributes",
description: "popover-, inert- oder Dialog-Attribute erzeugen eine nicht programmatisch bestimmbare Interaktion.",
user_impact: "Screenreader- und Tastaturnutzer erreichen Dialoge, Popover oder gesperrte Bereiche nicht zuverlässig.",
user_impact_en: "Screen reader and keyboard users cannot reliably access dialogs, popovers, or disabled regions.",
technical_impact: "Defekte popovertarget-Bezüge, unbenannte Oberflächen oder aktiver Fokus innerhalb inert markierter Bereiche.",
technical_impact_en: "Broken popovertarget references, unnamed surfaces, or active focus inside inert regions.",
score_impact: ScoreImpact {
base_penalty: 4.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.aria_prohibited_attr.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Invalid,
severity: Severity::High,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria-prohibited-attr"),
title: "Verbotene ARIA-Attribute",
title_en: "Prohibited ARIA attributes",
description: "ARIA-Attribute werden auf einer Rolle verwendet, die sie nicht erlaubt.",
user_impact: "Screenreader erhalten widersprüchliche oder ungültige Semantik.",
user_impact_en: "Screen readers receive contradictory or invalid semantics.",
technical_impact: "ARIA-Attribute passen nicht zur impliziten oder expliziten Rolle.",
technical_impact_en: "ARIA attributes do not match the implicit or explicit role.",
score_impact: ScoreImpact {
base_penalty: 4.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.aria_allowed_attr.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Invalid,
severity: Severity::High,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria-allowed-attr"),
title: "ARIA-Attribut für diese Rolle nicht zulässig",
title_en: "ARIA attribute not allowed for this role",
description: "Ein ARIA-Attribut wird auf einer Rolle verwendet, für die es laut ARIA-Spezifikation nicht vorgesehen ist.",
user_impact: "Assistive Technologien ignorieren das Attribut oder interpretieren es falsch.",
user_impact_en: "Assistive technologies ignore the attribute or interpret it incorrectly.",
technical_impact: "Attribut-Rollen-Kombination entspricht nicht der ARIA-Spezifikation.",
technical_impact_en: "The attribute/role combination does not match the ARIA specification.",
score_impact: ScoreImpact {
base_penalty: 4.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.aria_valid_attr_value.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Invalid,
severity: Severity::High,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria-valid-attr-value"),
title: "Ungültiger Wert eines ARIA-Attributs",
title_en: "Invalid ARIA attribute value",
description: "Ein ARIA-Attribut enthält einen Wert, der für seinen Typ nicht zulässig ist (z. B. ein Boolean-Attribut ohne \"true\"/\"false\", oder ein ID-Verweis auf ein nicht existierendes Element).",
user_impact: "Assistive Technologien können Zustand oder Beziehung des Elements nicht korrekt vermitteln.",
user_impact_en: "Assistive technologies cannot correctly convey the element's state or relationship.",
technical_impact: "Attributwert entspricht nicht dem in der ARIA-Spezifikation definierten Werttyp.",
technical_impact_en: "Attribute value does not match the value type defined in the ARIA specification.",
score_impact: ScoreImpact {
base_penalty: 4.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.aria_relationships.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria-valid-attr"),
title: "Leerer Verweis in ARIA-Beziehungsattribut",
title_en: "Empty reference in an ARIA relationship attribute",
description: "Ein Beziehungsattribut wie aria-controls, aria-owns oder aria-activedescendant ist vorhanden, verweist aber auf keinen Wert.",
user_impact: "Assistive Technologien können die beabsichtigte Beziehung zwischen den Elementen nicht auflösen.",
user_impact_en: "Assistive technologies cannot resolve the intended relationship between the elements.",
technical_impact: "Beziehungsattribut ohne gültige ID-Referenz.",
technical_impact_en: "Relationship attribute without a valid ID reference.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.aria_roles.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Invalid,
severity: Severity::High,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria-roles"),
title: "Ungültige ARIA-Rolle",
title_en: "Invalid ARIA role",
description: "Ein role-Attribut enthält einen Wert, der keine gültige ARIA-Rolle ist.",
user_impact: "Assistive Technologien können die Funktion des Elements nicht bestimmen.",
user_impact_en: "Assistive technologies cannot determine the element's function.",
technical_impact: "Rollenwert ist in der ARIA-Spezifikation nicht definiert.",
technical_impact_en: "Role value is not defined in the ARIA specification.",
score_impact: ScoreImpact {
base_penalty: 4.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.aria_attr_name.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Invalid,
severity: Severity::High,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria-attr-name-invalid"),
title: "Ungültiger ARIA-Attributname",
title_en: "Invalid ARIA attribute name",
description: "Ein Attribut beginnt mit \"aria-\", ist aber kein in der ARIA-Spezifikation definiertes Attribut (z. B. Tippfehler).",
user_impact: "Assistive Technologien ignorieren das Attribut vollständig.",
user_impact_en: "Assistive technologies ignore the attribute entirely.",
technical_impact: "Attributname existiert nicht in der ARIA-Spezifikation.",
technical_impact_en: "Attribute name does not exist in the ARIA specification.",
score_impact: ScoreImpact {
base_penalty: 4.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.language_mismatch.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::LanguageClarity,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 3.1.1"),
external_level: Some("A"),
axe_id: Some("html-xml-lang-mismatch"),
title: "Widersprüchliche Sprachangaben",
title_en: "Conflicting language declarations",
description: "Die Attribute lang und xml:lang geben unterschiedliche Primärsprachen an.",
user_impact: "Assistive Technologien erhalten widersprüchliche Angaben zur Sprache des Inhalts.",
user_impact_en: "Assistive technologies receive conflicting information about the content's language.",
technical_impact: "lang und xml:lang stimmen im Primary-Subtag nicht überein.",
technical_impact_en: "lang and xml:lang do not agree on the primary subtag.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.command_name.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria-command-name"),
title: "Fehlender Name bei Befehlselement",
title_en: "Missing name on a command element",
description: "Ein interaktives Befehlselement (link, button, menuitem) hat keinen zugänglichen Namen.",
user_impact: "Screenreader-Nutzer erfahren nicht, welche Aktion das Element auslöst.",
user_impact_en: "Screen reader users cannot tell what action the element triggers.",
technical_impact: "Kein berechenbarer accessible name für die Rolle link/button/menuitem.",
technical_impact_en: "No computable accessible name for the link/button/menuitem role.",
score_impact: ScoreImpact {
base_penalty: 5.0,
max_penalty: 12.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.input_field_name.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::FormsInteraction,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria-input-field-name"),
title: "Fehlender Name bei Eingabefeld",
title_en: "Missing name on an input field",
description: "Ein Eingabeelement (textbox, combobox u. Ä.) hat keinen zugänglichen Namen.",
user_impact: "Screenreader-Nutzer wissen nicht, welche Eingabe erwartet wird.",
user_impact_en: "Screen reader users cannot tell what input is expected.",
technical_impact: "Kein berechenbarer accessible name für die Eingabe-Rolle.",
technical_impact_en: "No computable accessible name for the input role.",
score_impact: ScoreImpact {
base_penalty: 5.0,
max_penalty: 12.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.meter_name.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::ContentAlternatives,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 1.1.1"),
external_level: Some("A"),
axe_id: Some("aria-meter-name"),
title: "Fehlender Name bei Messwertanzeige",
title_en: "Missing name on a meter element",
description: "Ein Element mit role=meter hat keinen zugänglichen Namen.",
user_impact: "Screenreader-Nutzer erfahren nicht, welchen Messwert die Anzeige darstellt.",
user_impact_en: "Screen reader users cannot tell what value the meter represents.",
technical_impact: "Kein berechenbarer accessible name für die meter-Rolle.",
technical_impact_en: "No computable accessible name for the meter role.",
score_impact: ScoreImpact {
base_penalty: 4.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.progressbar_name.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::ContentAlternatives,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 1.1.1"),
external_level: Some("A"),
axe_id: Some("aria-progressbar-name"),
title: "Fehlender Name bei Fortschrittsanzeige",
title_en: "Missing name on a progressbar element",
description: "Ein Element mit role=progressbar hat keinen zugänglichen Namen.",
user_impact: "Screenreader-Nutzer erfahren nicht, welchen Vorgang die Fortschrittsanzeige begleitet.",
user_impact_en: "Screen reader users cannot tell what process the progress bar tracks.",
technical_impact: "Kein berechenbarer accessible name für die progressbar-Rolle.",
technical_impact_en: "No computable accessible name for the progressbar role.",
score_impact: ScoreImpact {
base_penalty: 4.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.toggle_field_name.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::FormsInteraction,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria-toggle-field-name"),
title: "Fehlender Name bei Umschaltelement",
title_en: "Missing name on a toggle field",
description: "Ein Umschaltelement (checkbox, radio, switch u. Ä.) hat keinen zugänglichen Namen.",
user_impact: "Screenreader-Nutzer wissen nicht, was sie ein- oder ausschalten.",
user_impact_en: "Screen reader users cannot tell what they are toggling on or off.",
technical_impact: "Kein berechenbarer accessible name für die Umschalt-Rolle.",
technical_impact_en: "No computable accessible name for the toggle role.",
score_impact: ScoreImpact {
base_penalty: 5.0,
max_penalty: 12.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.dialog_name.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria-dialog-name"),
title: "Fehlender Name bei Dialog",
title_en: "Missing name on a dialog",
description: "Ein Element mit role=dialog oder alertdialog hat keinen zugänglichen Namen.",
user_impact: "Screenreader-Nutzer erfahren beim Fokuswechsel in den Dialog nicht dessen Zweck.",
user_impact_en: "Screen reader users don't learn the dialog's purpose when focus moves into it.",
technical_impact: "Kein berechenbarer accessible name für die dialog-Rolle.",
technical_impact_en: "No computable accessible name for the dialog role.",
score_impact: ScoreImpact {
base_penalty: 5.0,
max_penalty: 12.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.treeitem_name.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria-treeitem-name"),
title: "Fehlender Name bei Baumelement",
title_en: "Missing name on a tree item",
description: "Ein Element mit role=treeitem hat keinen zugänglichen Namen.",
user_impact: "Screenreader-Nutzer erfahren nicht, welchen Eintrag das Baumelement repräsentiert.",
user_impact_en: "Screen reader users cannot tell what entry the tree item represents.",
technical_impact: "Kein berechenbarer accessible name für die treeitem-Rolle.",
technical_impact_en: "No computable accessible name for the treeitem role.",
score_impact: ScoreImpact {
base_penalty: 4.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.table_headers_ref.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Invalid,
severity: Severity::High,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("td-headers-attr"),
title: "Ungültiger Headers-Verweis in Tabellenzelle",
title_en: "Invalid headers reference in a table cell",
description: "Eine Tabellenzelle referenziert über das headers-Attribut eine leere oder nicht existierende Header-ID.",
user_impact: "Screenreader können der Zelle keine oder eine falsche Spalten-/Zeilenüberschrift zuordnen.",
user_impact_en: "Screen readers cannot associate the cell with its correct column/row header, or none at all.",
technical_impact: "headers-Attribut verweist nicht auf eine gültige th-Zelle derselben Tabelle.",
technical_impact_en: "headers attribute does not reference a valid th cell in the same table.",
score_impact: ScoreImpact {
base_penalty: 4.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.table_header_data.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("th-has-data-cells"),
title: "Tabellenüberschrift ohne zugehörige Datenzellen",
title_en: "Table header without associated data cells",
description: "Eine Tabellenüberschrift (th) hat keine zugehörigen Datenzellen in derselben Tabelle.",
user_impact: "Screenreader kündigen eine Überschrift an, die keine Daten strukturiert.",
user_impact_en: "Screen readers announce a header that structures no data.",
technical_impact: "Header-Zelle ohne zugeordnete Datenzellen in der Tabelle.",
technical_impact_en: "Header cell with no associated data cells in the table.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.tablist_tabpanel.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria-tablist-tabpanel"),
title: "Tableiste ohne zugehöriges Tab-Panel",
title_en: "Tab list without an associated tab panel",
description: "Ein Element mit role=tablist hat kein zugehöriges Element mit role=tabpanel im Baum.",
user_impact: "Screenreader-Nutzer können nach Aktivieren eines Tabs den zugehörigen Inhalt nicht auffinden.",
user_impact_en: "Screen reader users cannot locate the content associated with a tab after activating it.",
technical_impact: "tablist ohne begleitendes tabpanel-Element.",
technical_impact_en: "tablist without an accompanying tabpanel element.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.tab_selected.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Missing,
severity: Severity::Low,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria/tab-selected-missing"),
title: "Kein Tab als ausgewählt ausgezeichnet",
title_en: "No tab marked as selected",
description: "Keiner der Tabs einer Tableiste trägt aria-selected=\"true\".",
user_impact: "Screenreader-Nutzer erfahren nicht, welcher Tab aktiv ist und zu welchem der sichtbare Inhalt gehört.",
user_impact_en: "Screen reader users are not told which tab is active and which one the visible content belongs to.",
technical_impact: "tablist ohne tab mit aria-selected=\"true\".",
technical_impact_en: "tablist without a tab carrying aria-selected=\"true\".",
score_impact: ScoreImpact {
base_penalty: 1.0,
max_penalty: 4.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.dialog_modal.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Missing,
severity: Severity::Low,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("dialog/modal-unmarked"),
title: "Dialog nicht als modal ausgezeichnet",
title_en: "Dialog not marked as modal",
description: "Ein Element mit role=\"dialog\" trägt kein aria-modal=\"true\".",
user_impact: "Ist der Dialog modal gemeint, lesen Screenreader den Inhalt dahinter weiter, als wäre er erreichbar.",
user_impact_en: "If the dialog is meant to be modal, screen readers keep reading the content behind it as if it were reachable.",
technical_impact: "role=\"dialog\" ohne aria-modal=\"true\"; ob der Dialog modal gemeint ist, zeigt erst sein Verhalten.",
technical_impact_en: "role=\"dialog\" without aria-modal=\"true\"; whether the dialog is meant to be modal only shows in its behavior.",
score_impact: ScoreImpact {
base_penalty: 1.0,
max_penalty: 4.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.combobox_options.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::FormsInteraction,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria-combobox-options"),
title: "Combobox ohne Optionsliste",
title_en: "Combobox without an options list",
description: "Ein Element mit role=combobox hat keine zugehörige listbox oder option im Baum.",
user_impact: "Screenreader-Nutzer erfahren nicht, welche Auswahlmöglichkeiten die Combobox bietet.",
user_impact_en: "Screen reader users cannot tell what choices the combobox offers.",
technical_impact: "combobox ohne verknüpfte listbox/option-Elemente.",
technical_impact_en: "combobox without associated listbox/option elements.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.frame_title.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::ContentAlternatives,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 2.4.1"),
external_level: Some("A"),
axe_id: Some("frame-title"),
title: "Iframe ohne zugänglichen Namen",
title_en: "Iframe without accessible name",
description: "Iframe oder Frame hat keinen title, aria-label oder aria-labelledby.",
user_impact: "Screenreader-Nutzer erkennen Zweck und Inhalt des eingebetteten Frames nicht.",
user_impact_en: "Screen reader users cannot identify the purpose and content of the embedded frame.",
technical_impact: "Fehlender zugänglicher Name auf iframe/frame.",
technical_impact_en: "Missing accessible name on iframe/frame.",
score_impact: ScoreImpact {
base_penalty: 4.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.frame_tested.cross_origin",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Informational,
severity: Severity::Medium,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("frame-tested"),
title: "Cross-origin Iframe: manuell prüfen",
title_en: "Cross-origin iframe: review manually",
description: "Cross-origin iframes können nicht automatisch auf Barrierefreiheit geprüft werden.",
user_impact: "Eingebettete Inhalte (Karten, Videos, Consent-Widgets) sind möglicherweise für Screenreader-Nutzer unzugänglich.",
user_impact_en: "Embedded content (maps, videos, consent widgets) may be inaccessible to screen reader users.",
technical_impact: "Iframe-Inhalt ist über CDP nicht erreichbar — automatische Regelprüfung nicht möglich.",
technical_impact_en: "Iframe content is unreachable via CDP — automatic rule checking is not possible.",
score_impact: ScoreImpact {
base_penalty: 0.0,
max_penalty: 0.0,
occurrence_scaling: Scaling::Linear,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.parsing.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Invalid,
severity: Severity::Critical,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("ids/duplicate"),
title: "Mehrfach vergebene ID wird referenziert",
title_en: "Referenced ID is assigned more than once",
description: "Ein Element verweist per IDREF (z. B. label for, headers, aria-labelledby) auf eine ID, die im Dokument mehrfach vorkommt.",
user_impact: "Assistierende Technik kann die Beziehung nicht eindeutig auflösen und gibt Name oder Beschreibung des falschen Elements aus.",
user_impact_en: "Assistive technology cannot resolve the relationship unambiguously and announces the name or description of the wrong element.",
technical_impact: "Dieselbe ID kommt im DOM mehrfach vor und wird von einem for-, headers- oder aria-Attribut referenziert.",
technical_impact_en: "The same ID occurs more than once in the DOM and is referenced by a for, headers or aria attribute.",
score_impact: ScoreImpact {
base_penalty: 5.0,
max_penalty: 15.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "perf.lcp.slow",
dimension: Dimension::Performance,
subcategory: Subcategory::LoadBehavior,
issue_class: IssueClass::Weak,
severity: Severity::High,
external_ref: Some("CWV:LCP"),
external_level: None,
axe_id: None,
title: "Langsamer Largest Contentful Paint",
title_en: "Slow Largest Contentful Paint",
description: "Hauptinhalt wird zu spät sichtbar.",
user_impact: "Nutzer sieht lange leere oder unvollständige Seite.",
user_impact_en: "Users see a blank or incomplete page for a long time.",
technical_impact: "LCP über 2500ms — Netzwerk, Rendering oder Ressourcen zu langsam.",
technical_impact_en: "LCP above 2500ms — network, rendering or resources too slow.",
score_impact: ScoreImpact {
base_penalty: 25.0,
max_penalty: 25.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "perf.fcp.slow",
dimension: Dimension::Performance,
subcategory: Subcategory::LoadBehavior,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: Some("CWV:FCP"),
external_level: None,
axe_id: None,
title: "Langsamer First Contentful Paint",
title_en: "Slow First Contentful Paint",
description: "Erster sichtbarer Inhalt erscheint zu spät.",
user_impact: "Nutzer wartet zu lange auf erste Rückmeldung.",
user_impact_en: "Users wait too long for the first feedback.",
technical_impact: "FCP über 1800ms — Server oder Rendering-Blockade.",
technical_impact_en: "FCP above 1800ms — server or rendering blockage.",
score_impact: ScoreImpact {
base_penalty: 25.0,
max_penalty: 25.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "perf.cls.unstable",
dimension: Dimension::Performance,
subcategory: Subcategory::VisualStability,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: Some("CWV:CLS"),
external_level: None,
axe_id: None,
title: "Layout-Verschiebungen während des Ladens",
title_en: "Layout shifts during loading",
description: "Seitenelemente springen während des Ladens.",
user_impact: "Nutzer klickt versehentlich auf falsches Element.",
user_impact_en: "Users accidentally click the wrong element.",
technical_impact: "CLS über 0.1 — fehlende Dimensionen, nachladende Inhalte.",
technical_impact_en: "CLS above 0.1 — missing dimensions, late-loading content.",
score_impact: ScoreImpact {
base_penalty: 25.0,
max_penalty: 25.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "perf.tbt.high",
dimension: Dimension::Performance,
subcategory: Subcategory::Interactivity,
issue_class: IssueClass::Weak,
severity: Severity::High,
external_ref: Some("CWV:TBT"),
external_level: None,
axe_id: None,
title: "Hohe Blocking-Zeit",
title_en: "High blocking time",
description: "Hauptthread blockiert zu lange durch JavaScript.",
user_impact: "Seite reagiert verzögert auf Nutzereingaben.",
user_impact_en: "The page responds to user input with delay.",
technical_impact: "TBT/INP über 200ms — zu viel JavaScript-Arbeit im Hauptthread.",
technical_impact_en: "TBT/INP above 200ms — too much JavaScript work on the main thread.",
score_impact: ScoreImpact {
base_penalty: 25.0,
max_penalty: 25.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "perf.ttfb.slow",
dimension: Dimension::Performance,
subcategory: Subcategory::LoadBehavior,
issue_class: IssueClass::Risk,
severity: Severity::Medium,
external_ref: Some("CWV:TTFB"),
external_level: None,
axe_id: None,
title: "Langsame Server-Antwortzeit",
title_en: "Slow server response time",
description: "Server reagiert zu langsam auf die Anfrage.",
user_impact: "Gesamte Ladezeit wird verzögert.",
user_impact_en: "The overall load time is delayed.",
technical_impact: "TTFB über 800ms — Server-Performance oder Netzwerk-Latenz.",
technical_impact_en: "TTFB above 800ms — server performance or network latency.",
score_impact: ScoreImpact {
base_penalty: 5.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "perf.resources.heavy",
dimension: Dimension::Performance,
subcategory: Subcategory::ResourceUsage,
issue_class: IssueClass::Risk,
severity: Severity::Medium,
external_ref: None,
external_level: None,
axe_id: None,
title: "Hoher Ressourcenverbrauch",
title_en: "High resource consumption",
description: "Seite lädt zu viele oder zu große Ressourcen.",
user_impact: "Langsame Ladezeiten, hoher Datenverbrauch auf Mobilgeräten.",
user_impact_en: "Slow load times, high data usage on mobile devices.",
technical_impact: "Gesamtgröße > 3MB, zu viele Requests, unkomprimiert.",
technical_impact_en: "Total size > 3MB, too many requests, uncompressed.",
score_impact: ScoreImpact {
base_penalty: 5.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "perf.dom.large",
dimension: Dimension::Performance,
subcategory: Subcategory::TechnicalComplexity,
issue_class: IssueClass::Risk,
severity: Severity::Low,
external_ref: None,
external_level: None,
axe_id: None,
title: "Großes DOM",
title_en: "Large DOM",
description: "Übermäßig viele DOM-Elemente.",
user_impact: "Seite reagiert langsam auf Interaktionen.",
user_impact_en: "The page responds slowly to interactions.",
technical_impact: "DOM-Größe belastet Browser-Rendering und Speicher.",
technical_impact_en: "DOM size burdens browser rendering and memory.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 5.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_TECHNICAL,
},
Rule {
id: "seo.title.missing",
dimension: Dimension::Seo,
subcategory: Subcategory::SnippetMetadata,
issue_class: IssueClass::Missing,
severity: Severity::Critical,
external_ref: None,
external_level: None,
axe_id: None,
title: "Fehlender Seitentitel",
title_en: "Missing page title",
description: "Kein <title>-Tag vorhanden.",
user_impact: "Seite wird in Suchergebnissen ohne aussagekräftigen Titel angezeigt.",
user_impact_en: "The page appears in search results without a meaningful title.",
technical_impact: "Leeres oder fehlendes <title>-Element.",
technical_impact_en: "Empty or missing <title> element.",
score_impact: ScoreImpact {
base_penalty: 10.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "seo.title.weak",
dimension: Dimension::Seo,
subcategory: Subcategory::SnippetMetadata,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: None,
external_level: None,
axe_id: None,
title: "Ungünstige Titellänge",
title_en: "Suboptimal title length",
description: "Titel zu kurz (<30 Zeichen) oder zu lang (>60 Zeichen).",
user_impact: "Snippet wird in Suchergebnissen suboptimal dargestellt.",
user_impact_en: "The snippet is displayed suboptimally in search results.",
technical_impact: "Title-Tag außerhalb des empfohlenen Zeichenbereichs.",
technical_impact_en: "Title tag outside the recommended character range.",
score_impact: ScoreImpact {
base_penalty: 5.0,
max_penalty: 5.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "seo.description.missing",
dimension: Dimension::Seo,
subcategory: Subcategory::SnippetMetadata,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: None,
external_level: None,
axe_id: None,
title: "Fehlende Meta-Description",
title_en: "Missing meta description",
description: "Keine Meta-Description vorhanden.",
user_impact: "Suchmaschine generiert eigene Beschreibung, oft suboptimal.",
user_impact_en: "The search engine generates its own description, often suboptimal.",
technical_impact: "Fehlendes <meta name=\"description\">-Element.",
technical_impact_en: "Missing <meta name=\"description\"> element.",
score_impact: ScoreImpact {
base_penalty: 10.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "seo.description.weak",
dimension: Dimension::Seo,
subcategory: Subcategory::SnippetMetadata,
issue_class: IssueClass::Weak,
severity: Severity::Low,
external_ref: None,
external_level: None,
axe_id: None,
title: "Ungünstige Description-Länge",
title_en: "Suboptimal description length",
description: "Meta-Description zu kurz (<120) oder zu lang (>160 Zeichen).",
user_impact: "Snippet wird abgeschnitten oder ist zu knapp.",
user_impact_en: "The snippet is truncated or too brief.",
technical_impact: "Description außerhalb des empfohlenen Zeichenbereichs.",
technical_impact_en: "Description outside the recommended character range.",
score_impact: ScoreImpact {
base_penalty: 5.0,
max_penalty: 5.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "seo.viewport.missing",
dimension: Dimension::Seo,
subcategory: Subcategory::SnippetMetadata,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: None,
external_level: None,
axe_id: None,
title: "Fehlende Viewport-Meta-Tag",
title_en: "Missing viewport meta tag",
description: "Kein Viewport-Meta-Tag vorhanden.",
user_impact: "Seite wird auf Mobilgeräten nicht korrekt dargestellt.",
user_impact_en: "The page is not rendered correctly on mobile devices.",
technical_impact: "Fehlendes <meta name=\"viewport\">-Element.",
technical_impact_en: "Missing <meta name=\"viewport\"> element.",
score_impact: ScoreImpact {
base_penalty: 10.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "seo.h1.missing",
dimension: Dimension::Seo,
subcategory: Subcategory::ContentStructure,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: None,
external_level: None,
axe_id: None,
title: "Fehlende H1-Überschrift",
title_en: "Missing H1 heading",
description: "Seite hat keine H1-Überschrift.",
user_impact: "Suchmaschine kann Hauptthema der Seite schlechter erkennen.",
user_impact_en: "Search engines can less easily identify the main topic of the page.",
technical_impact: "Kein <h1>-Element vorhanden.",
technical_impact_en: "No <h1> element present.",
score_impact: ScoreImpact {
base_penalty: 15.0,
max_penalty: 15.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "seo.h1.multiple",
dimension: Dimension::Seo,
subcategory: Subcategory::ContentStructure,
issue_class: IssueClass::Weak,
severity: Severity::Low,
external_ref: None,
external_level: None,
axe_id: None,
title: "Mehrere H1-Überschriften",
title_en: "Multiple H1 headings",
description: "Seite hat mehr als eine H1-Überschrift.",
user_impact: "Thematische Fokussierung der Seite unklar.",
user_impact_en: "The thematic focus of the page is unclear.",
technical_impact: "Mehrere <h1>-Elemente im DOM.",
technical_impact_en: "Multiple <h1> elements in the DOM.",
score_impact: ScoreImpact {
base_penalty: 5.0,
max_penalty: 5.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "seo.headings.issue",
dimension: Dimension::Seo,
subcategory: Subcategory::ContentStructure,
issue_class: IssueClass::Weak,
severity: Severity::Low,
external_ref: None,
external_level: None,
axe_id: None,
title: "Heading-Probleme",
title_en: "Heading issues",
description: "Übersprungene Ebenen, leere Headings oder zu lange Headings.",
user_impact: "Strukturelle Signale für Suchmaschine geschwächt.",
user_impact_en: "Structural signals for search engines are weakened.",
technical_impact: "Heading-Hierarchie nicht korrekt.",
technical_impact_en: "Heading hierarchy is not correct.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 9.0,
occurrence_scaling: Scaling::Linear,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "seo.canonical.missing",
dimension: Dimension::Seo,
subcategory: Subcategory::Indexability,
issue_class: IssueClass::Opportunity,
severity: Severity::Low,
external_ref: None,
external_level: None,
axe_id: None,
title: "Fehlende Canonical-URL",
title_en: "Missing canonical URL",
description: "Kein Canonical-Link vorhanden.",
user_impact: "Mögliche Duplicate-Content-Probleme.",
user_impact_en: "Possible duplicate content issues.",
technical_impact: "Fehlendes <link rel=\"canonical\">-Element.",
technical_impact_en: "Missing <link rel=\"canonical\"> element.",
score_impact: ScoreImpact {
base_penalty: 5.0,
max_penalty: 5.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "seo.lang.missing",
dimension: Dimension::Seo,
subcategory: Subcategory::SemanticSignals,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: None,
external_level: None,
axe_id: None,
title: "Fehlende Sprachangabe",
title_en: "Missing language declaration",
description: "Kein lang-Attribut im HTML-Tag.",
user_impact: "Suchmaschine kann Sprache nicht automatisch zuordnen.",
user_impact_en: "Search engines cannot automatically assign the language.",
technical_impact: "Fehlendes lang-Attribut im <html>-Element.",
technical_impact_en: "Missing lang attribute on the <html> element.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 3.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "seo.og.missing",
dimension: Dimension::Seo,
subcategory: Subcategory::SemanticSignals,
issue_class: IssueClass::Opportunity,
severity: Severity::Low,
external_ref: None,
external_level: None,
axe_id: None,
title: "Fehlende Open Graph Tags",
title_en: "Missing Open Graph tags",
description: "Keine Open Graph Meta-Tags vorhanden.",
user_impact: "Social-Media-Vorschau der Seite ist unformatiert.",
user_impact_en: "The social media preview of the page is unformatted.",
technical_impact: "Fehlende og:-Meta-Tags.",
technical_impact_en: "Missing og: meta tags.",
score_impact: ScoreImpact {
base_penalty: 5.0,
max_penalty: 5.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "seo.twitter.missing",
dimension: Dimension::Seo,
subcategory: Subcategory::SemanticSignals,
issue_class: IssueClass::Opportunity,
severity: Severity::Low,
external_ref: None,
external_level: None,
axe_id: None,
title: "Fehlende Twitter Card Tags",
title_en: "Missing Twitter Card tags",
description: "Keine Twitter Card Meta-Tags vorhanden.",
user_impact: "Twitter-Vorschau der Seite ist unformatiert.",
user_impact_en: "The Twitter preview of the page is unformatted.",
technical_impact: "Fehlende twitter:-Meta-Tags.",
technical_impact_en: "Missing twitter: meta tags.",
score_impact: ScoreImpact {
base_penalty: 5.0,
max_penalty: 5.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_TECHNICAL,
},
Rule {
id: "seo.https.missing",
dimension: Dimension::Seo,
subcategory: Subcategory::Indexability,
issue_class: IssueClass::Missing,
severity: Severity::Critical,
external_ref: None,
external_level: None,
axe_id: None,
title: "Kein HTTPS",
title_en: "No HTTPS",
description: "Seite wird nicht über HTTPS ausgeliefert.",
user_impact: "Browser zeigt Sicherheitswarnung, Ranking-Nachteil.",
user_impact_en: "The browser shows a security warning; ranking disadvantage.",
technical_impact: "Fehlende TLS-Verschlüsselung.",
technical_impact_en: "Missing TLS encryption.",
score_impact: ScoreImpact {
base_penalty: 10.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "sec.https.missing",
dimension: Dimension::Security,
subcategory: Subcategory::Transport,
issue_class: IssueClass::Missing,
severity: Severity::Critical,
external_ref: None,
external_level: None,
axe_id: None,
title: "Kein HTTPS",
title_en: "No HTTPS",
description: "Seite wird nicht über HTTPS ausgeliefert.",
user_impact: "Daten werden unverschlüsselt übertragen.",
user_impact_en: "Data is transmitted unencrypted.",
technical_impact: "Fehlende TLS-Verschlüsselung.",
technical_impact_en: "Missing TLS encryption.",
score_impact: ScoreImpact {
base_penalty: 25.0,
max_penalty: 25.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "sec.hsts.missing",
dimension: Dimension::Security,
subcategory: Subcategory::Headers,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: None,
external_level: None,
axe_id: None,
title: "HSTS Header fehlt",
title_en: "HSTS header missing",
description: "Strict-Transport-Security Header nicht gesetzt.",
user_impact: "Downgrade-Angriffe auf HTTP möglich.",
user_impact_en: "Downgrade attacks to HTTP are possible.",
technical_impact: "Fehlender HSTS-Header.",
technical_impact_en: "Missing HSTS header.",
score_impact: ScoreImpact {
base_penalty: 15.0,
max_penalty: 15.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "sec.csp.missing",
dimension: Dimension::Security,
subcategory: Subcategory::Headers,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: None,
external_level: None,
axe_id: None,
title: "Content Security Policy fehlt",
title_en: "Content Security Policy missing",
description: "Kein Content-Security-Policy Header gesetzt.",
user_impact: "Erhöhtes Risiko für XSS-Angriffe.",
user_impact_en: "Increased risk of XSS attacks.",
technical_impact: "Fehlender CSP-Header.",
technical_impact_en: "Missing CSP header.",
score_impact: ScoreImpact {
base_penalty: 15.0,
max_penalty: 15.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "sec.xcto.missing",
dimension: Dimension::Security,
subcategory: Subcategory::BrowserProtection,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: None,
external_level: None,
axe_id: None,
title: "X-Content-Type-Options Header fehlt",
title_en: "X-Content-Type-Options header missing",
description: "Browser kann MIME-Typ-Sniffing durchführen.",
user_impact: "Potenzielle Sicherheitslücke durch falsche MIME-Typen.",
user_impact_en: "Potential security gap through incorrect MIME types.",
technical_impact: "Fehlender X-Content-Type-Options: nosniff Header.",
technical_impact_en: "Missing X-Content-Type-Options: nosniff header.",
score_impact: ScoreImpact {
base_penalty: 10.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "sec.xfo.missing",
dimension: Dimension::Security,
subcategory: Subcategory::BrowserProtection,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: None,
external_level: None,
axe_id: None,
title: "X-Frame-Options Header fehlt",
title_en: "X-Frame-Options header missing",
description: "Seite kann in fremden iframes eingebettet werden.",
user_impact: "Clickjacking-Angriffe möglich.",
user_impact_en: "Clickjacking attacks are possible.",
technical_impact: "Fehlender X-Frame-Options Header.",
technical_impact_en: "Missing X-Frame-Options header.",
score_impact: ScoreImpact {
base_penalty: 10.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "sec.referrer.missing",
dimension: Dimension::Security,
subcategory: Subcategory::BrowserProtection,
issue_class: IssueClass::Missing,
severity: Severity::Low,
external_ref: None,
external_level: None,
axe_id: None,
title: "Referrer-Policy Header fehlt",
title_en: "Referrer-Policy header missing",
description: "Keine Kontrolle über Referrer-Informationen.",
user_impact: "Private URLs können an Dritte weitergegeben werden.",
user_impact_en: "Private URLs may be passed on to third parties.",
technical_impact: "Fehlender Referrer-Policy Header.",
technical_impact_en: "Missing Referrer-Policy header.",
score_impact: ScoreImpact {
base_penalty: 5.0,
max_penalty: 5.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_TECHNICAL,
},
Rule {
id: "mob.viewport.missing",
dimension: Dimension::Mobile,
subcategory: Subcategory::Viewport,
issue_class: IssueClass::Missing,
severity: Severity::Critical,
external_ref: None,
external_level: None,
axe_id: None,
title: "Fehlende Viewport-Konfiguration",
title_en: "Missing viewport configuration",
description: "Kein Viewport-Meta-Tag vorhanden.",
user_impact: "Seite wird auf Mobilgeräten nicht korrekt dargestellt.",
user_impact_en: "The page is not rendered correctly on mobile devices.",
technical_impact: "Fehlendes <meta name=\"viewport\">-Element.",
technical_impact_en: "Missing <meta name=\"viewport\"> element.",
score_impact: ScoreImpact {
base_penalty: 20.0,
max_penalty: 20.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "mob.viewport.improper",
dimension: Dimension::Mobile,
subcategory: Subcategory::Viewport,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: None,
external_level: None,
axe_id: None,
title: "Unvollständige Viewport-Konfiguration",
title_en: "Incomplete viewport configuration",
description: "Viewport-Tag fehlt width=device-width oder initial-scale=1.",
user_impact: "Seite wird möglicherweise nicht optimal skaliert.",
user_impact_en: "The page may not scale optimally.",
technical_impact: "Viewport-Meta-Tag mit unvollständiger Konfiguration.",
technical_impact_en: "Viewport meta tag with incomplete configuration.",
score_impact: ScoreImpact {
base_penalty: 10.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "mob.zoom.disabled",
dimension: Dimension::Mobile,
subcategory: Subcategory::Viewport,
issue_class: IssueClass::Risk,
severity: Severity::Critical,
external_ref: None,
external_level: None,
axe_id: None,
title: "Zoom deaktiviert",
title_en: "Zoom disabled",
description: "user-scalable=no verhindert das Zoomen.",
user_impact: "Nutzer mit Sehschwäche können Inhalte nicht vergrößern.",
user_impact_en: "Users with low vision cannot enlarge content.",
technical_impact: "Viewport-Meta mit user-scalable=no.",
technical_impact_en: "Viewport meta with user-scalable=no.",
score_impact: ScoreImpact {
base_penalty: 20.0,
max_penalty: 20.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "mob.touch_targets.small",
dimension: Dimension::Mobile,
subcategory: Subcategory::TouchUsability,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: None,
external_level: None,
axe_id: None,
title: "Zu kleine Touch-Targets",
title_en: "Touch targets too small",
description: "Interaktive Elemente kleiner als 44x44 Pixel.",
user_impact: "Schwer bedienbar auf Touchscreens.",
user_impact_en: "Hard to operate on touchscreens.",
technical_impact: "Bounding Box unter 44x44px.",
technical_impact_en: "Bounding box below 44x44px.",
score_impact: ScoreImpact {
base_penalty: 10.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "mob.fonts.small",
dimension: Dimension::Mobile,
subcategory: Subcategory::Readability,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: None,
external_level: None,
axe_id: None,
title: "Zu kleine Schriftgröße",
title_en: "Font size too small",
description: "Kleinste Schriftgröße unter 12px.",
user_impact: "Text auf Mobilgeräten schwer lesbar.",
user_impact_en: "Text is hard to read on mobile devices.",
technical_impact: "Font-size unter 12px detektiert.",
technical_impact_en: "Font size below 12px detected.",
score_impact: ScoreImpact {
base_penalty: 10.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "mob.horizontal_scroll",
dimension: Dimension::Mobile,
subcategory: Subcategory::ResponsiveLayout,
issue_class: IssueClass::Weak,
severity: Severity::High,
external_ref: None,
external_level: None,
axe_id: None,
title: "Horizontales Scrollen erforderlich",
title_en: "Horizontal scrolling required",
description: "Seite ist breiter als der Viewport.",
user_impact: "Nutzer muss horizontal scrollen, Inhalte abgeschnitten.",
user_impact_en: "Users must scroll horizontally; content is cut off.",
technical_impact: "scrollWidth > innerWidth.",
technical_impact_en: "scrollWidth > innerWidth.",
score_impact: ScoreImpact {
base_penalty: 20.0,
max_penalty: 20.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.image_map_server_side.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::ContentAlternatives,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 1.1.1"),
external_level: Some("A"),
axe_id: Some("server-side-image-map"),
title: "Server-seitige Image-Map verwendet",
title_en: "Server-side image map used",
description: "Ein Bild nutzt eine server-seitige Image-Map (ismap-Attribut) statt einer client-seitigen Variante.",
user_impact: "Tastatur- und Screenreader-Nutzer können die einzelnen Bildbereiche nicht ansteuern.",
user_impact_en: "Keyboard and screen reader users cannot target the individual image regions.",
technical_impact: "ismap-Attribut auf einem Bild ohne äquivalente clientseitige Alternative.",
technical_impact_en: "ismap attribute on an image without an equivalent client-side alternative.",
score_impact: ScoreImpact {
base_penalty: 1.5,
max_penalty: 4.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.svg_alt.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::ContentAlternatives,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 1.1.1"),
external_level: Some("A"),
axe_id: Some("svg-img-alt"),
title: "Fehlender Alternativtext bei SVG-Grafiken",
title_en: "Missing alternative text on SVG graphics",
description: "Eine SVG-Grafik mit informativem Inhalt hat keinen Alternativtext (title, aria-label).",
user_impact: "Screenreader-Nutzer erhalten keine Information über den Inhalt der Grafik.",
user_impact_en: "Screen reader users receive no information about the graphic's content.",
technical_impact: "SVG ohne <title>-Kindelement, aria-label oder aria-labelledby.",
technical_impact_en: "SVG without a <title> child element, aria-label, or aria-labelledby.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.area_alt.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::ContentAlternatives,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 1.1.1"),
external_level: Some("A"),
axe_id: Some("area-alt"),
title: "Fehlender Alternativtext bei Image-Map-Bereichen",
title_en: "Missing alternative text on image map areas",
description: "Ein aktiver <area>-Bereich einer Image-Map hat keinen Alternativtext.",
user_impact: "Screenreader-Nutzer erfahren nicht, wohin ein anklickbarer Bildbereich führt.",
user_impact_en: "Screen reader users are not told where a clickable image region leads.",
technical_impact: "<area>-Element ohne alt-Attribut.",
technical_impact_en: "<area> element without an alt attribute.",
score_impact: ScoreImpact {
base_penalty: 2.0,
max_penalty: 6.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.input_image_alt.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::ContentAlternatives,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 1.1.1"),
external_level: Some("A"),
axe_id: Some("input-image-alt"),
title: "Fehlender Alternativtext bei Bild-Buttons",
title_en: "Missing alternative text on image buttons",
description: "Ein <input type=\"image\">-Button (z. B. eine Bild-Absende-Schaltfläche) hat keinen Alternativtext.",
user_impact: "Screenreader-Nutzer erfahren nicht, welche Aktion der Button auslöst.",
user_impact_en: "Screen reader users are not told what action the button triggers.",
technical_impact: "<input type=\"image\"> ohne alt-Attribut.",
technical_impact_en: "<input type=\"image\"> without an alt attribute.",
score_impact: ScoreImpact {
base_penalty: 2.0,
max_penalty: 6.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.object_alt.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::ContentAlternatives,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 1.1.1"),
external_level: Some("A"),
axe_id: Some("object-alt"),
title: "Fehlende Textalternative bei eingebetteten Objekten",
title_en: "Missing text alternative on embedded objects",
description: "Ein <object>-Element (eingebettetes Bild/Dokument) hat keine Textalternative.",
user_impact: "Screenreader-Nutzer erhalten keine Information über den Inhalt des eingebetteten Objekts.",
user_impact_en: "Screen reader users receive no information about the embedded object's content.",
technical_impact: "<object> ohne Textinhalt oder aria-label.",
technical_impact_en: "<object> without text content or aria-label.",
score_impact: ScoreImpact {
base_penalty: 1.5,
max_penalty: 5.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.html_content_model.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("html-content-model"),
title: "Verletzung des HTML5-Content-Models",
title_en: "HTML5 content model violation",
description: "Ein strukturelles Element (Liste, Tabelle, Formular, Select) enthält Inhalt, der sein HTML5-Content-Model verletzt — das kann Screenreader-Semantik brechen, selbst wenn der resultierende Accessibility-Tree oberflächlich gültig aussieht.",
user_impact: "Screenreader können die Struktur nicht zuverlässig auswerten, auch wenn sie augenscheinlich korrekt wirkt.",
user_impact_en: "Screen readers cannot reliably parse the structure, even when it appears superficially valid.",
technical_impact: "Unerwartetes Kindelement innerhalb eines strukturellen Containers (z. B. <ul>, <table>, <select>).",
technical_impact_en: "Unexpected child element inside a structural container (e.g. <ul>, <table>, <select>).",
score_impact: ScoreImpact {
base_penalty: 1.5,
max_penalty: 4.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.form_field_group.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("form-field-group"),
title: "Fehlende Gruppierung verwandter Formularfelder",
title_en: "Missing grouping for related form fields",
description: "Zusammengehörige Radio-Buttons oder Checkboxen sind nicht mit <fieldset>/<legend> oder role=\"group\" gruppiert.",
user_impact: "Screenreader-Nutzer erkennen nicht, dass mehrere Felder zu einer Frage/Gruppe gehören.",
user_impact_en: "Screen reader users cannot tell that several fields belong to one question/group.",
technical_impact: "Radio-Buttons/Checkboxen ohne umschließendes <fieldset> mit <legend>.",
technical_impact_en: "Radio buttons/checkboxes without an enclosing <fieldset> with <legend>.",
score_impact: ScoreImpact {
base_penalty: 1.2,
max_penalty: 3.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.landmark_banner_nested.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("landmark-banner-is-top-level"),
title: "Banner-Landmark ist verschachtelt",
title_en: "Banner landmark is nested",
description: "Der Banner-Bereich (<header>/role=\"banner\") liegt innerhalb eines anderen Landmarks statt auf oberster Ebene.",
user_impact: "Screenreader-Nutzer, die per Landmark-Navigation zum Kopfbereich springen, landen in einem unerwarteten Kontext.",
user_impact_en: "Screen reader users who jump to the header via landmark navigation land in an unexpected context.",
technical_impact: "<header>/role=\"banner\" ist Nachfahre eines anderen Landmarks (z. B. <main>).",
technical_impact_en: "<header>/role=\"banner\" is a descendant of another landmark (e.g. <main>).",
score_impact: ScoreImpact {
base_penalty: 1.0,
max_penalty: 3.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.landmark_contentinfo_nested.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("landmark-contentinfo-is-top-level"),
title: "Contentinfo-Landmark ist verschachtelt",
title_en: "Contentinfo landmark is nested",
description: "Der Fußbereich (<footer>/role=\"contentinfo\") liegt innerhalb eines anderen Landmarks statt auf oberster Ebene.",
user_impact: "Screenreader-Nutzer, die per Landmark-Navigation zum Fußbereich springen, landen in einem unerwarteten Kontext.",
user_impact_en: "Screen reader users who jump to the footer via landmark navigation land in an unexpected context.",
technical_impact: "<footer>/role=\"contentinfo\" ist Nachfahre eines anderen Landmarks.",
technical_impact_en: "<footer>/role=\"contentinfo\" is a descendant of another landmark.",
score_impact: ScoreImpact {
base_penalty: 1.0,
max_penalty: 3.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.landmark_main_nested.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("landmark-main-is-top-level"),
title: "Main-Landmark ist verschachtelt",
title_en: "Main landmark is nested",
description: "Der Hauptinhaltsbereich (<main>/role=\"main\") liegt innerhalb eines anderen Landmarks statt auf oberster Ebene.",
user_impact: "Screenreader-Nutzer, die per Landmark-Navigation zum Hauptinhalt springen, landen in einem unerwarteten Kontext.",
user_impact_en: "Screen reader users who jump to the main content via landmark navigation land in an unexpected context.",
technical_impact: "<main>/role=\"main\" ist Nachfahre eines anderen Landmarks.",
technical_impact_en: "<main>/role=\"main\" is a descendant of another landmark.",
score_impact: ScoreImpact {
base_penalty: 1.0,
max_penalty: 3.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.landmark_banner_duplicate.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("landmark-no-duplicate-banner"),
title: "Mehrere Banner-Landmarks",
title_en: "Multiple banner landmarks",
description: "Die Seite hat mehr als einen Banner-Bereich (<header>/role=\"banner\"); erlaubt ist höchstens einer.",
user_impact: "Screenreader-Nutzer können den 'Kopfbereich' der Seite nicht eindeutig zuordnen.",
user_impact_en: "Screen reader users cannot uniquely identify the page's 'header' region.",
technical_impact: "Mehr als ein Element mit role=\"banner\" (implizit oder explizit).",
technical_impact_en: "More than one element with role=\"banner\" (implicit or explicit).",
score_impact: ScoreImpact {
base_penalty: 1.0,
max_penalty: 3.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.landmark_contentinfo_duplicate.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("landmark-no-duplicate-contentinfo"),
title: "Mehrere Contentinfo-Landmarks",
title_en: "Multiple contentinfo landmarks",
description: "Die Seite hat mehr als einen Fußbereich (<footer>/role=\"contentinfo\"); erlaubt ist höchstens einer.",
user_impact: "Screenreader-Nutzer können den 'Fußbereich' der Seite nicht eindeutig zuordnen.",
user_impact_en: "Screen reader users cannot uniquely identify the page's 'footer' region.",
technical_impact: "Mehr als ein Element mit role=\"contentinfo\" (implizit oder explizit).",
technical_impact_en: "More than one element with role=\"contentinfo\" (implicit or explicit).",
score_impact: ScoreImpact {
base_penalty: 1.0,
max_penalty: 3.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.landmark_main_duplicate.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("landmark-no-duplicate-main"),
title: "Mehrere Main-Landmarks",
title_en: "Multiple main landmarks",
description: "Die Seite hat mehr als einen Hauptinhaltsbereich (<main>/role=\"main\"); erlaubt ist genau einer.",
user_impact: "Screenreader-Nutzer, die zum Hauptinhalt springen, wissen nicht, welcher Bereich gemeint ist.",
user_impact_en: "Screen reader users who jump to the main content cannot tell which region is meant.",
technical_impact: "Mehr als ein Element mit role=\"main\" (implizit oder explizit).",
technical_impact_en: "More than one element with role=\"main\" (implicit or explicit).",
score_impact: ScoreImpact {
base_penalty: 1.2,
max_penalty: 3.5,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.landmark_banner.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("landmark-banner-present"),
title: "Fehlender Banner-Landmark",
title_en: "Missing banner landmark",
description: "Die Seite hat keinen Banner-Bereich (<header> bzw. role=\"banner\").",
user_impact: "Assistierende Technologien können den Kopfbereich der Seite nicht identifizieren.",
user_impact_en: "Assistive technologies cannot identify the site header.",
technical_impact: "Kein Element mit role=\"banner\" (implizit über <header> oder explizit) vorhanden.",
technical_impact_en: "No element with role=\"banner\" (implicit via <header> or explicit) present.",
score_impact: ScoreImpact {
base_penalty: 1.0,
max_penalty: 3.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.landmark_navigation.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Missing,
severity: Severity::Low,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("landmark-navigation-present"),
title: "Fehlender Navigations-Landmark",
title_en: "Missing navigation landmark",
description: "Die Seite hat keinen Navigationsbereich (<nav> bzw. role=\"navigation\").",
user_impact: "Screenreader-Nutzer können nicht direkt zur Navigation springen.",
user_impact_en: "Screen reader users cannot jump straight to the navigation.",
technical_impact: "Kein Element mit role=\"navigation\" (implizit über <nav> oder explizit) vorhanden.",
technical_impact_en: "No element with role=\"navigation\" (implicit via <nav> or explicit) present.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.landmark_contentinfo.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Missing,
severity: Severity::Low,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("landmark-contentinfo-present"),
title: "Fehlender Fußbereich-Landmark (contentinfo)",
title_en: "Missing contentinfo landmark",
description: "Die Seite hat keinen Fußbereich als Landmark (<footer> auf oberster Ebene bzw. role=\"contentinfo\").",
user_impact: "Screenreader-Nutzer können den Fußbereich mit Kontakt, Impressum und Rechtlichem nicht direkt ansteuern.",
user_impact_en: "Screen reader users cannot jump straight to the footer with contact and legal information.",
technical_impact: "Kein Element mit role=\"contentinfo\" (implizit über ein <footer> außerhalb von <main>/<article>/<section> oder explizit) vorhanden.",
technical_impact_en: "No element with role=\"contentinfo\" (implicit via a <footer> outside <main>/<article>/<section>, or explicit) present.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.list_structure.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("lists/item-outside-list"),
title: "Listenelemente ohne Listen-Container",
title_en: "List items outside a list container",
description: "Listenelemente (<li> oder role=\"listitem\") liegen nicht innerhalb eines Listen-Containers (<ul>/<ol> oder role=\"list\").",
user_impact: "Screenreader-Nutzer erhalten keine Ansage der Listenlänge/-struktur.",
user_impact_en: "Screen reader users receive no announcement of the list's length/structure.",
technical_impact: "<li> außerhalb von <ul>/<ol>.",
technical_impact_en: "<li> outside <ul>/<ol>.",
score_impact: ScoreImpact {
base_penalty: 1.2,
max_penalty: 3.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.heading_level.skipped",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("headings/skip-level"),
title: "Übersprungene Überschriftenebene",
title_en: "Skipped heading level",
description: "Die Überschriftenhierarchie springt über eine Ebene (z. B. von h1 direkt auf h3).",
user_impact: "Screenreader-Nutzer, die über Überschriften navigieren, vermuten fehlende Abschnitte.",
user_impact_en: "Screen reader users navigating by headings assume that sections are missing.",
technical_impact: "Überschriftenebenen folgen nicht lückenlos aufeinander.",
technical_impact_en: "Heading levels do not follow each other without gaps.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.heading_empty.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 2.4.6"),
external_level: Some("AA"),
axe_id: Some("headings/empty"),
title: "Leere Überschrift",
title_en: "Empty heading",
description: "Ein Überschriften-Element enthält keinen Text.",
user_impact: "Screenreader-Nutzer stoßen in der Überschriftenliste auf einen Eintrag ohne Inhalt.",
user_impact_en: "Screen reader users find an entry without content in the list of headings.",
technical_impact: "<h1>–<h6> bzw. role=\"heading\" ohne Text und ohne aria-label.",
technical_impact_en: "<h1>–<h6> or role=\"heading\" without text and without aria-label.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.main_heading.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("headings/h1-missing"),
title: "Fehlende Hauptüberschrift (h1)",
title_en: "Missing main heading (h1)",
description: "Die Seite hat keine Überschrift der Ebene 1.",
user_impact: "Screenreader-Nutzer finden den Einstieg in den Hauptinhalt nicht über die Überschriftennavigation.",
user_impact_en: "Screen reader users cannot find the start of the main content via heading navigation.",
technical_impact: "Kein <h1>-Element im Dokument.",
technical_impact_en: "No <h1> element in the document.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.main_heading_multiple.weak",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Weak,
severity: Severity::Low,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("headings/h1-multiple"),
title: "Mehrere Hauptüberschriften (h1)",
title_en: "Multiple main headings (h1)",
description: "Die Seite hat mehr als eine Überschrift der Ebene 1. In HTML zulässig, aber prüfenswert.",
user_impact: "Screenreader-Nutzer können den eigentlichen Seitentitel schwerer vom Rest unterscheiden.",
user_impact_en: "Screen reader users find it harder to tell the actual page title from the rest.",
technical_impact: "Mehrere <h1>-Elemente im Dokument.",
technical_impact_en: "Several <h1> elements in the document.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.list_children.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("lists/invalid-structure"),
title: "Liste mit unzulässigen Kindelementen",
title_en: "List with invalid child elements",
description: "Eine Liste enthält direkte Kinder, die keine Listenelemente sind.",
user_impact: "Screenreader-Nutzer hören eine falsche Anzahl von Listeneinträgen oder verlieren Inhalte.",
user_impact_en: "Screen reader users hear a wrong number of list items or lose content.",
technical_impact: "Direkte Kinder von <ul>/<ol>/role=\"list\" sind keine <li>/role=\"listitem\".",
technical_impact_en: "Direct children of <ul>/<ol>/role=\"list\" are not <li>/role=\"listitem\".",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.list_empty.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Invalid,
severity: Severity::Low,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("lists/empty"),
title: "Leere Liste",
title_en: "Empty list",
description: "Eine Liste enthält keine Listenelemente.",
user_impact: "Screenreader kündigen eine Liste ohne Inhalt an.",
user_impact_en: "Screen readers announce a list without content.",
technical_impact: "<ul>/<ol>/role=\"list\" ohne <li>/role=\"listitem\".",
technical_impact_en: "<ul>/<ol>/role=\"list\" without <li>/role=\"listitem\".",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.definition_description.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("lists/term-without-definition"),
title: "Begriff ohne Definition in Definitionsliste",
title_en: "Term without definition in a description list",
description: "Ein Begriff (<dt>) in einer Definitionsliste hat keine zugehörige Definition (<dd>).",
user_impact: "Screenreader-Nutzer können Begriff und Definition nicht als zusammengehörig erkennen.",
user_impact_en: "Screen reader users cannot recognize the term and its definition as belonging together.",
technical_impact: "<dt> ohne folgendes <dd> in der <dl>.",
technical_impact_en: "<dt> without a following <dd> inside the <dl>.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.table_headers.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("tables/header-missing"),
title: "Datentabelle ohne Kopfzellen",
title_en: "Data table without header cells",
description: "Eine Datentabelle hat keine Kopfzellen, die Zeilen oder Spalten beschriften.",
user_impact: "Screenreader-Nutzer hören Zellwerte ohne die zugehörige Zeilen- oder Spaltenbeschriftung.",
user_impact_en: "Screen reader users hear cell values without their row or column label.",
technical_impact: "Kein <th> und keine Zelle mit role=\"columnheader\"/\"rowheader\".",
technical_impact_en: "No <th> and no cell with role=\"columnheader\"/\"rowheader\".",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.table_name.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Missing,
severity: Severity::Low,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("tables/name-missing"),
title: "Datentabelle ohne Namen",
title_en: "Data table without a name",
description: "Eine Datentabelle hat weder <caption> noch aria-label.",
user_impact: "Screenreader-Nutzer erfahren beim Betreten der Tabelle nicht, worum es in ihr geht.",
user_impact_en: "Screen reader users are not told what the table is about when entering it.",
technical_impact: "Kein <caption> und kein aria-label an der Tabelle.",
technical_impact_en: "No <caption> and no aria-label on the table.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.table_presentational_headers.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("tables/presentational-with-headers"),
title: "Layouttabelle mit Kopfzellen",
title_en: "Layout table with header cells",
description: "Eine als präsentational ausgezeichnete Tabelle enthält Kopfzellen.",
user_impact: "Screenreader-Nutzer erhalten widersprüchliche Signale, ob es sich um eine Datentabelle handelt.",
user_impact_en: "Screen reader users receive contradictory signals about whether this is a data table.",
technical_impact: "role=\"presentation\"/\"none\" an einer Tabelle mit <th>.",
technical_impact_en: "role=\"presentation\"/\"none\" on a table with <th>.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.page_language.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::LanguageClarity,
issue_class: IssueClass::Invalid,
severity: Severity::High,
external_ref: Some("WCAG 3.1.1"),
external_level: Some("A"),
axe_id: Some("document/lang-invalid"),
title: "Ungültige Sprachangabe der Seite",
title_en: "Invalid page language",
description: "Das lang-Attribut am html-Element ist kein gültiger BCP-47-Sprachcode.",
user_impact: "Screenreader wählen eine falsche oder die Standard-Aussprache.",
user_impact_en: "Screen readers choose a wrong or the default pronunciation.",
technical_impact: "Ungültiger Wert im lang-Attribut des html-Elements.",
technical_impact_en: "Invalid value in the html element's lang attribute.",
score_impact: ScoreImpact {
base_penalty: 10.0,
max_penalty: 12.5,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.positive_tabindex.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Invalid,
severity: Severity::High,
external_ref: Some("WCAG 2.4.3"),
external_level: Some("A"),
axe_id: Some("keyboard/positive-tabindex"),
title: "Positiver tabindex verändert die Fokusreihenfolge",
title_en: "Positive tabindex changes the focus order",
description: "Ein Element hat einen tabindex größer als 0 und springt damit in der Tab-Reihenfolge vor alle übrigen Elemente.",
user_impact: "Tastaturnutzer erreichen Elemente in einer Reihenfolge, die nicht dem Seitenaufbau entspricht.",
user_impact_en: "Keyboard users reach elements in an order that does not match the page layout.",
technical_impact: "tabindex-Wert größer als 0.",
technical_impact_en: "tabindex value greater than 0.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.label_title_only.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("label-title-only"),
title: "Formularfeld-Beschriftung nur über title-Attribut",
title_en: "Form field labeled only via the title attribute",
description: "Ein Formularfeld bezieht seinen zugänglichen Namen ausschließlich aus dem title-Attribut statt aus einem sichtbaren Label.",
user_impact: "Das title-Attribut wird von vielen Screenreadern inkonsistent oder gar nicht vorgelesen; sehende Nutzer sehen es nur bei Hover.",
user_impact_en: "The title attribute is read inconsistently or not at all by many screen readers; sighted users only see it on hover.",
technical_impact: "Formularelement ohne <label>/aria-label, Name kommt nur aus title.",
technical_impact_en: "Form element without <label>/aria-label, name derived only from title.",
score_impact: ScoreImpact {
base_penalty: 1.2,
max_penalty: 3.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.viewport_zoom.restricted",
dimension: Dimension::Accessibility,
subcategory: Subcategory::VisualPresentation,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 1.4.4"),
external_level: Some("AA"),
axe_id: Some("zoom/viewport-scale-limited"),
title: "Zoom auf 500% durch Viewport-Meta-Tag verhindert",
title_en: "Viewport meta tag prevents zooming to 500%",
description: "Das viewport-Meta-Tag begrenzt maximum-scale so stark, dass Nutzer nicht auf mindestens 500% zoomen können.",
user_impact: "Sehbehinderte Nutzer können den Seiteninhalt nicht ausreichend vergrößern.",
user_impact_en: "Users with low vision cannot magnify the page content enough.",
technical_impact: "maximum-scale im viewport-Meta-Tag unter 5 gesetzt.",
technical_impact_en: "maximum-scale in the viewport meta tag set below 5.",
score_impact: ScoreImpact {
base_penalty: 1.5,
max_penalty: 4.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.click_handler_keyboard.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 2.1.1"),
external_level: Some("A"),
axe_id: Some("click-events-have-key-events"),
title: "Klick-Handler ohne Tastatur-Äquivalent",
title_en: "Click handler without keyboard equivalent",
description: "Ein nicht-interaktives Element (z. B. <div>) hat einen Klick-Handler, aber keinen entsprechenden Tastatur-Event-Handler.",
user_impact: "Tastaturnutzer können die Funktion nicht auslösen.",
user_impact_en: "Keyboard users cannot trigger the function.",
technical_impact: "onclick auf einem <div>/<span> ohne onkeydown/onkeyup, tabindex oder passende Rolle.",
technical_impact_en: "onclick on a <div>/<span> without onkeydown/onkeyup, tabindex, or a matching role.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.bypass_main_landmark.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 2.4.1"),
external_level: Some("A"),
axe_id: Some("landmark-one-main"),
title: "Kein Hauptinhaltsbereich als Sprungziel",
title_en: "No main-content region as a bypass target",
description: "Die Seite hat keinen <main>-Bereich, der als Sprungziel zum Überspringen wiederkehrender Blöcke dienen könnte.",
user_impact: "Tastaturnutzer haben kein verlässliches Ziel, um wiederkehrende Navigation zu überspringen.",
user_impact_en: "Keyboard users have no reliable target to bypass repeated navigation blocks.",
technical_impact: "Kein Element mit role=\"main\" (implizit über <main> oder explizit) vorhanden.",
technical_impact_en: "No element with role=\"main\" (implicit via <main> or explicit) present.",
score_impact: ScoreImpact {
base_penalty: 1.5,
max_penalty: 4.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.skip_link.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 2.4.1"),
external_level: Some("A"),
axe_id: Some("skip-link"),
title: "Fehlender Skip-Link",
title_en: "Missing skip link",
description: "Die Seite hat eine Navigation, aber keinen Skip-Link, um sie zu überspringen und direkt zum Inhalt zu springen.",
user_impact: "Tastaturnutzer müssen bei jedem Seitenwechsel durch die gesamte Navigation tabben.",
user_impact_en: "Keyboard users must tab through the entire navigation on every page change.",
technical_impact: "Kein Link mit erkennbarem 'skip'/'zum Inhalt'-Text oder Anker auf #main/#content am Seitenanfang.",
technical_impact_en: "No link with a recognizable 'skip'/'to content' text or anchor to #main/#content at the top of the page.",
score_impact: ScoreImpact {
base_penalty: 1.5,
max_penalty: 4.0,
occurrence_scaling: Scaling::Fixed,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.focus_indicator_suppressed.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Invalid,
severity: Severity::High,
external_ref: Some("WCAG 2.4.7"),
external_level: Some("AA"),
axe_id: Some("focus-visible-outline-none"),
title: "Fokus-Indikator per CSS entfernt",
title_en: "Focus indicator removed via CSS",
description: "CSS entfernt den sichtbaren Fokusrahmen (outline: none/0) ohne einen gleichwertigen Ersatz bereitzustellen.",
user_impact: "Tastaturnutzer sehen nicht, welches Element gerade fokussiert ist.",
user_impact_en: "Keyboard users cannot see which element currently has focus.",
technical_impact: "outline: none/0 in :focus-Regeln ohne alternative sichtbare Fokusmarkierung.",
technical_impact_en: "outline: none/0 in :focus rules without an alternative visible focus indicator.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.scrollable_region_focus.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 2.1.1"),
external_level: Some("A"),
axe_id: Some("scrollable-region-focusable"),
title: "Scrollbarer Bereich per Tastatur nicht erreichbar",
title_en: "Scrollable region not reachable by keyboard",
description: "Ein Bereich mit eigenem Scrollbalken ist nicht fokussierbar und enthält keine fokussierbaren Elemente.",
user_impact: "Tastaturnutzer können den Bereich nicht scrollen und den verdeckten Inhalt nicht lesen.",
user_impact_en: "Keyboard users cannot scroll the region and cannot read the hidden content.",
technical_impact: "Element mit overflow: auto/scroll und überlaufendem Inhalt, ohne tabindex und ohne fokussierbare Kinder.",
technical_impact_en: "Element with overflow: auto/scroll and overflowing content, without tabindex and without focusable children.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.error_description.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::FormsInteraction,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 3.3.1"),
external_level: Some("A"),
axe_id: Some("input-error-message"),
title: "Ungültiges Feld ohne zugängliche Fehlerbeschreibung",
title_en: "Invalid field without an accessible error description",
description: "Ein als ungültig markiertes Formularfeld hat keine zugängliche Beschreibung des Fehlers.",
user_impact: "Screenreader-Nutzer erfahren, dass ein Fehler vorliegt, aber nicht welcher.",
user_impact_en: "Screen reader users learn that an error exists, but not what it is.",
technical_impact: "aria-invalid=\"true\" ohne description/aria-describedby auf ein sichtbares Fehlerelement.",
technical_impact_en: "aria-invalid=\"true\" without a description/aria-describedby pointing to a visible error element.",
score_impact: ScoreImpact {
base_penalty: 1.5,
max_penalty: 4.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.duplicate_id_aria.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Invalid,
severity: Severity::High,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("duplicate-id-aria"),
title: "ARIA-Referenz zeigt auf mehrdeutige ID",
title_en: "ARIA reference points to an ambiguous ID",
description: "Ein ARIA-Attribut wie aria-owns/aria-describedby/aria-labelledby verweist auf eine ID, die im Dokument mehrfach vergeben ist.",
user_impact: "Assistierende Technologien können nicht zuverlässig bestimmen, welches Element gemeint ist.",
user_impact_en: "Assistive technologies cannot reliably determine which element is referenced.",
technical_impact: "Referenzierte ID kommt im DOM mehr als einmal vor.",
technical_impact_en: "The referenced ID occurs more than once in the DOM.",
score_impact: ScoreImpact {
base_penalty: 3.0,
max_penalty: 10.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.interactive_name.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria-label"),
title: "Interaktives Element ohne aussagekräftigen Namen",
title_en: "Interactive element without a meaningful name",
description: "Ein interaktives Element hat keinen zugänglichen Namen, oder der Name besteht nur aus einem Icon/Symbol ohne Beschreibung.",
user_impact: "Screenreader-Nutzer erfahren nicht, wozu das Element dient.",
user_impact_en: "Screen reader users are not told what the element does.",
technical_impact: "Fehlendes oder rein symbolisches aria-label/aria-labelledby/Textinhalt.",
technical_impact_en: "Missing or purely symbolic aria-label/aria-labelledby/text content.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.title_only_description.weak",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Weak,
severity: Severity::Medium,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("title-only-description"),
title: "Natives title-Attribut als einzige Beschreibung",
title_en: "Native title attribute as the only description",
description: "Ein interaktives Element wird nur über das native title-Attribut beschrieben, ohne sichtbare Beschriftung, aria-label oder aria-labelledby (Best Practice).",
user_impact: "Der Tooltip des title-Attributs erscheint nur bei Mauszeiger-Hover; per Tastatur und auf Touchgeräten bleibt der Zweck des Elements unsichtbar.",
user_impact_en: "The title attribute's tooltip only appears on mouse hover; with a keyboard and on touch devices the element's purpose stays invisible.",
technical_impact: "Der zugängliche Name stammt nur aus dem title-Attribut; kein Textinhalt, kein aria-label, kein aria-labelledby.",
technical_impact_en: "The accessible name comes only from the title attribute; no text content, no aria-label, no aria-labelledby.",
score_impact: ScoreImpact {
base_penalty: 1.0,
max_penalty: 4.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.description_duplicates_name.redundant",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Weak,
severity: Severity::Low,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("description-duplicates-name"),
title: "Beschreibung wiederholt den Namen",
title_en: "Description repeats the name",
description: "Zugänglicher Name und zugängliche Beschreibung eines Elements sind identisch (Best Practice).",
user_impact: "Screenreader sagen denselben Text zweimal an, ohne zusätzliche Information.",
user_impact_en: "Screen readers announce the same text twice without adding information.",
technical_impact: "aria-describedby oder title liefert denselben Text wie der Name.",
technical_impact_en: "aria-describedby or title yields the same text as the name.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 2.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.aria_required_children.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("aria-required-children"),
title: "Erforderliche Kindelemente einer ARIA-Rolle fehlen",
title_en: "Required child elements of an ARIA role are missing",
description: "Ein Element mit einer Composite-ARIA-Rolle (z. B. tablist) fehlt eines der laut ARIA-Spezifikation erforderlichen Kindelemente (z. B. zugehörige tabpanels).",
user_impact: "Assistierende Technologien können die Beziehung zwischen den Widget-Teilen nicht auswerten.",
user_impact_en: "Assistive technologies cannot resolve the relationship between the widget's parts.",
technical_impact: "z. B. role=\"tablist\" ohne zugehörige role=\"tabpanel\"-Elemente.",
technical_impact_en: "e.g. role=\"tablist\" without corresponding role=\"tabpanel\" elements.",
score_impact: ScoreImpact {
base_penalty: 1.5,
max_penalty: 4.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.dialog_missing_name.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("dialog-name"),
title: "Dialog ohne zugänglichen Namen",
title_en: "Dialog without an accessible name",
description: "Ein Dialog (role=\"dialog\"/\"alertdialog\") hat keinen zugänglichen Namen.",
user_impact: "Screenreader-Nutzer erfahren beim Öffnen des Dialogs nicht, worum es geht.",
user_impact_en: "Screen reader users are not told what the dialog is about when it opens.",
technical_impact: "Dialog-Element ohne aria-label oder aria-labelledby auf den Dialogtitel.",
technical_impact_en: "Dialog element without aria-label or aria-labelledby pointing to the dialog title.",
score_impact: ScoreImpact {
base_penalty: 2.0,
max_penalty: 6.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.control_name.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("control-missing-label"),
title: "Formularelement, Link oder Button ohne zugänglichen Namen",
title_en: "Form control, link, or button without an accessible name",
description: "Ein Formularfeld, Link oder Button hat keinen zugänglichen Namen (weder sichtbares Label noch aria-label/aria-labelledby).",
user_impact: "Screenreader-Nutzer erfahren nicht, welche Eingabe erwartet wird oder wohin ein Link/Button führt.",
user_impact_en: "Screen reader users are not told what input is expected or where a link/button leads.",
technical_impact: "Formularelement/Link/Button ohne <label>, aria-label, aria-labelledby oder Textinhalt.",
technical_impact_en: "Form control/link/button without <label>, aria-label, aria-labelledby, or text content.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_ALL,
},
Rule {
id: "a11y.link_as_button.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Invalid,
severity: Severity::Low,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("link-as-button"),
title: "Link als Button-Ersatz ohne Tastenrolle",
title_en: "Link used as a button substitute without a matching role",
description: "Ein Link (<a>) mit href=\"#\" oder javascript:-Pseudo-URL und onclick-Handler wird wie ein Button verwendet, ohne role=\"button\" zu tragen.",
user_impact: "Assistierende Technologien kündigen das Element als Link an, obwohl es sich wie ein Button verhält — Tastaturnutzer erwarten das falsche Bedienverhalten.",
user_impact_en: "Assistive technologies announce the element as a link even though it behaves like a button — keyboard users expect the wrong interaction.",
technical_impact: "<a href=\"#\">/<a href=\"javascript:...\"> mit onclick, ohne role=\"button\".",
technical_impact_en: "<a href=\"#\">/<a href=\"javascript:...\"> with onclick, without role=\"button\".",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 2.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.redundant_role.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Invalid,
severity: Severity::Low,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("redundant-role"),
title: "Redundante ARIA-Rolle",
title_en: "Redundant ARIA role",
description: "Ein Element trägt ein role-Attribut, das exakt der bereits impliziten Rolle des HTML-Elements entspricht (z. B. <button role=\"button\">).",
user_impact: "Kein unmittelbarer Nutzerschaden, aber unnötiges Risiko für Inkonsistenzen bei zukünftigen Änderungen.",
user_impact_en: "No immediate user harm, but an unnecessary risk of inconsistency in future changes.",
technical_impact: "role-Attribut dupliziert die native implizite Rolle des Tags.",
technical_impact_en: "role attribute duplicates the tag's native implicit role.",
score_impact: ScoreImpact {
base_penalty: 0.3,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.summary_name.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::TechnicalRobustness,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 4.1.2"),
external_level: Some("A"),
axe_id: Some("summary-name"),
title: "Summary-Element ohne zugänglichen Namen",
title_en: "Summary element without an accessible name",
description: "Ein <summary>-Element (Aufklapp-Auslöser in <details>) hat keinen zugänglichen Namen.",
user_impact: "Screenreader-Nutzer erfahren nicht, was der Aufklapp-Bereich enthält, bevor sie ihn öffnen.",
user_impact_en: "Screen reader users are not told what the disclosure section contains before opening it.",
technical_impact: "<summary> ohne Textinhalt oder aria-label.",
technical_impact_en: "<summary> without text content or aria-label.",
score_impact: ScoreImpact {
base_penalty: 1.5,
max_penalty: 4.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.landmark_region.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::StructureSemantics,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 1.3.1"),
external_level: Some("A"),
axe_id: Some("region"),
title: "Inhalt außerhalb einer Landmark-Region",
title_en: "Content outside a landmark region",
description: "Inhalt außerhalb einer Landmark-Region.",
user_impact: "Inhalt außerhalb einer Landmark-Region.",
user_impact_en: "Content outside a landmark region.",
technical_impact: "Inhalt außerhalb einer Landmark-Region.",
technical_impact_en: "Content outside a landmark region.",
score_impact: ScoreImpact {
base_penalty: 1.5,
max_penalty: 4.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.focusable_no_role.invalid",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Invalid,
severity: Severity::Critical,
external_ref: Some("WCAG 2.1.1"),
external_level: Some("A"),
axe_id: Some("focusable-no-role"),
title: "Fokussierbares Element ohne interaktive Rolle",
title_en: "Focusable element without an interactive role",
description: "Fokussierbares Element ohne interaktive Rolle.",
user_impact: "Fokussierbares Element ohne interaktive Rolle.",
user_impact_en: "Focusable element without an interactive role.",
technical_impact: "Fokussierbares Element ohne interaktive Rolle.",
technical_impact_en: "Focusable element without an interactive role.",
score_impact: ScoreImpact {
base_penalty: 2.0,
max_penalty: 6.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.link_purpose_context.weak",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Weak,
severity: Severity::Low,
external_ref: Some("WCAG 2.4.4"),
external_level: Some("A"),
axe_id: Some("link-name-context"),
title: "Generischer Linktext mit möglichem Kontextbezug",
title_en: "Generic link text with possible contextual meaning",
description: "Generischer Linktext mit möglichem Kontextbezug.",
user_impact: "Generischer Linktext mit möglichem Kontextbezug.",
user_impact_en: "Generic link text with possible contextual meaning.",
technical_impact: "Generischer Linktext mit möglichem Kontextbezug.",
technical_impact_en: "Generic link text with possible contextual meaning.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 2.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.display_toggle.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Missing,
severity: Severity::Medium,
external_ref: Some("WCAG 2.2.2"),
external_level: Some("A"),
axe_id: Some("display/toggle-missing"),
title: "Kein Umschalter für Darstellungsmodi",
title_en: "No display-mode toggle",
description: "Die Seite enthält Visualisierungen (figure[data-viz]), aber keinen Umschalter [data-display-toggle] (Best Practice, Darstellungs-Konvention).",
user_impact: "Fehlt der Umschalter tatsächlich, kann nicht auf eine ruhige oder reine Textdarstellung wechseln, wer Bewegung oder Grafiken nicht verträgt.",
user_impact_en: "If there really is no toggle, visitors who cannot cope with motion or graphics cannot switch to a calm or text-only display.",
technical_impact: "Kein Element mit [data-display-toggle] auf einer Seite mit figure[data-viz].",
technical_impact_en: "No [data-display-toggle] element on a page with figure[data-viz].",
score_impact: ScoreImpact {
base_penalty: 1.5,
max_penalty: 4.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.display_init.late",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Weak,
severity: Severity::Low,
external_ref: Some("WCAG 2.2.2"),
external_level: Some("A"),
axe_id: Some("display/init-missing"),
title: "Darstellungsmodus wird zu spät gesetzt",
title_en: "Display mode set too late",
description: "html[data-display] fehlt oder wird erst nach Beginn des Seitenkörpers gesetzt (Best Practice, Darstellungs-Konvention).",
user_impact: "Die Seite kann kurz in der falschen Darstellung erscheinen, etwa mit Animation, bevor die gewählte ruhige Darstellung greift.",
user_impact_en: "The page can briefly render in the wrong mode, for example with animation, before the chosen calm mode applies.",
technical_impact: "data-display nicht per blockierendem Skript im <head> gesetzt.",
technical_impact_en: "data-display not set by a blocking script in <head>.",
score_impact: ScoreImpact {
base_penalty: 0.5,
max_penalty: 1.5,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.display_text_media.visible",
dimension: Dimension::Accessibility,
subcategory: Subcategory::NavigationInteraction,
issue_class: IssueClass::Invalid,
severity: Severity::Medium,
external_ref: Some("WCAG 2.2.2"),
external_level: Some("A"),
axe_id: Some("display/text-media-visible"),
title: "Grafiken im Textmodus sichtbar",
title_en: "Visualisations visible in text mode",
description: "Im Darstellungsmodus text zeigt eine Visualisierung weiterhin Canvas, SVG, Video oder Bild (Best Practice, Darstellungs-Konvention).",
user_impact: "Wer den Textmodus gewählt hat, bekommt trotzdem Grafiken oder bewegte Inhalte.",
user_impact_en: "Visitors who chose text mode still get graphics or moving content.",
technical_impact: "[data-viz-live]/[data-viz-static] oder Medien der figure bei html[data-display=text] sichtbar.",
technical_impact_en: "[data-viz-live]/[data-viz-static] or figure media visible with html[data-display=text].",
score_impact: ScoreImpact {
base_penalty: 1.5,
max_penalty: 4.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.display_text_layer.missing",
dimension: Dimension::Accessibility,
subcategory: Subcategory::ContentAlternatives,
issue_class: IssueClass::Missing,
severity: Severity::High,
external_ref: Some("WCAG 1.1.1"),
external_level: Some("A"),
axe_id: Some("display/text-not-visible"),
title: "Textschicht einer Visualisierung fehlt im Textmodus",
title_en: "Visualisation text layer missing in text mode",
description: "Im Darstellungsmodus text hat eine Visualisierung kein sichtbares, nicht-leeres [data-viz-text] (Best Practice, Darstellungs-Konvention).",
user_impact: "Im Textmodus geht die Aussage der Grafik verloren.",
user_impact_en: "In text mode the statement of the visualisation is lost.",
technical_impact: "[data-viz-text] fehlt, ist leer oder bei html[data-display=text] nicht sichtbar.",
technical_impact_en: "[data-viz-text] missing, empty or not visible with html[data-display=text].",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
Rule {
id: "a11y.display_text_layer.hidden",
dimension: Dimension::Accessibility,
subcategory: Subcategory::ContentAlternatives,
issue_class: IssueClass::Invalid,
severity: Severity::High,
external_ref: Some("WCAG 1.1.1"),
external_level: Some("A"),
axe_id: Some("display/text-hidden"),
title: "Textschicht einer Visualisierung für Hilfstechnik verborgen",
title_en: "Visualisation text layer hidden from assistive technology",
description: "[data-viz-text] ist per hidden, aria-hidden, inert oder CSS aus dem Accessibility Tree entfernt (Best Practice, Darstellungs-Konvention).",
user_impact: "Screenreader-Nutzer erfahren die Aussage der Grafik nicht.",
user_impact_en: "Screen reader users do not get the statement of the visualisation.",
technical_impact: "hidden, aria-hidden=true, inert oder display:none/visibility:hidden an [data-viz-text] oder einem Vorfahren.",
technical_impact_en: "hidden, aria-hidden=true, inert or display:none/visibility:hidden on [data-viz-text] or an ancestor.",
score_impact: ScoreImpact {
base_penalty: 2.5,
max_penalty: 8.0,
occurrence_scaling: Scaling::Logarithmic,
},
report_visibility: VIS_STANDARD,
},
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rule_lookup_by_id() {
let rule = RuleLookup::by_id("a11y.alt_text.missing");
assert!(rule.is_some());
let rule = rule.unwrap();
assert_eq!(rule.dimension, Dimension::Accessibility);
assert_eq!(rule.severity, Severity::High);
}
#[test]
fn test_legacy_wcag_lookup() {
let rule = RuleLookup::by_legacy_wcag_id("1.1.1");
assert!(rule.is_some());
assert_eq!(rule.unwrap().id, "a11y.alt_text.missing");
}
#[test]
fn test_frame_title_taxonomy_uses_wcag_241() {
let rule = RuleLookup::by_legacy_wcag_id("frame-title")
.expect("frame-title axe id should resolve");
assert_eq!(rule.id, "a11y.frame_title.missing");
assert_eq!(rule.external_ref, Some("WCAG 2.4.1"));
assert_eq!(rule.axe_id, Some("frame-title"));
}
#[test]
fn test_dom_parity_rules_resolve_to_specific_taxonomy() {
let form =
RuleLookup::by_legacy_wcag_id("form-no-submit").expect("form-no-submit should resolve");
assert_eq!(form.id, "a11y.form_no_submit.missing");
assert_eq!(form.external_ref, Some("WCAG 3.2.2"));
let presentation = RuleLookup::by_legacy_wcag_id("presentation-semantic-children")
.expect("presentation-semantic-children should resolve");
assert_eq!(
presentation.id,
"a11y.presentation_semantic_children.invalid"
);
assert_eq!(presentation.external_ref, Some("WCAG 1.3.1"));
let landmark_main = RuleLookup::by_legacy_wcag_id("landmark-main-present")
.expect("landmark-main-present should resolve");
assert_eq!(landmark_main.id, "a11y.landmark_main.missing");
assert_eq!(landmark_main.external_ref, Some("WCAG 1.3.1"));
let landmark_unique = RuleLookup::by_legacy_wcag_id("landmark-unique")
.expect("landmark-unique should resolve");
assert_eq!(landmark_unique.id, "a11y.landmark_unique.invalid");
assert_eq!(landmark_unique.external_ref, Some("WCAG 1.3.1"));
let modern = RuleLookup::by_legacy_wcag_id("modern-attribute-misuse")
.expect("modern-attribute-misuse should resolve");
assert_eq!(modern.id, "a11y.modern_attributes.invalid");
assert_eq!(modern.external_ref, Some("WCAG 4.1.2"));
assert_eq!(modern.axe_id, Some("modern-attribute-misuse"));
}
#[test]
fn test_all_rules_have_dimension() {
for rule in RULES {
assert!(
rule.subcategory.dimension() == rule.dimension,
"Rule {} has subcategory {:?} which belongs to {:?}, not {:?}",
rule.id,
rule.subcategory,
rule.subcategory.dimension(),
rule.dimension
);
}
}
#[test]
fn test_all_legacy_wcag_ids_resolve() {
for (wcag_id, rule_id) in LEGACY_WCAG_MAP {
assert!(
RuleLookup::by_id(rule_id).is_some(),
"Legacy WCAG ID {} maps to rule {} which doesn't exist",
wcag_id,
rule_id
);
}
}
#[test]
fn test_shared_rules_resolve_to_specific_taxonomy() {
for shared in crate::wcag::shared::SHARED_RULES {
let rule = RuleLookup::by_legacy_wcag_id(shared.id)
.or_else(|| RuleLookup::by_legacy_wcag_id(shared.criterion))
.unwrap_or_else(|| panic!("{} resolves to no taxonomy rule", shared.id));
assert_eq!(
rule.external_ref,
Some(format!("WCAG {}", shared.criterion).as_str()),
"{} resolves to {} with a different criterion",
shared.id,
rule.id
);
assert_ne!(
rule.id, "a11y.structure.missing",
"{} falls into the generic 1.3.1 bucket",
shared.id
);
}
}
#[test]
fn test_unique_rule_ids() {
let mut ids: Vec<&str> = RULES.iter().map(|r| r.id).collect();
ids.sort();
let len_before = ids.len();
ids.dedup();
assert_eq!(len_before, ids.len(), "Duplicate rule IDs found");
}
}