Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 9 additions & 16 deletions docs/src/lib/components/Header.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
import { getFirstChild } from '$lib/utils/navigation';
import GithubIcon from '$lib/components/GithubIcon.svelte';
import { OpenVAALogo } from '$lib/components/openVAALogo';
import { DRAWER_ID } from '../consts';

function isActiveSection(section: NavigationSection): boolean {
return page.url.pathname.startsWith(section.route);
}
</script>

<header class="fixed top-0 right-0 left-0 z-50 h-(--spacing-headerHeight) max-h-(--spacing-headerHeight) bg-base-300">
<header class="h-(--spacing-headerHeight) max-h-(--spacing-headerHeight) bg-base-300">
<div class="navbar px-lg">
<div class="navbar-start">
<a href="/">
Expand All @@ -35,24 +36,16 @@
</div>
<div class="navbar-end hidden justify-end md:flex"><GithubIcon /></div>

<!-- Mobile menu button -->
<div class="navbar-end md:hidden">
<details class="dropdown dropdown-end">
<summary class="btn btn-circle btn-ghost" aria-label="Open menu">
<!-- Mobile menu -->
<div class="navbar-end gap-md md:hidden">
<GithubIcon />
<div class="flex-none lg:hidden">
<label for={DRAWER_ID} aria-label="open sidebar" class="btn btn-circle btn-ghost">
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The aria-label "open sidebar" should have consistent capitalization with the close button's aria-label "Close menu" on line 26 of +layout.svelte. Consider changing this to "Open sidebar" or "Open menu" for consistency.

Suggested change
<label for={DRAWER_ID} aria-label="open sidebar" class="btn btn-circle btn-ghost">
<label for={DRAWER_ID} aria-label="Open sidebar" class="btn btn-circle btn-ghost">

Copilot uses AI. Check for mistakes.
<svg class="h-24 w-24" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</summary>
<ul
class="dropdown-content menu mt-md flex w-max max-w-[80vw] flex-col gap-md rounded-md bg-base-100 p-2 py-lg text-lg shadow-lg">
{#each navigation as section}
{@const isActive = isActiveSection(section)}
{@const firstChild = getFirstChild(section)}
<li><a href={firstChild} class="flex justify-end">{section.title}</a></li>
{/each}
<li class="flex justify-end"><GithubIcon /></li>
</ul>
</details>
</label>
</div>
Comment on lines +42 to +48
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wrapping div with class "flex-none lg:hidden" is redundant since the parent already has "md:hidden" which will hide this entire section on medium screens and above. The lg:hidden class serves no additional purpose here.

Copilot uses AI. Check for mistakes.
</div>
</div>
</header>
Expand Down
72 changes: 25 additions & 47 deletions docs/src/lib/components/Navigation.svelte
Original file line number Diff line number Diff line change
@@ -1,56 +1,34 @@
<script lang="ts">
import { page } from '$app/stores';
import { findActiveSection, hasChildren, isActive } from '../utils/navigation';
import { page } from '$app/state';
import { findActiveSection } from '../utils/navigation';
import { navigation } from '../navigation.config';
import NavigationItem from './NavigationItem.svelte';

$: activeSection = findActiveSection($page.url);
type Props = {
full?: boolean;
onLinkClick?: () => unknown;
class?: string;
};

const { full = false, onLinkClick = () => void 0, class: className = '' }: Props = $props();

const url = $derived(page.url);
const activeSection = $derived(findActiveSection(url));
</script>

{#if activeSection}
<nav
class="flex max-w-[20rem] min-w-[20rem] flex-col gap-md overflow-y-auto border-r border-base-300 bg-base-100 p-lg">
<nav class="flex flex-col gap-md overflow-y-auto {className}">
{#if full && navigation.length > 0}
<ul class="menu-compact menu m-0 -ms-12 p-0">
{#each navigation as section}
<NavigationItem item={section} {url} {onLinkClick} />
{/each}
</ul>
{:else if !full && activeSection}
<h3 class="font-bold">{activeSection.title}</h3>

<ul class="menu-compact menu m-0 -ms-12 p-0">
{#each activeSection.children as item}
{#if hasChildren(item)}
{@const itemActive =
isActive(item.route, $page.url) || item.children.some((child) => isActive(child.route, $page.url))}
<li>
<details open={itemActive}>
<summary>
{item.title}
</summary>
<ul>
{#each item.children as child}
{@const childActive = isActive(child.route, $page.url)}
<li>
<a href={child.route} class:menu-active={childActive}>
{child.title}
</a>
</li>
{/each}
</ul>
</details>
</li>
{:else}
{@const itemActive = isActive(item.route, $page.url)}
<li>
<a href={item.route} class:menu-active={itemActive} class:secondary={item.isSecondary}>
{item.title}
</a>
</li>
{/if}
<NavigationItem {item} {url} {onLinkClick} />
{/each}
</ul>
</nav>
{/if}

<style>
.menu-active {
background-color: var(--color-base-300);
color: var(--color-neutral);
}
details > summary {
cursor: pointer;
}
</style>
{/if}
</nav>
48 changes: 48 additions & 0 deletions docs/src/lib/components/NavigationItem.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<script lang="ts">
import { hasChildren, isActive } from '../utils/navigation';
import type { NavigationItem as NavigationItemType, NavigationSection } from '$lib/navigation.type';
import Self from './NavigationItem.svelte';

interface Props {
item: NavigationItemType | NavigationSection;
url: URL;
onLinkClick?: () => unknown;
}

const { item, url, onLinkClick = () => void 0 }: Props = $props();

const itemActive = $derived(
isActive(item.route, url) || (hasChildren(item) && item.children.some((child) => isActive(child.route, url)))
);
</script>

{#if hasChildren(item)}
<li>
<details open={itemActive}>
<summary>
{item.title}
</summary>
<ul>
{#each item.children as child}
<Self item={child} {url} {onLinkClick} />
{/each}
</ul>
</details>
</li>
{:else}
<li>
<a href={item.route} class:menu-active={itemActive} class:secondary={item.isSecondary} onclick={onLinkClick}>
{item.title}
</a>
</li>
{/if}

<style>
.menu-active {
background-color: var(--color-base-300);
color: var(--color-neutral);
}
details > summary {
cursor: pointer;
}
</style>
3 changes: 1 addition & 2 deletions docs/src/lib/components/TableOfContents.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
</script>

{#if filteredHeadings.length > 0}
<nav class="toc" aria-label="Table of contents">
<nav class="toc top-xl" aria-label="Table of contents">
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The top position has changed from 8rem to top-xl (which resolves to 2.5rem based on the CSS custom properties). This significantly alters the sticky positioning of the table of contents. Please verify this is intentional, as it will make the TOC stick much closer to the top of the viewport.

Copilot uses AI. Check for mistakes.
<h2 class="toc-title">On this page</h2>
<ul class="toc-list">
{#each filteredHeadings as heading (heading.id)}
Expand All @@ -95,7 +95,6 @@
<style>
.toc {
position: sticky;
top: 8rem;
max-height: calc(100vh - 8rem);
overflow-y: auto;
border-left: 2px solid var(--color-base-300);
Expand Down
2 changes: 2 additions & 0 deletions docs/src/lib/consts.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export const OPENVAA_REPO_URL = 'https://github.com/OpenVAA/voting-advice-application';

export const DRAWER_ID = 'main-nav-drawer';
2 changes: 1 addition & 1 deletion docs/src/lib/layouts/MdLayout.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import TableOfContents from '$lib/components/TableOfContents.svelte';
import type { Snippet } from 'svelte';
import PeerNavigation from '$lib/components/PeerNavigation.svelte';
import TableOfContents from '$lib/components/TableOfContents.svelte';
import './prism-vs.css';

interface Props {
Expand Down
4 changes: 2 additions & 2 deletions docs/src/routes/(content)/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

<div class="flex min-h-[calc(100vh-var(--spacing-headerHeight))] w-full max-w-full grid-cols-[20rem_1fr_20rem] md:grid">
<div class="row-span-full hidden md:grid">
<Navigation />
<Navigation class="max-w-[20rem] min-w-[20rem] border-r border-base-300 bg-base-100 p-lg" />
</div>

<main class="col-span-full col-start-2 prose grid-cols-subgrid md:grid">
<main class="col-span-full col-start-2 prose max-w-screen grid-cols-subgrid md:grid">
{@render children()}
</main>
</div>
31 changes: 24 additions & 7 deletions docs/src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,33 @@
import './layout.css';
import Header from '$lib/components/Header.svelte';
import Footer from '$lib/components/Footer.svelte';
import { DRAWER_ID } from '$lib/consts';
import Navigation from '$lib/components/Navigation.svelte';

let { children } = $props();
const { children } = $props();
let checked = $state(false);
</script>

<svelte:head><link rel="icon" href="/favicon.png" /></svelte:head>

<Header />

<div class="mt-headerHeight">
{@render children()}
<div class="drawer drawer-end">
<input id={DRAWER_ID} type="checkbox" class="drawer-toggle" bind:checked />
<div class="drawer-content">
<Header />
<div>
{@render children()}
<Footer />
</div>
</div>
<div class="drawer-side">
<label for={DRAWER_ID} aria-label="Close menu" class="drawer-overlay"></label>
<div class="menu min-h-screen w-[80dvw] bg-base-200 p-lg">
<button aria-label="Close menu" class="mb-md cursor-pointer self-end" onclick={() => (checked = false)}>
<svg xmlns="http://www.w3.org/2000/svg" class="h-24 w-24" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
<Navigation full onLinkClick={() => (checked = false)} />
</div>
</div>
</div>

<Footer />
Loading