diff --git a/.changeset/clean-flies-start.md b/.changeset/clean-flies-start.md new file mode 100644 index 00000000000..d8eca7d332f --- /dev/null +++ b/.changeset/clean-flies-start.md @@ -0,0 +1,23 @@ +--- +'@astrojs/starlight': minor +--- + +Simplifies markup for Starlight’s mobile menu toggle + +**⚠️ Potentially breaking change:** If you use a theme plugin, custom styles, or component overrides targeting the `MobileMenuToggle` button or `PageFrame` components, you may need to adjust these for the new markup. The button is no longer wrapped in a `` custom element and no longer uses the `aria-expanded` attribute. Instead, you can use the `.sl-menu-button` class name to target the button and the `:popover-open` pseudo-class to style the menu open state specifically. + +In the following example, custom styles for the menu button are updated for the new approach: + +```diff +- starlight-menu-button button { ++ .sl-menu-button { + color: var(--sl-color-text); +} + +- starlight-menu-button[aria-expanded='true'] button { ++ .sl-menu-button:has(~ :popover-open) { + color: var(--sl-color-text-accent-high); +} +``` + +See [`MobileMenuToggle.astro`](https://github.com/withastro/starlight/blob/main/packages/starlight/components/MobileMenuToggle.astro) and [`PageFrame.astro`](https://github.com/withastro/starlight/blob/main/packages/starlight/components/PageFrame.astro) on GitHub for the full source code of the updated components. diff --git a/.changeset/long-jokes-post.md b/.changeset/long-jokes-post.md new file mode 100644 index 00000000000..25c2c654de1 --- /dev/null +++ b/.changeset/long-jokes-post.md @@ -0,0 +1,18 @@ +--- +'@astrojs/starlight': minor +--- + +Refactors Starlight’s mobile menu toggle to work when JavaScript fails or is disabled + +⚠️ **BREAKING CHANGE:** This release drops official support for Chromium-based browsers prior to version 116 (released August 2023), Safari-based browsers prior to version 17.0 (released September 2023), and Firefox prior to version 125 (released April 2024). You can find a list of currently supported browsers and their versions using this [browserslist query](https://browsersl.ist/#q=%3E+0.5%25%2C+not+dead%2C+Chrome+%3E%3D+116%2C+Edge+%3E%3D+116%2C+Firefox+%3E%3D+125%2C+Safari+%3E%3D+17.0%2C+iOS+%3E%3D+17.0%2C+not+op_mini+all). + +This change also removes the `data-mobile-menu-expanded` attribute, which was previously added to `` while the mobile menu is open. If you have custom code that was depending on this attribute, you will need to update it to use a new selector to check if the mobile menu is open. + +In the following example, a custom background colour for the site header while the menu is open is updated for the new approach: + +```diff +- [data-mobile-menu-expanded] header { ++ body:has(sl-sidebar-pane:popover-open) header { + background-color: var(--sl-color-bg); +} +``` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9a36b76c339..96e05a4d4b2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -289,7 +289,7 @@ To add a language, you will need its BCP-47 tag and a label. See [“Adding a ne - Components that require client-side JavaScript or CSS should use JavaScript/CSS features that are well-supported by browsers. - You can find a list of supported browsers and their versions using this [browserslist query](https://browsersl.ist/#q=%3E+0.5%25%2C+not+dead%2C+Chrome+%3E%3D+111%2C+Edge+%3E%3D+111%2C+Firefox+%3E%3D+121%2C+Safari+%3E%3D+16.4%2C+iOS+%3E%3D+16.4%2C+not+op_mini+all). To check whether or not a feature is supported, you can visit the [Can I use](https://caniuse.com) website and search for the feature. + You can find a list of supported browsers and their versions using this [browserslist query](https://browsersl.ist/#q=%3E+0.5%25%2C+not+dead%2C+Chrome+%3E%3D+116%2C+Edge+%3E%3D+116%2C+Firefox+%3E%3D+125%2C+Safari+%3E%3D+17.0%2C+iOS+%3E%3D+17.0%2C+not+op_mini+all). To check whether or not a feature is supported, you can visit the [Can I use](https://caniuse.com) website and search for the feature. [discord]: https://astro.build/chat [issues]: https://github.com/withastro/starlight/issues diff --git a/packages/starlight/__e2e__/basics.test.ts b/packages/starlight/__e2e__/basics.test.ts index 485e2ad5e83..33511edb960 100644 --- a/packages/starlight/__e2e__/basics.test.ts +++ b/packages/starlight/__e2e__/basics.test.ts @@ -779,11 +779,13 @@ test.describe('mobile menu focus trap', () => { await starlight.goto('/headings'); const currentFocus = page.locator('*:focus'); + const mainFrame = page.locator('.main-frame'); // Open the mobile menu. const mobileMenuButton = page.getByRole('button', { name: 'Menu' }); await mobileMenuButton.click(); await expect(currentFocus).toHaveCount(1); + await expect(mainFrame).toHaveAttribute('inert', ''); // Focus the theme selector, which is the last focusable element in the mobile menu. const themeSelector = page.getByRole('navigation', { name: 'Main' }).getByLabel('Select theme'); @@ -801,6 +803,7 @@ test.describe('mobile menu focus trap', () => { await mobileMenuButton.click(); // The focus trap should be released and tabbing will focus the mobile table of contents button. + await expect(mainFrame).not.toHaveAttribute('inert', ''); await page.keyboard.press('Tab'); await expect(currentFocus).toHaveText(/On this page/); }); @@ -811,6 +814,7 @@ test.describe('mobile menu focus trap', () => { await starlight.goto('/anchor-heading'); const currentFocus = page.locator('*:focus'); + const mainFrame = page.locator('.main-frame'); const anchorLinkAccessibleName = 'Section titled “An anchor heading”'; const anchorHeadingLink = page.getByRole('link', { name: anchorLinkAccessibleName }); @@ -822,6 +826,7 @@ test.describe('mobile menu focus trap', () => { const mobileMenuButton = page.getByRole('button', { name: 'Menu' }); await mobileMenuButton.click(); await expect(currentFocus).toHaveAccessibleName('Menu'); + await expect(mainFrame).toHaveAttribute('inert', ''); // Try to focus the anchor heading which should be prevented by the focus trap, // keeping focus where it is. @@ -832,11 +837,14 @@ test.describe('mobile menu focus trap', () => { await page.setViewportSize({ width: 1280, height: 720 }); // The anchor heading link should be focusable again. + await expect(mainFrame).not.toHaveAttribute('inert', ''); await anchorHeadingLink.focus(); await expect(currentFocus).toHaveText(anchorLinkAccessibleName); // Resizing back to a smaller viewport should not re-enable the focus trap. await page.setViewportSize({ width: 375, height: 667 }); + await expect(mainFrame).not.toHaveAttribute('inert', ''); + await expect(page.locator('.sidebar-pane:popover-open')).toHaveCount(0); await expect(currentFocus).toHaveText(anchorLinkAccessibleName); }); }); diff --git a/packages/starlight/src/components/MobileMenuToggle.astro b/packages/starlight/src/components/MobileMenuToggle.astro index 0686d1354d3..9108f64d5db 100644 --- a/packages/starlight/src/components/MobileMenuToggle.astro +++ b/packages/starlight/src/components/MobileMenuToggle.astro @@ -2,60 +2,11 @@ import Icon from '../user-components/Icon.astro'; --- - - - - - +