본문/사이드바 헤더 보조 텍스트 토큰 통일.
Collapsible subtitle 이 text-[10px] 였는데, 같은 페이지 좌우(좌: 본문 카드의
CardHeader, 우: 사이드바의 Collapsible) 에서 헤더 우측 metadata 슬롯이 1px 다른
크기로 노출되고 있었음. 두 슬롯은 시각적으로 같은 역할(title 옆 보조 정보) 인데
토큰이 분기돼 있어 미세한 시각 점프 발생.
해결: Collapsible subtitle 을 text-[11px] 로 bump — CardHeader subtitle 과 동일
토큰. 사이드바는 280px 로 좁지만 기존 truncate 안전망(min-w-0 truncate) 이 그대로
overflow 를 흡수하므로 1px 키워도 wrap/clip 위험 없음.
유지: 헤더 chevron(▶) 은 text-[10px] 그대로 — 순수 장식 글리프라 subtitle 텍스트
크기와 별개 토큰. CardHeader 측은 chevron 자체가 없어 비교 대상이 아님.
다른 text-[10px] 사용처는 의도된 토큰으로 유지:
- StockChart 툴바 버튼/SMA preset/수평선 chips — 액션 컨트롤군 자체 토큰.
- AlertsPanel 발화 뱃지/조용히 버튼 — 보조 액션 토큰.
- InvestorCumulative window 버튼 그룹 — aria-pressed 컨트롤 토큰.
- ShortcutsHelp kbd — 키 글리프 토큰.
- CompositeScoreCard "/ 100" / SubScore hint — footnote 캡션 토큰.
검증: tsc/next lint 통과.
Collapsible 헤더 button 의 hover bg 를 zinc-900/60 → zinc-800/50 으로 교체.
문제: hover 톤이 부모의 bg 불투명도에 의존해서 방향이 뒤집힘.
- 비-sticky 부모 bg-zinc-900/40 + hover bg-zinc-900/60 → hover 영역이 더 불투명
(어두워짐) = highlight. OK.
- OrderbookPanel sticky 부모 bg-zinc-900/80 + hover bg-zinc-900/60 → hover 영역이
덜 불투명(더 옅어짐) = hover 가 "뒤로 가는" 인상. 톤 역전.
원인: 둘 다 같은 zinc-900 색이라 hover 가 불투명도 차이만으로 highlight 를
표시. 부모 opacity 가 hover opacity 보다 높으면 신호가 뒤집힘.
해결: hover 를 zinc-800 (zinc-900 보다 한 단계 밝은 색) 으로 옮김. 색 자체가
밝아 /50 opacity 라도 두 부모 케이스 모두에서 highlight 방향이 유지됨. 사이드바
list-row hover 톤(RelatedStocks bg-zinc-800/40 · PeerList bg-zinc-800/50)과도
통일 — 클릭 가능 요소의 hover 톤이 앱 전반에서 동일 색족.
검증: tsc/next lint 통과.
리뷰어 b46f6f1 non-blocking 노트 반영. 기존 subtitle 은 shrink-0 이라
값이 길어지면 헤더 폭을 넘기며 title 을 두 줄로 밀어낼 수 있었음.
실제 트리거 예: PriceTargets 의 "목표 1,234,567 · 손절 987,654"
같은 큰 주가 범위.
변경:
- subtitle: shrink-0 제거 + min-w-0 truncate → 폭 부족 시 말줄임표.
- title flex span: min-w-0 추가 → flex shrink 계산이 양쪽 모두에서
정상 동작하도록 (min-w-0 없으면 자식 텍스트의 min-content 가 부모
shrink 를 막아 truncate 가 발동 안 됨).
title 텍스트 자체는 늘 짧음(호가/내 목표가/관련 종목/투자 메모/가격
알람) — title 쪽 truncate 는 불필요, min-w-0 만으로 충분.
검증:
- npx tsc --noEmit clean
- npx next lint "✔ No ESLint warnings or errors"
Three lower-sidebar panels are now wrapped in a shared Collapsible so
mobile readers can fold away sections they're not actively using.
New shared pieces:
- lib/collapsible.ts — readOpen/writeOpen helpers backed by
localStorage under 'panel-open:<name>'. Per-panel-name (not
per-code) because the preference is a panel-level habit; users
don't want different fold state for every symbol.
- components/Collapsible.tsx — button-as-header + chevron + body.
aria-expanded for AT. Body uses 'hidden' instead of conditional
unmount so textarea text, partial alert form input, and
RelatedStocks fetch results all survive collapse cycles.
SSR-safe: useState(defaultOpen) then useEffect-sync to avoid React
hydration mismatch when localStorage diverges from default.
Panel refactors:
- RelatedStocks: outer rounded div + header replaced with Collapsible;
loading/error/empty/data states all rendered inside body (header
stays visible so user can still toggle/recognize the section even
while loading).
- InvestmentNote: same swap. saved.updatedAt drives the subtitle.
- AlertsPanel: same swap. current price drives the subtitle.
Defaults are open=true everywhere; the surface area is the toggle
itself, not a forced collapse. User's first interaction sets the
sticky preference.