diff --git a/frontend/src/App.css b/frontend/src/App.css index dc85c0f..856c24a 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -2029,6 +2029,72 @@ a.app-footer__repo:hover { line-height: 1.5; } +/* ── Sticky Reading Header ────────────────────────────────────────────────── */ + +.reading-header { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 150; + transform: translateY(-100%); + transition: transform 0.25s ease; + background: var(--bg-card); + border-bottom: 1px solid var(--color-border); + pointer-events: none; +} + +.reading-header--visible { + transform: translateY(0); + pointer-events: auto; +} + +.reading-header__inner { + max-width: 1200px; + margin: 0 auto; + padding: 0.5rem 1.5rem; + display: flex; + align-items: center; + gap: 0.5rem; + min-height: 36px; + overflow: hidden; +} + +.reading-header__title { + font-weight: 600; + font-size: 0.85rem; + color: var(--text-primary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex-shrink: 1; + min-width: 0; +} + +.reading-header__separator { + color: var(--text-muted); + flex-shrink: 0; +} + +.reading-header__section { + font-size: 0.8rem; + color: var(--text-secondary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex-shrink: 1; + min-width: 0; +} + +@media (max-width: 600px) { + .reading-header__inner { + padding: 0.4rem 1rem; + } + .reading-header__section { + display: none; + } +} + /* ── Table of Contents ────────────────────────────────────────────────────── */ .technique-toc { diff --git a/frontend/src/components/ReadingHeader.tsx b/frontend/src/components/ReadingHeader.tsx new file mode 100644 index 0000000..d5649d3 --- /dev/null +++ b/frontend/src/components/ReadingHeader.tsx @@ -0,0 +1,34 @@ +/** + * Sticky reading header that appears when the article H1 scrolls out of view. + * + * Shows the article title and current section name in a thin fixed bar + * at the top of the viewport. Uses CSS transform for slide-in/out animation. + */ + +interface ReadingHeaderProps { + /** Article title */ + title: string; + /** Currently active section heading (from scroll-spy) */ + currentSection: string; + /** Whether the header should be visible (H1 is out of viewport) */ + visible: boolean; +} + +export default function ReadingHeader({ title, currentSection, visible }: ReadingHeaderProps) { + return ( +