Skip to content

Accessibility Helpers

Faststrap now includes built-in accessibility primitives in faststrap.accessibility.

Why this exists

  • Reduce repeated a11y boilerplate in every app.
  • Make keyboard and screen-reader support default.
  • Keep APIs simple and composable with existing FastHTML/Faststrap components.

When to use these helpers

  1. Any page with keyboard users in mind.
  2. Dynamic pages that update content after interactions.
  3. Dialog-like UI that must keep tab focus contained.
  4. Icon-only controls that need screen-reader labels.

Components

from faststrap import SkipLink

SkipLink(target="#main-content", text="Skip to content")

Use it near the top of your page so keyboard users can jump to main content immediately.

VisuallyHidden

from faststrap import VisuallyHidden

Button(
    Icon("x"),
    VisuallyHidden("Close dialog"),
)

Hides content visually while keeping it available to assistive technologies.

LiveRegion

from faststrap import LiveRegion

LiveRegion("Profile saved", politeness="polite")

Use for dynamic status updates that should be announced by screen readers.

FocusTrap

from faststrap import FocusTrap

FocusTrap(
    Input(name="email"),
    Button("Save"),
)

Wrap dialog-like content to constrain tab focus inside the container.

Practical pattern for dynamic updates

Div(
    Button("Save", hx_post="/save", hx_target="#status"),
    Div(id="status"),
    LiveRegion(id="a11y-status"),
)

Use route responses to update both visible status and the live region where appropriate.

from faststrap import *

app = FastHTML()
add_bootstrap(app)

@app.get("/")
def home():
    return Container(
        SkipLink("#main-content"),
        Div(
            H1("Dashboard"),
            id="main-content",
        ),
    )

Accessibility notes

  • SkipLink should appear early in the DOM.
  • LiveRegion should be used for meaningful state changes (avoid noisy updates).
  • FocusTrap is best for overlays, dialogs, and modal-like content.

API Reference

Create a skip link for keyboard users.

Source code in src/faststrap/accessibility.py
def SkipLink(
    target: str = "#main-content",
    text: str = "Skip to main content",
    **kwargs: Any,
) -> Any:
    """Create a skip link for keyboard users."""
    from fasthtml.common import A

    user_cls = kwargs.pop("cls", "")
    cls = merge_classes(
        "visually-hidden-focusable position-absolute top-0 start-0 m-3 p-2 bg-body border rounded",
        user_cls,
    )
    attrs = {"href": target, "cls": cls}
    attrs.update(convert_attrs(kwargs))
    return A(text, **attrs)

faststrap.accessibility.VisuallyHidden(*children, focusable=False, **kwargs)

Hide content visually while keeping it available to screen readers.

Source code in src/faststrap/accessibility.py
def VisuallyHidden(
    *children: Any,
    focusable: bool = False,
    **kwargs: Any,
) -> Span:
    """Hide content visually while keeping it available to screen readers."""
    user_cls = kwargs.pop("cls", "")
    base_cls = "visually-hidden-focusable" if focusable else "visually-hidden"
    attrs = {"cls": merge_classes(base_cls, user_cls)}
    attrs.update(convert_attrs(kwargs))
    return Span(*children, **attrs)

faststrap.accessibility.LiveRegion(*children, politeness='polite', atomic=True, relevant='additions text', **kwargs)

Create an ARIA live region for dynamic status messages.

Source code in src/faststrap/accessibility.py
def LiveRegion(
    *children: Any,
    politeness: AriaLiveType = "polite",
    atomic: bool = True,
    relevant: str = "additions text",
    **kwargs: Any,
) -> Div:
    """Create an ARIA live region for dynamic status messages."""
    user_cls = kwargs.pop("cls", "")
    attrs = {
        "cls": merge_classes("visually-hidden", user_cls),
        "aria_live": politeness,
        "aria_atomic": "true" if atomic else "false",
        "aria_relevant": relevant,
        "role": "status" if politeness == "polite" else "alert",
    }
    attrs.update(convert_attrs(kwargs))
    return Div(*children, **attrs)

faststrap.accessibility.FocusTrap(*children, autofocus_selector=None, **kwargs)

Create a focus trap container for dialogs and overlays.

Source code in src/faststrap/accessibility.py
def FocusTrap(
    *children: Any,
    autofocus_selector: str | None = None,
    **kwargs: Any,
) -> Div:
    """Create a focus trap container for dialogs and overlays."""
    user_cls = kwargs.pop("cls", "")
    attrs = {
        "cls": merge_classes(user_cls),
        "data_fs_focus_trap": "true",
        "tabindex": "-1",
    }
    if autofocus_selector:
        attrs["data_fs_autofocus"] = autofocus_selector
    attrs.update(convert_attrs(kwargs))
    return Div(*children, **attrs)