astro-accessibility — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited astro-accessibility (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Astro's static-first model is inherently accessibility-friendly because HTML renders without JavaScript. Leverage this advantage fully.
<nav>, <button>, <dialog>, <main>, <header>, <footer>) for their intended purpose. NEVER style <div> or <span> elements to look like interactive controls.<button> handles Enter, Space, focus, and role automatically — a styled <div> provides none of this.<main>, a <nav> for navigation, <header> and <footer> for page-level landmarks. Screen reader users navigate by landmarks.<h1> per page, then <h2> through <h6> in logical order without skipping levels. Headings are the primary navigation mechanism for screen reader users.Follow the first rule of ARIA: do NOT use ARIA if a native HTML element or attribute exists that provides the semantics you need. ARIA is a last resort, not a first choice.
role="tablist", role="alert", role="status", role="dialog". Apply roles only when you cannot use the native element.aria-expanded="true|false", aria-selected="true|false", aria-pressed="true|false". Update these dynamically as the UI changes.aria-labelledby, aria-describedby, aria-controls, aria-owns. Use these to connect related content that is not adjacent in the DOM.aria-label to override visible text. This creates a mismatch where screen reader users hear one thing while sighted users see another, violating WCAG 2.5.3 (Label in Name).role="status" or aria-live="polite". Content injected into these regions is announced by screen readers without interrupting the user.aria-live="assertive" strictly for critical interruptions: form validation errors, time-sensitive alerts, or session expiration warnings. Overusing assertive announcements degrades the experience.EVERY interactive element must be reachable and operable via keyboard alone. This is not optional — it is a Level A requirement.
tabindex hacks to reorder focus.tabindex="0" adds an element to the natural tab order. Use this on custom interactive elements that are not natively focusable.tabindex="-1" makes an element programmatically focusable (via element.focus()) but removes it from the tab order. Use this for focus management targets like headings or containers that receive focus after navigation.tabindex greater than 0. It overrides the natural tab order and creates unpredictable navigation that confuses all keyboard users.<dialog> element whenever possible. It provides focus trapping, Escape to close, and aria-modal behavior automatically.aria-modal="true" on custom dialog elements to signal to assistive technology that content behind the dialog is inert.outline: none without providing a custom focus style that meets contrast requirements.:focus-visible (not :focus) for custom focus styles. This shows outlines only for keyboard users while hiding them for mouse clicks, providing the best experience for both input methods.<ClientRouter /> (view transitions), focus management can break on page navigation. The browser may leave focus in a stale position or reset it to the document body.astro:page-load event to programmatically manage focus after transitions complete.All interactive click/tap targets must be at least 24x24 CSS pixels (WCAG 2.5.8 Target Size Minimum).
.icon-button {
min-width: 24px;
min-height: 24px;
padding: 4px; /* Increases tap target beyond visual bounds */
}alt text that describes the purpose and context of the image, not just its appearance. Write "Submit order form" not "green button."alt="" (empty string). Do NOT omit the alt attribute entirely — that causes screen readers to announce the file name.aria-describedby pointing to a detailed text description elsewhere on the page.<Image /> component requires the alt attribute — this is enforced at build time. Treat build errors about missing alt as accessibility defects, not annoyances.<label> element. Use the for attribute on the label matching the id on the input. Placeholder text is NOT a substitute for a label.<fieldset> and <legend>. The legend provides context that individual labels cannot.aria-describedby on the input pointing to the error message element's id. Screen readers announce the error when the user focuses the input.required attribute AND a visible indicator (asterisk, "(required)" text). Do not rely on color alone to indicate required status.aria-live="polite" region so screen readers announce validation results without requiring the user to navigate to the error.client:* directives render as static HTML. They are inherently accessible because they require no JavaScript to present their content.client:only="react|vue|svelte" components have NO server-rendered HTML. Screen readers see nothing until JavaScript loads and executes. This creates a blank gap in the accessibility tree during loading.client:load, client:visible, client:idle) over client:only. Server rendering ensures content is available immediately in the accessibility tree.h1 to h2 to h3, never skipping from h1 to h3).@axe-core/playwright for end-to-end accessibility testing against your built pages.Before shipping any page or component, verify all of the following:
tabindex greater than 0aria-describedbyaria-live regions for screen reader announcements~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.