From 67356fda0da5837150f54f21995ee9982042b156 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 12 Jul 2026 21:53:56 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20=EB=82=A0=EC=A7=9C=20?= =?UTF-8?q?=EB=B2=94=EC=9C=84=20=EC=84=A0=ED=83=9D=EA=B8=B0=20=ED=82=A4?= =?UTF-8?q?=EB=B3=B4=EB=93=9C=20=EC=A0=91=EA=B7=BC=EC=84=B1=20=EB=B0=8F=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=9D=B8=EC=A7=80=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DateRangePicker 프리셋 버튼 그룹에 `aria-pressed` 추가 - 탭 이동 시 식별 가능하도록 `focus-visible` 등 포커스 스타일 적용 --- .jules/palette.md | 3 + .../dashboard/date-range-picker.tsx | 79 +++++++++---------- 2 files changed, 42 insertions(+), 40 deletions(-) create mode 100644 .jules/palette.md diff --git a/.jules/palette.md b/.jules/palette.md new file mode 100644 index 00000000..978e92ac --- /dev/null +++ b/.jules/palette.md @@ -0,0 +1,3 @@ +## 2024-07-12 - DateRangePicker 토글 버튼의 키보드 접근성 및 상태 인지 개선 +**Learning:** 여러 선택지 중 하나를 고르는 토글 형태의 버튼 그룹(예: 기간 선택 프리셋)에서는 시각적인 스타일(색상) 변화만으로 상태를 나타내면 스크린 리더 등 보조 기기 사용자가 현재 선택된 값을 인지하기 어렵습니다. 또한 키보드 네비게이션 시 포커스 링(`focus-visible` 클래스)이 빠져 있으면 사용자가 어떤 버튼에 위치해 있는지 시각적으로 확인하기 어렵습니다. +**Action:** 토글 형태의 버튼이나 탭에는 반드시 `aria-pressed={isActive}` 속성을 추가하여 상태를 명확히 전달하고, Tailwind CSS를 사용할 때는 `focus-visible:outline-none focus-visible:ring-2`와 같은 키보드 포커스 스타일을 반드시 포함하여 키보드 접근성을 확보해야 합니다. \ No newline at end of file diff --git a/packages/web/src/components/dashboard/date-range-picker.tsx b/packages/web/src/components/dashboard/date-range-picker.tsx index aef2f7be..e9780693 100644 --- a/packages/web/src/components/dashboard/date-range-picker.tsx +++ b/packages/web/src/components/dashboard/date-range-picker.tsx @@ -1,35 +1,35 @@ -'use client' +"use client"; -import { useSearchParams, useRouter } from 'next/navigation' -import { subDays, format, differenceInDays } from 'date-fns' -import { Suspense } from 'react' -import { cn } from '@/lib/utils' +import { useSearchParams, useRouter } from "next/navigation"; +import { subDays, format, differenceInDays } from "date-fns"; +import { Suspense } from "react"; +import { cn } from "@/lib/utils"; const PRESETS = [ - { days: 7, label: '7d' }, - { days: 30, label: '30d' }, - { days: 90, label: '90d' }, - { days: 3650, label: 'ALL' }, -] as const + { days: 7, label: "7d" }, + { days: 30, label: "30d" }, + { days: 90, label: "90d" }, + { days: 3650, label: "ALL" }, +] as const; function DateRangePickerContent() { - const router = useRouter() - const searchParams = useSearchParams() + const router = useRouter(); + const searchParams = useSearchParams(); - const currentFrom = searchParams.get('from') - const currentTo = searchParams.get('to') + const currentFrom = searchParams.get("from"); + const currentTo = searchParams.get("to"); - const today = new Date() - const sevenDaysAgo = subDays(today, 7) + const today = new Date(); + const sevenDaysAgo = subDays(today, 7); - const defaultFrom = currentFrom || format(sevenDaysAgo, 'yyyy-MM-dd') - const defaultTo = currentTo || format(today, 'yyyy-MM-dd') + const defaultFrom = currentFrom || format(sevenDaysAgo, "yyyy-MM-dd"); + const defaultTo = currentTo || format(today, "yyyy-MM-dd"); - const fromDate = new Date(defaultFrom) - const toDate = new Date(defaultTo) - const daysDiff = differenceInDays(toDate, fromDate) + const fromDate = new Date(defaultFrom); + const toDate = new Date(defaultTo); + const daysDiff = differenceInDays(toDate, fromDate); - const isToday = format(toDate, 'yyyy-MM-dd') === format(today, 'yyyy-MM-dd') + const isToday = format(toDate, "yyyy-MM-dd") === format(today, "yyyy-MM-dd"); const activePreset = isToday ? daysDiff === 6 ? 7 @@ -40,20 +40,20 @@ function DateRangePickerContent() { : daysDiff >= 3649 ? 3650 : null - : null + : null; const handlePreset = (days: number) => { - const to = format(today, 'yyyy-MM-dd') - const from = format(subDays(today, days), 'yyyy-MM-dd') + const to = format(today, "yyyy-MM-dd"); + const from = format(subDays(today, days), "yyyy-MM-dd"); - const newParams = new URLSearchParams(searchParams.toString()) - newParams.set('from', from) - newParams.set('to', to) + const newParams = new URLSearchParams(searchParams.toString()); + newParams.set("from", from); + newParams.set("to", to); // 페이지네이션 사용 중인 화면에서 날짜가 바뀌면 첫 페이지로 리셋 - newParams.delete('page') + newParams.delete("page"); - router.push(`?${newParams.toString()}`) - } + router.push(`?${newParams.toString()}`); + }; return (
@@ -63,11 +63,12 @@ function DateRangePickerContent() { key={preset.days} type="button" onClick={() => handlePreset(preset.days)} + aria-pressed={activePreset === preset.days} className={cn( - 'px-3 py-1 text-xs font-medium rounded-md transition-colors', + "px-3 py-1 text-xs font-medium rounded-md transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background", activePreset === preset.days - ? 'bg-primary text-primary-foreground' - : 'text-muted-foreground hover:text-foreground hover:bg-muted', + ? "bg-primary text-primary-foreground" + : "text-muted-foreground hover:text-foreground hover:bg-muted", )} > {preset.label} @@ -75,20 +76,18 @@ function DateRangePickerContent() { ))}
- {format(fromDate, 'MMM d')} ~ {format(toDate, 'MMM d')} + {format(fromDate, "MMM d")} ~ {format(toDate, "MMM d")} - ) + ); } export function DateRangePicker() { return ( - } + fallback={
} > - ) + ); }