Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 5 additions & 10 deletions website/styles/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
}

/*
* Contrast (#590). Ratios are WCAG 2.x, computed from the declared colours
* Contrast. Ratios are WCAG 2.x, computed from the declared colours
* composited over the theme backgrounds (#fff / #121212 unless noted); text
* below 18.66 px bold needs 4.5:1.
*
Expand All @@ -61,9 +61,7 @@
*
* Code blocks use Shiki's `css-variables` theme, so token colours are these
* variables (`styles/vars/shiki-vars.css`, declared through `:where()`).
* The light string token measured 3.04:1; the light comment (2.06:1) and
* parameter (2.30:1) tokens and the dark comment token (3.84:1) fail the same
* way and are corrected alongside it. Each pick keeps the original hue.
* Each pick keeps the original hue.
*/
:root:not(.rp-dark) {
/* #3c3c3c at 75 %: 5.18:1 on #fff, 5.01:1 on --rp-c-bg-soft, 4.86:1 on --rp-c-bg-mute. */
Expand Down Expand Up @@ -94,10 +92,8 @@
}

/*
* Keyboard access (#590). The appearance switch (theme/index.tsx) is now a
* tab stop; its focus ring is declared here so the two custom controls (this
* switch and the skip link) share one visible style instead of the UA default.
* Ring is --rp-c-brand on --rp-c-bg: 4.83:1 light, 10.06:1 dark.
* Appearance-switch focus ring, shared with the skip link instead of the UA
* default. Ring is --rp-c-brand on --rp-c-bg: 4.83:1 light, 10.06:1 dark.
*/
.rp-switch-appearance:focus-visible {
border-radius: 4px;
Expand All @@ -111,8 +107,7 @@
}

/*
* Skip link (theme/index.tsx `SkipLink`): the first focusable element on the
* page, clipped until it receives focus, then pinned over the sticky nav.
* Skip link: clipped until focused, then pinned over the sticky nav.
* Text is --rp-c-brand on --rp-c-bg: 4.83:1 light, 10.06:1 dark.
*/
.ab-skip-link {
Expand Down
32 changes: 14 additions & 18 deletions website/theme/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ const HomeBody = () => (
</section>
);

/** Focus target of the skip link; placed as the first child of `<main>`. */
const contentId = 'ab-content';

/**
Expand Down Expand Up @@ -150,20 +149,18 @@ const Layout = () => (
* The default `SwitchAppearance` is a click-only `<div>`: no role, no name,
* not in the tab order. `Nav`, `NavScreen` and `NavHamburger` import it from
* `@rspress/core/theme`, so this named export replaces it site-wide. It stays
* a `<div>` — given `role="button"`, a translated name, a tab stop and
* Enter/Space handling — instead of becoming a `<button>`, because
* `NavHamburger` (rendered on every page, shown at widths ≤ 1280 px) mounts
* the switch inside its own `<button>`, and a `<button>` inside a `<button>`
* is invalid HTML that React reports on every render. That nested copy is
* the hamburger's own control, so once mounted the switch checks whether it
* sits inside a `<button>` and, if so, renders as the plain click target the
* default theme used — a focusable `role="button"` inside a button would be
* nested interactive content with undefined keyboard behaviour. The original
* class names are kept so the theme's CSS still applies. `aria-pressed` and
* the nesting check only apply after mount: the SSG HTML is rendered with the
* default theme while the client's first render already knows the stored
* preference, so a state attribute in the initial markup would mismatch on
* hydration. The original's view-transition animation
* a `<div>` instead of becoming a `<button>` because `NavHamburger` (shown at
* widths ≤ 1280 px) mounts the switch inside its own `<button>`, and a
* `<button>` inside a `<button>` is invalid HTML that React reports on every
* render. That nested copy is the hamburger's own control, so once mounted the
* switch checks whether it sits inside a `<button>` and, if so, renders as the
* plain click target the default theme used — a focusable `role="button"`
* inside a button would be nested interactive content with undefined keyboard
* behaviour. The theme's own class names are kept so its CSS still applies.
* `aria-pressed` and the nesting check only apply after mount: the SSG HTML is
* rendered with the default theme while the client's first render already
* knows the stored preference, so a state attribute in the initial markup
* would mismatch on hydration. The original's view-transition animation
* (`themeConfig.enableAppearanceAnimation`, off for this site) is not
* reproduced.
*/
Expand Down Expand Up @@ -212,7 +209,6 @@ const SwitchAppearance = ({ onClick }: { onClick?: () => void }) => {
);
};

/** Accessible name for a social link, from its host: `github.com`. */
const socialLinkName = (href: string): string => {
try {
return new URL(href).hostname.replace(/^www\./, '');
Expand All @@ -227,8 +223,8 @@ const socialLinkName = (href: string): string => {
* `NavScreen` import `SocialLinks` from `@rspress/core/theme`, so this named
* export replaces it. The anchor is created inside the default component and
* its icon comes from a build-time virtual module, so rather than restating
* the component the name is set on the rendered anchors after each render;
* assistive technology reads the live DOM. `github-stars` links already carry
* the component the name is set on the rendered anchors after each render,
* which is what assistive technology reads. `github-stars` links already carry
* their own label and are left alone.
*/
const SocialLinks = (props: { socialLinks?: SocialLink[] }) => {
Expand Down
Loading