Skip to content

Accessibility#

View Markdown

When ssg.a11y is enabled, themed pages get:

  • a Skip to content link as the first focusable control in <body>
  • id="ox-main" on <main> so the skip link has a target
  • print CSS that hides header, sidebar, search, and reader-chrome extras

The feature is off unless you turn it on. Disabled pages emit no skip link and no extra print CSS.

import { oxContent } from "@ox-content/vite-plugin";

export default {
  plugins: [
    oxContent({
      ssg: {
        a11y: true,
      },
    }),
  ],
};

false or omitted keeps the extras off. true enables the defaults. An object enables the feature and can override the skip-link label:

oxContent({
  ssg: {
    a11y: { skipLinkLabel: "Skip to main" },
  },
});
Field Default Effect
skipLinkLabel "Skip to content" Text of the skip link. Escaped in the HTML.

The skip link uses class="ox-skip-link" and href="#ox-main". CSS keeps it visually hidden until it receives keyboard focus, so the first Tab stop is the skip link.

Print output keeps the article and hides site chrome: the header, sidebar, search modal, and reader-chrome controls such as copy and back-to-top.

Bare mode never emits the skip link or the extra print CSS. Hostile labels cannot break out of the link text.

Embed states#

Almost nothing resolves in the reader's browser. Cards, players, and link previews are all built once and shipped as static HTML, so they have no loading state to show — they arrive resolved. When a build-time fetch fails, the card that ships is already the fallback or the error card; the reader never watches it happen.

The exception is islands. A component tag hydrates in the browser, so it is the one embed with states over time:

State Marker Appearance
loading .ox-island-loading spinner, after a short delay so a fast hydrate never flashes one
resolved [data-ox-hydrated] the component itself
error .ox-island-error the static content it was rendered from, marked as not interactive

All three honour prefers-reduced-motion: reduce.

Reduced motion#

Built-in embed and island styles honour prefers-reduced-motion: reduce. The loading spinners on Open Graph cards, GitHub cards, and unhydrated islands stop rotating, the island skeleton stops shimmering, and hover transitions on tabs and the YouTube placeholder are dropped.

Each indicator keeps its shape, so a card that is still loading still looks like it is loading — only the movement goes away. These styles ship with the embeds themselves and need no configuration.

Last updated: