From 0293ad9e47f432e69852af2ee62cb819309db1cf Mon Sep 17 00:00:00 2001 From: shubham oulkar <91728992+ShubhamOulkar@users.noreply.github.com> Date: Wed, 13 Aug 2025 05:07:28 +0530 Subject: [PATCH] fix(a11y): add correct `aria-label` on `ThemeToggle` button (#8045) * fix aria-label a11y * fix test * add a11y test for aria-label * fix i18n aria-label text * fix hydration * refactor test * fix test * Exclude ThemeToggle btn from SSR --- apps/site/components/withNavBar.tsx | 18 +++++++++-- apps/site/tests/e2e/general-behavior.spec.ts | 31 ++++++++++++++----- packages/i18n/src/locales/en.json | 3 +- .../src/Containers/NavBar/index.module.css | 6 ++++ 4 files changed, 47 insertions(+), 11 deletions(-) diff --git a/apps/site/components/withNavBar.tsx b/apps/site/components/withNavBar.tsx index 8cab6b824c919..24ca3d9d794f2 100644 --- a/apps/site/components/withNavBar.tsx +++ b/apps/site/components/withNavBar.tsx @@ -2,7 +2,6 @@ import LanguageDropdown from '@node-core/ui-components/Common/LanguageDropDown'; import Skeleton from '@node-core/ui-components/Common/Skeleton'; -import ThemeToggle from '@node-core/ui-components/Common/ThemeToggle'; import NavBar from '@node-core/ui-components/Containers/NavBar'; // TODO(@AvivKeller): I don't like that we are importing styles from another module import styles from '@node-core/ui-components/Containers/NavBar/index.module.css'; @@ -27,6 +26,16 @@ const SearchButton = dynamic(() => import('#site/components/Common/Search'), { ), }); +const ThemeToggle = dynamic( + () => import('@node-core/ui-components/Common/ThemeToggle'), + { + ssr: false, + loading: () => ( + + ), + } +); + const WithNavBar: FC = () => { const { navigationItems } = useSiteNavigation(); const { resolvedTheme, setTheme } = useTheme(); @@ -39,6 +48,11 @@ const WithNavBar: FC = () => { const toggleCurrentTheme = () => setTheme(resolvedTheme === 'dark' ? 'light' : 'dark'); + const themeToggleAriaLabel = + resolvedTheme === 'dark' + ? t('components.common.themeToggle.light') + : t('components.common.themeToggle.dark'); + const changeLanguage = (locale: SimpleLocaleConfig) => replace(pathname!, { locale: locale.code }); @@ -63,7 +77,7 @@ const WithNavBar: FC = () => { - page.evaluate(() => document.documentElement.dataset.theme); + page.evaluate( + () => document.documentElement.dataset.theme as 'light' | 'dark' + ); + +const getCurrentAriaLabel = (theme: string) => + theme === 'dark' + ? locators.themeToggleAriaLabels.light + : locators.themeToggleAriaLabels.dark; const openLanguageMenu = async (page: Page) => { const button = page.getByRole('button', { @@ -65,21 +74,27 @@ test.describe('Node.js Website', () => { test.describe('Theme', () => { test('should toggle between light/dark themes', async ({ page }) => { const themeToggle = page.getByRole('button', { - name: locators.themeToggleName, + name: /Switch to (Light|Dark) Mode/i, }); - await expect(themeToggle).toBeVisible(); const initialTheme = await getTheme(page); + const initialAriaLabel = getCurrentAriaLabel(initialTheme); + let currentAriaLabel = await themeToggle.getAttribute('aria-label'); + expect(currentAriaLabel).toBe(initialAriaLabel); + await themeToggle.click(); const newTheme = await getTheme(page); - expect(newTheme).not.toEqual(initialTheme); - expect(['light', 'dark']).toContain(newTheme); + const newAriaLabel = getCurrentAriaLabel(newTheme); + currentAriaLabel = await themeToggle.getAttribute('aria-label'); + + expect(newTheme).not.toBe(initialTheme); + expect(currentAriaLabel).toBe(newAriaLabel); }); test('should persist theme across page navigation', async ({ page }) => { const themeToggle = page.getByRole('button', { - name: locators.themeToggleName, + name: /Switch to (Light|Dark) Mode/i, }); await themeToggle.click(); const selectedTheme = await getTheme(page); diff --git a/packages/i18n/src/locales/en.json b/packages/i18n/src/locales/en.json index cadcca42859f3..5465c88e9882e 100644 --- a/packages/i18n/src/locales/en.json +++ b/packages/i18n/src/locales/en.json @@ -229,7 +229,8 @@ "label": "Choose Language" }, "themeToggle": { - "label": "Toggle Dark Mode" + "light": "Switch to Light Mode", + "dark": "Switch to Dark Mode" } }, "metabar": { diff --git a/packages/ui-components/src/Containers/NavBar/index.module.css b/packages/ui-components/src/Containers/NavBar/index.module.css index 331f63f46d83c..764c0edd5ec7a 100644 --- a/packages/ui-components/src/Containers/NavBar/index.module.css +++ b/packages/ui-components/src/Containers/NavBar/index.module.css @@ -108,6 +108,12 @@ span.searchButtonSkeleton { } } +span.themeToggleSkeleton { + @apply size-9 + rounded-md + py-4; +} + .ghIconWrapper { @apply size-9 rounded-md