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
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function NotificationBell({ showTooltip = false }: NotificationBellProps) {
onClick={handleChatButtonClick}
className={cn('relative inline-flex', shouldShowTooltip && 'chat-tooltip-anchor')}
>
<ChatCatIcon />
<ChatCatIcon className="drop-shadow-[0_4px_4px_rgba(0,0,0,0.20)]" />
{totalUnreadCount > 0 ? (
<span className="absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-red-500 px-1 text-[10px] text-white">
{totalUnreadCount > 99 ? '99+' : totalUnreadCount}
Expand Down
3 changes: 0 additions & 3 deletions src/pages/Schedule/components/ScheduleDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,6 @@ function ScheduleDetail({ year, month, day, onItemClick }: scheduleDetailProps)

return (
<div ref={containerRef} className="flex flex-1 flex-col gap-2 overflow-y-auto bg-white px-6 pt-4 pb-6">
<span className="pb-1 text-[16px] leading-4 font-semibold">
{month}월 {day}일 일정
</span>
{sortedSchedules.length ? (
sortedSchedules.map(({ title, startedAt, endedAt, scheduleCategory }, index) => {
const highlighted = isOnSelectedDay(startedAt, endedAt);
Expand Down
57 changes: 32 additions & 25 deletions src/pages/Schedule/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useMemo, useRef, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import Portal from '@/components/common/Portal';
import { SCHEDULE_DAYS } from '@/constants/schedule';
import { dateUtils } from '@/utils/hooks/useSchedule';
import CalendarWeekRow from './components/CalendarWeekRow';
Expand All @@ -15,7 +16,9 @@ const COLOR_LEGENDS = [
{ name: '기숙사', color: '#B9ADEF' },
];

const HEADER_HEIGHT = 44;
const PEEK_HEIGHT = 150;
const SHEET_TOP_OFFSET = HEADER_HEIGHT + 200;

function Schedule() {
const [searchParams, setSearchParams] = useSearchParams();
Expand Down Expand Up @@ -84,7 +87,7 @@ function Schedule() {
});

return (
<div className="relative flex h-[calc(100vh-44px)] flex-col overflow-hidden bg-white">
<div className="relative flex h-[calc(var(--viewport-height)-44px)] flex-col overflow-hidden bg-white">
<div className="overflow-y-auto" style={{ maxHeight: `calc(100% - ${PEEK_HEIGHT}px)` }}>
<main
className="flex w-full shrink-0 touch-pan-y flex-col bg-white pt-[23px]"
Expand Down Expand Up @@ -124,30 +127,34 @@ function Schedule() {
</ul>
</div>

<div
className={`absolute inset-0 z-9 bg-black/40 transition-opacity duration-300 ${isSheetExpanded ? 'pointer-events-auto opacity-100' : 'pointer-events-none opacity-0'}`}
onClick={() => setIsSheetExpanded(false)}
/>

<section
className="absolute inset-x-0 bottom-0 z-10 flex flex-col rounded-t-3xl bg-white shadow-[0_-4px_12px_rgba(0,0,0,0.06)] transition-transform duration-300 ease-out"
style={{
height: `calc(100% - 200px)`,
transform: isSheetExpanded ? 'translateY(0)' : `translateY(calc(100% - ${PEEK_HEIGHT}px))`,
}}
>
<div
className="flex shrink-0 justify-center pt-3 pb-2"
onTouchStart={handleSheetTouchStart}
onTouchEnd={handleSheetTouchEnd}
>
<div className="h-1 w-8 rounded-full bg-[#D1D5DB]" />
</div>

<div className="flex flex-1 flex-col overflow-hidden">
<ScheduleDetail year={year} month={month} day={day} onItemClick={() => setIsSheetExpanded(true)} />
</div>
</section>
<Portal>
<>
<div
className={`fixed inset-0 z-[31] bg-black/40 transition-opacity duration-300 ${isSheetExpanded ? 'pointer-events-auto opacity-100' : 'pointer-events-none opacity-0'}`}
onClick={() => setIsSheetExpanded(false)}
/>

<section
className="fixed inset-x-0 bottom-0 z-[32] flex flex-col rounded-t-3xl bg-white shadow-[0_-4px_12px_rgba(0,0,0,0.06)] transition-transform duration-300 ease-out"
style={{
height: `calc(var(--viewport-height) - ${SHEET_TOP_OFFSET}px)`,
transform: isSheetExpanded ? 'translateY(0)' : `translateY(calc(100% - ${PEEK_HEIGHT}px))`,
}}
>
<div
className="flex shrink-0 justify-center pt-3 pb-2"
onTouchStart={handleSheetTouchStart}
onTouchEnd={handleSheetTouchEnd}
>
<div className="h-1 w-8 rounded-full bg-[#D1D5DB]" />
</div>

<div className="flex flex-1 flex-col overflow-hidden">
<ScheduleDetail year={year} month={month} day={day} onItemClick={() => setIsSheetExpanded(true)} />
</div>
</section>
Comment on lines +137 to +155
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

포털 바텀시트에 모달 접근성 처리가 빠져 있습니다.

이제 UI가 사실상 모달인데 role="dialog"/aria-modal이 없고, 포커스 진입·복귀나 Escape 닫기도 없습니다. 현재 상태면 키보드/스크린리더에서 배경으로 포커스가 빠질 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/Schedule/index.tsx` around lines 137 - 155, The bottom sheet lacks
modal accessibility: add role="dialog" and aria-modal="true" to the section
rendered by Schedule (the sheet using isSheetExpanded), implement focus
management by saving document.activeElement before opening, moving focus into a
tabbable element inside the sheet (use a ref on the sheet container or first
focusable child) when isSheetExpanded becomes true, restore focus to the saved
element on close (setIsSheetExpanded false), and add an Escape key handler (tied
to the same logic that handleSheetTouchEnd/setIsSheetExpanded) to close the
sheet; also ensure background content is hidden from assistive tech (e.g., set
aria-hidden on the main page container while the sheet is open) and consider
using a small focus trap implemented in the Schedule component to keep tab focus
cycling inside the ScheduleDetail content.

</>
</Portal>
</div>
);
}
Expand Down
Loading