feat(symbol-page): aria-pressed PeriodTabs + sticky orderbook in 1D mode
PeriodTabs (reviewer 488fb32 concern):
- Drop role=tablist/tab/aria-selected. These imply a full ARIA tab
pattern (tabpanel, aria-controls, roving tabIndex, internal Arrow
focus traversal) which we don't implement — page-level left/right keys
drive period change instead. Switch to plain button group with
aria-pressed={active}, which accurately describes a segmented
toggle. Comment updated to match.
OrderbookPanel sticky (new slice):
- Add optional sticky prop. When true, panel uses 'sticky top-4' with
a slightly heavier bg-zinc-900/80 + backdrop-blur so it stays
legible over the panels scrolling behind it.
- page.tsx passes sticky={isIntraday} — only 1D (10m bars) mode
benefits, since orderbook timeliness drops on daily+ charts and the
fixed slot would harm sidebar layout stability there.
This commit is contained in:
@@ -253,7 +253,7 @@ export default function CodePage({ params }: { params: { code: string } }) {
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<SymbolSidebar code={code} />
|
||||
<OrderbookPanel code={code} />
|
||||
<OrderbookPanel code={code} sticky={isIntraday} />
|
||||
<PriceTargets code={code} current={current} onChange={setTargets} />
|
||||
<AlertsPanel code={code} name={chart?.name} current={current} />
|
||||
<InvestmentNote code={code} />
|
||||
|
||||
@@ -16,11 +16,16 @@ import { api, type OrderbookLevel, type OrderbookResponse } from "../lib/api";
|
||||
|
||||
type Props = {
|
||||
code: string;
|
||||
// 1D(10분봉) 모드일 때만 sticky 로 띄움 — 종목 페이지가 길어 스크롤해도 매수/매도 호가가
|
||||
// 시야에 남도록. 일봉 이상에선 호가의 실시간 가치가 낮아 일반 흐름으로 두는 게 사이드바
|
||||
// 다른 패널(PriceTargets/AlertsPanel/InvestmentNote/RelatedStocks) 의 위치 안정성을 해치지
|
||||
// 않음.
|
||||
sticky?: boolean;
|
||||
};
|
||||
|
||||
const POLL_MS = 10_000;
|
||||
|
||||
export function OrderbookPanel({ code }: Props) {
|
||||
export function OrderbookPanel({ code, sticky = false }: Props) {
|
||||
const [data, setData] = useState<OrderbookResponse | null>(null);
|
||||
const [err, setErr] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -51,7 +56,13 @@ export function OrderbookPanel({ code }: Props) {
|
||||
}, [code]);
|
||||
|
||||
return (
|
||||
<div className="rounded-md border border-zinc-800 bg-zinc-900/40 p-3">
|
||||
<div
|
||||
className={
|
||||
sticky
|
||||
? "sticky top-4 rounded-md border border-zinc-800 bg-zinc-900/80 p-3 backdrop-blur"
|
||||
: "rounded-md border border-zinc-800 bg-zinc-900/40 p-3"
|
||||
}
|
||||
>
|
||||
<div className="mb-2 flex items-center justify-between gap-2">
|
||||
<span className="text-xs font-medium text-zinc-300">호가</span>
|
||||
<span className="text-[10px] text-zinc-500">
|
||||
|
||||
@@ -43,10 +43,12 @@ export function PeriodTabs({ value, onChange }: Props) {
|
||||
// 페이지에서 "지금 어느 기간이 선택돼 있는지" 한눈에 안 들어왔음.
|
||||
// 한국 컬러 상승 톤(rose-500) 으로 칠해서 zinc 버튼군 사이에서 즉시 식별. 다른 모듈의 활성/positive
|
||||
// 상태(목표가 라인, 등락 양수 등) 도 같은 rose 계열이라 톤 일관성 유지.
|
||||
// aria-pressed: 토글 그룹 의미 명시 — 스크린리더가 선택 상태 읽어줌.
|
||||
// ARIA: 이건 tabpanel 을 가진 진짜 탭이 아니라 차트 기간 segmented control 임.
|
||||
// role=tablist/tab 을 붙이면 스크린리더가 aria-controls·roving tabIndex·Arrow focus 이동 같은
|
||||
// ARIA 탭 패턴을 기대하는데 여기엔 그 인프라가 없음 (←/→ 는 페이지 전역 단축키로 처리). 단순
|
||||
// 버튼 그룹 + aria-pressed 가 정확한 표현.
|
||||
return (
|
||||
<div
|
||||
role="tablist"
|
||||
aria-label="기간 선택"
|
||||
className="inline-flex rounded-full border border-zinc-800 bg-zinc-900 p-1 text-xs"
|
||||
>
|
||||
@@ -56,8 +58,7 @@ export function PeriodTabs({ value, onChange }: Props) {
|
||||
<button
|
||||
key={p.id}
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-selected={active}
|
||||
aria-pressed={active}
|
||||
onClick={() => onChange(p.id, p.interval, p.days)}
|
||||
className={
|
||||
active
|
||||
|
||||
Reference in New Issue
Block a user