diff --git a/src/react/SimulationFilter.tsx b/src/react/SimulationFilter.tsx index 26cffa6..89bf9df 100644 --- a/src/react/SimulationFilter.tsx +++ b/src/react/SimulationFilter.tsx @@ -11,7 +11,7 @@ import { isVisionMode, } from '../core/constants/modes.js'; import { VisionMode, VisionOptions } from '../types/simulationTypes.js'; -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useId, useRef, useState } from 'react'; import { SimulationKey, useTheme } from './ThemeProvider.js'; import { VisionFilterPortal } from './VisionPortal.js'; import { TOOLBAR_POSITION } from '../core/constants/position.js'; @@ -65,6 +65,7 @@ export default function SimulationFilter(props?: SimulationFilterProps) { const [open, setOpen] = useState(false); const { simulationFilter, setSimulationFilter, language } = useTheme(); const initialized = useRef(false); + const optionsId = useId(); if (!visible) return null; if (!allowInProd && !IS_DEV) return null; @@ -169,37 +170,83 @@ export default function SimulationFilter(props?: SimulationFilterProps) {
- - - + - setOpen(!open)} > {SIMULATE_LABEL[language]} - + {open && ( -
+
)} {open && - MODES.map((value) => ( - - ))} + {MODES.map((value) => ( + + ))} +
+ )}
); diff --git a/src/react/ThemeSwitcher.tsx b/src/react/ThemeSwitcher.tsx index d07c5a3..10e76d1 100644 --- a/src/react/ThemeSwitcher.tsx +++ b/src/react/ThemeSwitcher.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useEffect, useRef, useState, useMemo } from 'react'; +import React, { useEffect, useId, useMemo, useRef, useState } from 'react'; import { useTheme, getThemeOptions, type ThemeKey } from './ThemeProvider.js'; import Logo from '../icons/Logo.js'; import US from '../icons/Us.js'; @@ -38,6 +38,7 @@ export function ThemeSwitcher({ options, position }: TThemeSwitcherProps) { ); const [isOpen, setIsOpen] = useState(false); const wrapperRef = useRef(null); + const menuId = useId(); const switcherClass = SWITCHER_POSITION[position ?? 'right-bottom']; const switcherMenuClass = @@ -62,6 +63,15 @@ export function ThemeSwitcher({ options, position }: TThemeSwitcherProps) { setIsOpen((prev) => !prev); }; + const toggleAriaLabel = + language === 'Korean' + ? isOpen + ? '테마 전환 메뉴 닫기' + : '테마 전환 메뉴 열기' + : isOpen + ? 'Close theme switcher menu' + : 'Open theme switcher menu'; + return (
@@ -71,6 +81,8 @@ export function ThemeSwitcher({ options, position }: TThemeSwitcherProps) { type="button" aria-haspopup="menu" aria-expanded={isOpen} + aria-controls={menuId} + aria-label={toggleAriaLabel} onClick={toggle} className={`fixed w-[60px] h-[60px] p-[10px] ${isOpen ? 'bg-[#252525] border-[1px] border-[#8144FF]' : 'bg-[rgba(129,68,255,0.2)]'} rounded-full flex justify-center items-center shadow-[0_0_3px_0_rgba(0,0,0,0.17)] ${switcherClass} @@ -78,7 +90,11 @@ export function ThemeSwitcher({ options, position }: TThemeSwitcherProps) { > {isOpen ? (
- +
) : (
{isOpen ? (
@@ -112,6 +133,7 @@ export function ThemeSwitcher({ options, position }: TThemeSwitcherProps) {