diff --git a/astro.config.mjs b/astro.config.mjs index 5931855..dbe7e04 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,6 +1,8 @@ import { defineConfig } from 'astro/config'; import starlight from '@astrojs/starlight'; import starlightLinksValidator from 'starlight-links-validator'; +import starlightSidebarTopics from 'starlight-sidebar-topics'; +import { sidebarTopics } from './src/config/sidebarTopics'; import remarkGlossary from './src/plugins/remark-glossary'; import remarkCenter from './src/plugins/remark-center'; import remarkFigure from './src/plugins/remark-figure'; @@ -70,9 +72,13 @@ export default defineConfig({ // TOC is disabled globally but can be enabled per-directory in src/config/tocConfig.ts // or per-page via frontmatter (tableOfContents: true) tableOfContents: { minHeadingLevel: 2, maxHeadingLevel: 3 }, - // Sidebar configuration is now managed in src/config/sidebarConfig.ts - // This allows different sidebars per top-level navigation section - plugins: [starlightLinksValidator()], + plugins: [ + // Separates sidebar into topics that are switchable with a dropdown + starlightSidebarTopics(sidebarTopics, { + exclude: ['/', '/test-content-figure'], + }), + starlightLinksValidator(), + ], }), ], }); diff --git a/package.json b/package.json index 3197978..9d3b807 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,8 @@ "@mdi/js": "^7.4.47", "astro": "^7.1.3", "sharp": "^0.34.5", + "starlight-sidebar-topics": "^0.8.0", + "starlight-sidebar-topics-dropdown": "^0.6.0", "unist-util-visit": "^5.1.0" }, "devDependencies": { @@ -43,9 +45,10 @@ "astro-eslint-parser": "^1.4.0", "eslint": "^10.5.0", "eslint-plugin-astro": "^1.7.0", - "mdast-util-mdx": "^3.0.0", + "globals": "^16.5.0", "husky": "^9.1.7", "lint-staged": "^17.0.8", + "mdast-util-mdx": "^3.0.0", "mdast-util-mdx-jsx": "^3.2.0", "mdx2vast": "^0.3.1", "prettier": "^3.8.4", @@ -55,7 +58,6 @@ "remark-frontmatter": "^5.0.0", "remark-lint-no-dead-urls": "^2.0.1", "remark-mdx": "^3.1.1", - "globals": "^16.5.0", "remark-preset-lint-recommended": "^7.0.1", "starlight-links-validator": "^0.20.0", "tsx": "^4.22.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5ba09bc..b2c9153 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,12 @@ importers: sharp: specifier: ^0.34.5 version: 0.34.5 + starlight-sidebar-topics: + specifier: ^0.8.0 + version: 0.8.0(@astrojs/starlight@0.41.4(@astrojs/markdown-remark@7.2.1)(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(rollup@4.59.0)(tsx@4.22.4)(yaml@2.9.0))(typescript@6.0.3)) + starlight-sidebar-topics-dropdown: + specifier: ^0.6.0 + version: 0.6.0(@astrojs/starlight@0.41.4(@astrojs/markdown-remark@7.2.1)(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(rollup@4.59.0)(tsx@4.22.4)(yaml@2.9.0))(typescript@6.0.3))(starlight-sidebar-topics@0.8.0(@astrojs/starlight@0.41.4(@astrojs/markdown-remark@7.2.1)(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(rollup@4.59.0)(tsx@4.22.4)(yaml@2.9.0))(typescript@6.0.3))) unist-util-visit: specifier: ^5.1.0 version: 5.1.0 @@ -3335,6 +3341,19 @@ packages: '@astrojs/starlight': '>=0.38.0' astro: '>=6.0.0' + starlight-sidebar-topics-dropdown@0.6.0: + resolution: {integrity: sha512-V9a31xML6B28Eo/EZU84AqE8sjhPLjmRpo0buYtRyugRAp2vH7ZKJQDzQwpjSZen7MVb3hgYTNqBus0pdDr+XQ==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@astrojs/starlight': '>=0.38' + starlight-sidebar-topics: '>=0.7' + + starlight-sidebar-topics@0.8.0: + resolution: {integrity: sha512-hE8+w2vNL13h6cq3UJGl05milJQ3+1BSlhaV5V4a993YzYczU6uLAj6jfUDKQ2mmgkdVWp8/UNDF4Je/rQXNQw==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@astrojs/starlight': '>=0.38.0' + stream-replace-string@2.0.0: resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==} @@ -4745,7 +4764,7 @@ snapshots: dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 - picomatch: 4.0.4 + picomatch: 4.0.5 optionalDependencies: rollup: 4.59.0 @@ -5810,9 +5829,9 @@ snapshots: dependencies: format: 0.2.2 - fdir@6.5.0(picomatch@4.0.4): + fdir@6.5.0(picomatch@4.0.5): optionalDependencies: - picomatch: 4.0.4 + picomatch: 4.0.5 file-entry-cache@8.0.0: dependencies: @@ -8183,6 +8202,16 @@ snapshots: transitivePeerDependencies: - supports-color + starlight-sidebar-topics-dropdown@0.6.0(@astrojs/starlight@0.41.4(@astrojs/markdown-remark@7.2.1)(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(rollup@4.59.0)(tsx@4.22.4)(yaml@2.9.0))(typescript@6.0.3))(starlight-sidebar-topics@0.8.0(@astrojs/starlight@0.41.4(@astrojs/markdown-remark@7.2.1)(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(rollup@4.59.0)(tsx@4.22.4)(yaml@2.9.0))(typescript@6.0.3))): + dependencies: + '@astrojs/starlight': 0.41.4(@astrojs/markdown-remark@7.2.1)(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(rollup@4.59.0)(tsx@4.22.4)(yaml@2.9.0))(typescript@6.0.3) + starlight-sidebar-topics: 0.8.0(@astrojs/starlight@0.41.4(@astrojs/markdown-remark@7.2.1)(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(rollup@4.59.0)(tsx@4.22.4)(yaml@2.9.0))(typescript@6.0.3)) + + starlight-sidebar-topics@0.8.0(@astrojs/starlight@0.41.4(@astrojs/markdown-remark@7.2.1)(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(rollup@4.59.0)(tsx@4.22.4)(yaml@2.9.0))(typescript@6.0.3)): + dependencies: + '@astrojs/starlight': 0.41.4(@astrojs/markdown-remark@7.2.1)(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(rollup@4.59.0)(tsx@4.22.4)(yaml@2.9.0))(typescript@6.0.3) + picomatch: 4.0.5 + stream-replace-string@2.0.0: {} string-argv@0.3.2: {} @@ -8297,13 +8326,13 @@ snapshots: tinyglobby@0.2.15: dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 tinyglobby@0.2.17: dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 to-regex-range@5.0.1: dependencies: diff --git a/src/config/sidebarConfig.ts b/src/config/sidebarConfig.ts index bc56f1e..b87ba2a 100644 --- a/src/config/sidebarConfig.ts +++ b/src/config/sidebarConfig.ts @@ -21,17 +21,6 @@ export type Item = { // Define which URL paths belong to which sidebar section export const sidebarSections: Record = { - // Home page - minimal sidebar or none - '/': [], - - // Feature Guide section - '/feature-guide': [ - { - label: 'Website Feature Guide', - items: [{ label: 'Overview', slug: 'feature-guide' }], - }, - ], - // Learning Course section '/learning-course': [ { @@ -257,67 +246,6 @@ export const sidebarSections: Record = { ], }, ], - - // Getting Started section - '/getting-started': [ - { - label: 'Getting Started', - items: [ - { - label: 'Website Feature Guide', - slug: 'learning-course/getting-started/website-feature-guide', - }, - { - label: 'Required Tools', - slug: 'learning-course/getting-started/required-tools', - }, - { - label: 'VS Code Overview', - slug: 'learning-course/getting-started/vscode-overview', - }, - { - label: 'Forking and Cloning', - slug: 'learning-course/getting-started/forking-and-cloning', - }, - { - label: 'Intro to Java', - slug: 'learning-course/stage0/stage-overview', - }, - ], - }, - ], - - // Intro To Java section - '/intro-to-java': [ - { - label: 'Intro to Java', - items: [ - { - label: 'Stage Overview', - slug: 'learning-course/stage0/stage-overview', - }, - { - label: 'Java fundamentals', - slug: 'learning-course/stage0/java-fundamentals', - }, - { - label: 'operators', - slug: 'learning-course/stage0/operators', - }, - ], - }, - ], - - // Resources section (content lives at /resources but navbar says "Other Resources") - // '/resources': [ - // { - // label: 'Resources', - // items: [ - // { label: 'Overview', slug: 'resources' }, - // { label: 'Glossary', slug: 'resources/glossary' }, - // ], - // }, - // ], }; /** diff --git a/src/config/sidebarTopics.ts b/src/config/sidebarTopics.ts new file mode 100644 index 0000000..6874f26 --- /dev/null +++ b/src/config/sidebarTopics.ts @@ -0,0 +1,56 @@ +import type { StarlightUserConfig } from '@astrojs/starlight/types'; +import { sidebarSections, type SidebarItem } from './sidebarConfig'; + +type StarlightSidebarItem = NonNullable[number]; + +function convertItem(item: SidebarItem): StarlightSidebarItem { + if (item.items) { + return { + label: item.label, + collapsed: item.collapsed ?? false, + items: item.items.map(convertItem), + }; + } + return { label: item.label, link: '/' + item.slug + '/' }; +} + +export const sidebarTopics = [ + { + label: 'Learning Course', + id: 'learning-course', + link: '/learning-course/', + icon: 'notes', + items: sidebarSections['/learning-course']![0]!.items.map(convertItem), + }, + { + label: "Educator's Guide", + id: 'educators-guide', + link: '/educators-guide/introduction/', + icon: 'open-book', + items: sidebarSections['/educators-guide']![0]!.items.map(convertItem), + }, + { + label: 'Best Practices', + id: 'best-practices', + link: '/best-practices/overview/', + icon: 'approve-check-circle', + items: sidebarSections['/best-practices']![0]!.items.map(convertItem), + }, + { + label: 'Resources', + id: 'resources', + link: '/resources/', + icon: 'document', + items: sidebarSections['/other-resources']![0]!.items.map(convertItem), + }, + { + label: 'Contribution', + id: 'contribution', + link: '/contribution/', + icon: 'code-branch', + items: [ + { label: 'Overview', link: '/contribution/' }, + ...sidebarSections['/contribution']![0]!.items.map(convertItem), + ], + }, +]; diff --git a/src/starlightOverrides/Header.astro b/src/starlightOverrides/Header.astro index 1ceca95..866a7b9 100644 --- a/src/starlightOverrides/Header.astro +++ b/src/starlightOverrides/Header.astro @@ -4,88 +4,8 @@ import Search from '@astrojs/starlight/components/Search.astro'; import { mdiWeatherNight, mdiWhiteBalanceSunny } from '@mdi/js'; import MdiIcon from '../components/MdiIcon.astro'; -import { getSidebarForPath, type SidebarItem } from '../config/sidebarConfig'; - const props = Astro.props; - -const navLinks = [ - { href: '/', label: 'Home' }, - { href: '/learning-course/', label: 'Learning Course' }, - { href: '/educators-guide/introduction/', label: "Educator's Guide" }, - { href: '/best-practices/overview', label: 'Best Practices' }, - { href: '/contribution/', label: 'Contribution' }, -]; - -const currentPath = Astro.url.pathname; - -function isActive(href: string): boolean { - if (href === '/') { - return currentPath === '/' || currentPath === ''; - } - - // Special case: Changelog is a specific page under /resources/ - // Only mark it active if we're exactly on the changelog page - if (href === '/resources/site-changelog/') { - return ( - currentPath === '/resources/site-changelog/' || - currentPath === '/resources/site-changelog' - ); - } - - // Special case: "Other Resources" should NOT be active when on Changelog - if (href === '/resources/') { - return ( - currentPath.startsWith('/resources/') && - !currentPath.startsWith('/resources/site-changelog') - ); - } - - // For nested paths like /educators-guide/introduction/, check the base section - const baseSection = '/' + href.split('/')[1] + '/'; - return currentPath.startsWith(baseSection); -} - -// Get sidebar items for current page -const sidebarSections = getSidebarForPath(currentPath); - -// Build nested structure for mobile menu -type MobileNavItem = { - label: string; - href?: string; - items?: MobileNavItem[]; - depth: number; -}; - -function buildMobileNavItems( - items: SidebarItem[], - depth: number = 0, -): MobileNavItem[] { - const result: MobileNavItem[] = []; - - for (const item of items) { - if (item.slug) { - result.push({ - label: item.label, - href: '/' + item.slug + '/', - depth, - }); - } else if (item.items) { - result.push({ - label: item.label, - items: buildMobileNavItems(item.items, depth + 1), - depth, - }); - } - } - - return result; -} - -// Get all items from all sections -const mobileSidebarItems: MobileNavItem[] = []; -for (const section of sidebarSections) { - mobileSidebarItems.push(...buildMobileNavItems(section.items, 0)); -} +const { hasSidebar } = Astro.locals.starlightRoute; ---
@@ -96,17 +16,22 @@ for (const section of sidebarSections) {
- + { + hasSidebar && ( + + ) + }
@@ -136,115 +61,8 @@ for (const section of sidebarSections) {
- - - - - - + + - - +{hasSidebar && isPageWithTopic && } + diff --git a/src/styles/global.css b/src/styles/global.css index b10c513..08cbff6 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -7,7 +7,7 @@ :root { /* Layout */ --sl-content-width: 85rem !important; - --sl-nav-height: 5.5rem !important; + --sl-nav-height: 3.5rem !important; /* Typography */ --sl-font: 'Inter', system-ui, sans-serif !important; @@ -44,7 +44,7 @@ --sl-color-purple-low: rgba(163, 113, 247, 0.3) !important; } -@media (max-width: 1350px) { +@media (max-width: 50rem) { :root { --sl-nav-height: 3.5rem !important; } @@ -64,6 +64,12 @@ border-bottom: none !important; } +@media (max-width: 50rem) { + starlight-menu-button { + display: none !important; + } +} + /* ========================================================================== Content Links ========================================================================== */ @@ -195,17 +201,6 @@ section[class*='hero'] a:hover svg { drop-shadow(0 0 50px rgba(255, 255, 255, 0.15)) !important; } -/* ========================================================================== - Mobile Sidebar Toggle (Hide Starlight's default - we use unified menu) - ========================================================================== */ - -@media (max-width: 1350px) { - /* Hide Starlight's default sidebar toggle - we merged it into header menu */ - starlight-menu-button { - display: none !important; - } -} - /* ========================================================================== Sidebar ========================================================================== */ @@ -231,7 +226,6 @@ aside.sidebar { /* Hide scrollbar but allow scrolling */ scrollbar-width: none; -ms-overflow-style: none; - background-color: transparent !important; } .sidebar-pane::-webkit-scrollbar, @@ -239,6 +233,13 @@ aside.sidebar { display: none; } +@media (min-width: 50rem) { + .sidebar-pane, + #starlight__sidebar { + background-color: transparent !important; + } +} + .sidebar-pane a[aria-current='page'], #starlight__sidebar a[aria-current='page'] { background-color: var(--accent-color) !important;