@@ -17,6 +17,56 @@ interface Props {
1717
1818const { title, description = ' ' , lang = ' en' , showBreadcrumb = true , availableLanguages = [] } = Astro .props ;
1919const currentPath = Astro .url .pathname ;
20+
21+ const isJa = lang === ' ja' ;
22+ const navLabels = isJa
23+ ? {
24+ product: ' プロダクト' ,
25+ tutorial: ' チュートリアル' ,
26+ reference: ' リファレンス' ,
27+ faq: ' FAQ' ,
28+ docs: ' ドキュメント' ,
29+ contribution: ' コントリビューションガイド' ,
30+ }
31+ : {
32+ product: ' Product' ,
33+ tutorial: ' Tutorials' ,
34+ reference: ' Reference' ,
35+ faq: ' FAQ' ,
36+ docs: ' Documentation' ,
37+ contribution: ' Contribution Guides' ,
38+ };
39+
40+ const navLinks = {
41+ products: [
42+ { label: ' Vivliostyle Viewer' , href: ` /${lang }/viewer/ ` },
43+ { label: ' Vivliostyle CLI' , href: ` /${lang }/cli/ ` },
44+ { label: ' VFM' , href: ` /${lang }/vfm/ ` },
45+ { label: ' Vivliostyle Themes' , href: ` /${lang }/themes/ ` },
46+ ],
47+ tutorial: {
48+ label: navLabels .tutorial ,
49+ href: isJa ? ' https://vivliostyle.org/ja/tutorials/' : ' https://vivliostyle.org/en/tutorials/' ,
50+ external: true ,
51+ },
52+ reference: {
53+ docs: [
54+ { label: isJa ? ' サポートする CSS 機能' : ' Supported CSS Features' , href: ` /${lang }/reference/supported-css-features/ ` },
55+ { label: isJa ? ' Core API リファレンス' : ' Core API Reference' , href: ` /${lang }/reference/api/ ` },
56+ ],
57+ contribution: [
58+ { label: ' Vivliostyle.js' , href: ` /${lang }/reference/contribution-guide/ ` },
59+ { label: ' Vivliostyle CLI' , href: ` /${lang }/reference/contributing-cli/ ` },
60+ { label: ' VFM' , href: ` /${lang }/reference/contributing-vfm/ ` },
61+ { label: ' Vivliostyle Themes' , href: ` /${lang }/reference/contributing-themes/ ` },
62+ ],
63+ },
64+ faq: {
65+ label: navLabels .faq ,
66+ href: isJa ? ' https://vivliostyle.org/ja/faq/' : ' https://vivliostyle.org/en/faq/' ,
67+ external: true ,
68+ },
69+ };
2070---
2171<!DOCTYPE html >
2272<html lang ={ lang } >
@@ -36,6 +86,12 @@ const currentPath = Astro.url.pathname;
3686 ))}
3787 <link rel =" sitemap" href =" /sitemap-index.xml" >
3888 <link rel =" stylesheet" href =" /styles/global.css" >
89+ { isJa && (
90+ <link
91+ rel = " stylesheet"
92+ href = " https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700&display=swap"
93+ />
94+ )}
3995 <!-- Prism.js syntax highlighting -->
4096 <link rel =" stylesheet" href =" https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css" media =" (prefers-color-scheme: light)" >
4197 <link rel =" stylesheet" href =" https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" media =" (prefers-color-scheme: dark)" >
@@ -79,20 +135,56 @@ const currentPath = Astro.url.pathname;
79135 <a href ={ ` /${lang }/ ` } class =" logo" >
80136 <img src =" /vivliostyle-docs.svg" alt =" Vivliostyle Docs" class =" logo-image" />
81137 </a >
82- <nav class =" header-nav" >
83- <a href ={ ` /${lang }/viewer/ ` } >Viewer</a >
84- <a href ={ ` /${lang }/cli/ ` } >CLI</a >
85- <a href ={ ` /${lang }/vfm/ ` } >VFM</a >
86- <a href ={ ` /${lang }/themes/ ` } >Themes</a >
87- <a href ={ ` /${lang }/reference/ ` } >Reference</a >
88- <a href =" https://github.com/vivliostyle" class =" github-link" aria-label =" GitHub" target =" _blank" rel =" noopener noreferrer" >
89- <svg viewBox =" 0 0 16 16" width =" 24" height =" 24" fill =" currentColor" >
90- <path d =" M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" ></path >
91- </svg >
138+ <nav class =" header-nav" aria-label ={ isJa ? ' グローバルナビゲーション' : ' Global navigation' } >
139+ <details class =" nav-accordion" >
140+ <summary >{ navLabels .product } </summary >
141+ <div class =" nav-panel" >
142+ { navLinks .products .map ((item ) => (
143+ <a href = { item .href } >{ item .label } </a >
144+ ))}
145+ </div >
146+ </details >
147+ <a
148+ href ={ navLinks .tutorial .href }
149+ target ={ navLinks .tutorial .external ? ' _blank' : undefined }
150+ rel ={ navLinks .tutorial .external ? ' noopener noreferrer' : undefined }
151+ >
152+ { navLinks .tutorial .label }
92153 </a >
93- <a href ={ lang === ' ja' ? ' https://vivliostyle.org/ja/' : ' https://vivliostyle.org/' } class =" vivliostyle-link" aria-label =" Vivliostyle" target =" _blank" rel =" noopener noreferrer" >
94- <img src =" /vivliostyle-logo72.png" alt =" Vivliostyle" class =" vivliostyle-icon" />
154+ <details class =" nav-accordion" >
155+ <summary >{ navLabels .reference } </summary >
156+ <div class =" nav-panel" >
157+ <div class =" nav-group" >
158+ <h3 class =" nav-group-title" >{ navLabels .docs } </h3 >
159+ { navLinks .reference .docs .map ((item ) => (
160+ <a href = { item .href } >{ item .label } </a >
161+ ))}
162+ </div >
163+ <div class =" nav-group" >
164+ <h3 class =" nav-group-title" >{ navLabels .contribution } </h3 >
165+ { navLinks .reference .contribution .map ((item ) => (
166+ <a href = { item .href } >{ item .label } </a >
167+ ))}
168+ </div >
169+ </div >
170+ </details >
171+ <a
172+ href ={ navLinks .faq .href }
173+ target ={ navLinks .faq .external ? ' _blank' : undefined }
174+ rel ={ navLinks .faq .external ? ' noopener noreferrer' : undefined }
175+ >
176+ { navLinks .faq .label }
95177 </a >
178+ <div class =" nav-icon-links" >
179+ <a href =" https://github.com/vivliostyle" class =" github-link" aria-label =" GitHub" target =" _blank" rel =" noopener noreferrer" >
180+ <svg viewBox =" 0 0 16 16" width =" 24" height =" 24" fill =" currentColor" >
181+ <path d =" M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" ></path >
182+ </svg >
183+ </a >
184+ <a href ={ lang === ' ja' ? ' https://vivliostyle.org/ja/' : ' https://vivliostyle.org/' } class =" vivliostyle-link" aria-label =" Vivliostyle" target =" _blank" rel =" noopener noreferrer" >
185+ <img src =" /vivliostyle-logo72.png" alt =" Vivliostyle" class =" vivliostyle-icon" />
186+ </a >
187+ </div >
96188 </nav >
97189 <div class =" header-actions" >
98190 <SearchBox lang ={ lang as ' en' | ' ja' } />
@@ -192,20 +284,105 @@ const currentPath = Astro.url.pathname;
192284
193285 .header-nav {
194286 display: flex;
195- gap: 1.5rem ;
287+ gap: 1rem ;
196288 flex: 1;
197289 align-items: center;
290+ min-width: 0;
198291 }
199-
292+
200293 .header-nav a {
201294 color: var(--color-text);
202295 text-decoration: none;
203296 font-weight: 500;
297+ font-size: 0.95rem;
204298 }
205-
206- .header-nav a:hover {
299+
300+ .header-nav summary {
301+ list-style: none;
302+ cursor: pointer;
303+ display: flex;
304+ align-items: center;
305+ gap: 0.35rem;
306+ font-size: 1.1rem;
307+ }
308+
309+ .header-nav summary::-webkit-details-marker {
310+ display: none;
311+ }
312+
313+ .header-nav summary::after {
314+ content: '';
315+ width: 0;
316+ height: 0;
317+ border-left: 4px solid transparent;
318+ border-right: 4px solid transparent;
319+ border-top: 5px solid currentColor;
320+ opacity: 0.6;
321+ transition: transform 0.2s ease;
322+ }
323+
324+ .header-nav a:hover,
325+ .header-nav summary:hover {
326+ color: var(--color-primary);
327+ }
328+
329+ .nav-accordion {
330+ position: relative;
331+ }
332+
333+ .nav-accordion[open] summary::after {
334+ transform: rotate(180deg);
335+ }
336+
337+ .nav-panel {
338+ position: absolute;
339+ top: calc(100% + 0.5rem);
340+ left: 0;
341+ background: var(--color-bg);
342+ border: 1px solid var(--color-border);
343+ border-radius: 8px;
344+ padding: 0.75rem;
345+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
346+ display: none;
347+ min-width: 240px;
348+ z-index: 1200;
349+ }
350+
351+ .nav-accordion[open] .nav-panel {
352+ display: grid;
353+ gap: 0.75rem;
354+ }
355+
356+ .nav-group {
357+ display: grid;
358+ gap: 0.4rem;
359+ }
360+
361+ .nav-group-title {
362+ font-size: 1rem;
363+ font-weight: 600;
364+ color: var(--color-text-muted);
365+ letter-spacing: 0.02em;
366+ margin: 0;
367+ }
368+
369+ .nav-panel a {
370+ font-weight: 500;
371+ font-size: 0.9rem;
372+ color: var(--color-text);
373+ padding-left: 1rem;
374+ }
375+
376+ .nav-panel a:hover {
207377 color: var(--color-primary);
208378 }
379+
380+ .nav-icon-links {
381+ margin-left: auto;
382+ display: flex;
383+ align-items: center;
384+ gap: 0.5rem;
385+ }
209386
210387 .header-nav .github-link,
211388 .header-nav .vivliostyle-link {
@@ -484,6 +661,17 @@ const currentPath = Astro.url.pathname;
484661
485662 .header-nav {
486663 gap: 0.5rem;
664+ }
665+
666+ .header-nav a {
667+ font-size: 0.9rem;
668+ }
669+
670+ .header-nav summary {
671+ font-size: 1rem;
672+ }
673+
674+ .nav-panel a {
487675 font-size: 0.85rem;
488676 }
489677
@@ -682,11 +870,60 @@ const currentPath = Astro.url.pathname;
682870 }, 250);
683871 });
684872 };
873+
874+ // ヘッダーナビのアコーディオン制御
875+ const initNavAccordions = () => {
876+ const accordions = Array.from(document.querySelectorAll('.nav-accordion')) as HTMLDetailsElement[];
877+ if (!accordions.length) {
878+ return;
879+ }
880+
881+ const closeAll = () => {
882+ accordions.forEach((accordion) => {
883+ if (accordion.open) {
884+ accordion.removeAttribute('open');
885+ }
886+ });
887+ };
888+
889+ accordions.forEach((accordion) => {
890+ accordion.addEventListener('toggle', () => {
891+ if (!accordion.open) {
892+ return;
893+ }
894+ accordions.forEach((other) => {
895+ if (other !== accordion) {
896+ other.removeAttribute('open');
897+ }
898+ });
899+ });
900+ });
901+
902+ document.addEventListener('click', (event) => {
903+ const target = event.target;
904+ if (!(target instanceof Node)) {
905+ return;
906+ }
907+ if (!target.closest('.nav-accordion')) {
908+ closeAll();
909+ }
910+ });
911+
912+ document.addEventListener('keydown', (event) => {
913+ if (event.key === 'Escape') {
914+ closeAll();
915+ }
916+ });
917+ };
685918
686919 // DOMContentLoaded時に初期化
687920 if (document.readyState === 'loading') {
688- document.addEventListener('DOMContentLoaded', initMobileMenu);
921+ document.addEventListener('DOMContentLoaded', () => {
922+ initMobileMenu();
923+ initNavAccordions();
924+ });
689925 } else {
690926 initMobileMenu();
927+ initNavAccordions();
691928 }
692929</script >
0 commit comments