Skip to content

Commit 3a6ad33

Browse files
authored
feat: docs flyout menu (#71)
Adds a dropdown menu that has one main a tag and flyout sub a tags on hover. The menu is used for the docs entry Related to #3 / #61
1 parent 15492cc commit 3a6ad33

File tree

8 files changed

+96
-25
lines changed

8 files changed

+96
-25
lines changed

apps/svelte.dev/src/routes/nav.json/+server.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const GET = async () => {
1111
async function get_nav_list(): Promise<NavigationLink[]> {
1212
const docs = Object.values(_docs.topics).map((topic) => ({
1313
title: topic.metadata.title,
14+
path: '/' + topic.slug, // this will make the UI show a flyout menu for the docs nav entry
1415
sections: topic.children.map((section) => ({
1516
title: section.metadata.title,
1617
sections: section.children.map((page) => ({

apps/svelte.dev/src/routes/tutorial/[slug]/Menu.svelte

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { click_outside, focus_outside } from '@sveltejs/site-kit/actions';
44
import { Icon } from '@sveltejs/site-kit/components';
55
import { open_nav } from '@sveltejs/site-kit/nav';
6-
import { mql, reduced_motion, theme } from '@sveltejs/site-kit/stores';
6+
import { mql, reduced_motion } from '@sveltejs/site-kit/stores';
77
import { expoOut } from 'svelte/easing';
88
import { slide } from 'svelte/transition';
99
@@ -27,7 +27,6 @@
2727

2828
<div
2929
class="container"
30-
class:dark={$theme.current === 'dark'}
3130
use:focus_outside={() => (is_open = false)}
3231
use:click_outside={() => (is_open = false)}
3332
>
@@ -155,8 +154,6 @@
155154

156155
<style>
157156
.container {
158-
--shadow: 0px 0px 14px rgba(0, 0, 0, 0.1);
159-
160157
position: relative;
161158
162159
display: flex;
@@ -173,10 +170,6 @@
173170
z-index: 4;
174171
}
175172
176-
.container.dark {
177-
--shadow: 0 0 0 1px var(--sk-back-4);
178-
}
179-
180173
header {
181174
position: relative;
182175
/* z-index: 2; */
@@ -229,7 +222,7 @@
229222
background-color: var(--sk-back-2);
230223
231224
border-radius: var(--sk-border-radius);
232-
box-shadow: var(--shadow);
225+
box-shadow: var(--sk-shadow);
233226
234227
cursor: pointer;
235228
}
@@ -306,7 +299,7 @@
306299
max-height: 70vh;
307300
background: var(--sk-back-2);
308301
z-index: -1;
309-
box-shadow: var(--shadow);
302+
box-shadow: var(--sk-shadow);
310303
border-radius: 0 0 var(--sk-border-radius) var(--sk-border-radius);
311304
display: flex;
312305
flex-direction: column;
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<script lang="ts">
2+
import { page } from '$app/stores';
3+
import type { NavigationLink } from '../types';
4+
5+
let { links: _links, prefix }: { links: NavigationLink; prefix: string } = $props();
6+
7+
const links = $derived([
8+
{ title: _links.title, path: _links.pathname },
9+
..._links.sections!.map((s) => ({ title: s.title, path: s.path! }))
10+
]);
11+
const current = $derived($page.url.pathname.startsWith(`/${prefix}`) ? 'page' : null);
12+
</script>
13+
14+
<div class="dropdown">
15+
<a href={links[0].path} aria-current={current}>{links[0].title}</a>
16+
<nav class="dropdown-content">
17+
{#each links.slice(1) as link}
18+
<a href={link.path}>{link.title}</a>
19+
{/each}
20+
</nav>
21+
</div>
22+
23+
<style>
24+
.dropdown {
25+
position: relative;
26+
display: inline-block;
27+
}
28+
29+
.dropdown-content {
30+
display: none;
31+
position: absolute;
32+
left: -1rem;
33+
background-color: var(--sk-back-1);
34+
min-width: 10rem;
35+
z-index: 1;
36+
animation: flyout 0.3s ease-in-out;
37+
box-shadow: var(--sk-shadow);
38+
border-radius: 0 0 var(--sk-border-radius) var(--sk-border-radius);
39+
}
40+
41+
@keyframes flyout {
42+
from {
43+
opacity: 0;
44+
transform: translateY(-10px);
45+
}
46+
to {
47+
opacity: 1;
48+
transform: translateY(0);
49+
}
50+
}
51+
52+
.dropdown:hover .dropdown-content {
53+
display: block;
54+
}
55+
56+
.dropdown-content a {
57+
color: var(--sk-text-3);
58+
padding: 1.3rem;
59+
text-decoration: none;
60+
display: block;
61+
margin: 0 !important;
62+
63+
&:last-of-type {
64+
border-radius: 0 0 var(--sk-border-radius) var(--sk-border-radius);
65+
}
66+
}
67+
68+
.dropdown-content a:hover {
69+
background-color: var(--sk-back-4);
70+
}
71+
</style>

packages/site-kit/src/lib/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export { default as Icons } from './Icons.svelte';
44
export { default as Section } from './Section.svelte';
55
export { default as Shell } from './Shell.svelte';
66
export { default as ThemeToggle } from './ThemeToggle.svelte';
7+
export { default as LinksDropdown } from './LinksDropdown.svelte';

packages/site-kit/src/lib/docs/DocsOnThisPage.svelte

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import { page } from '$app/stores';
55
import { click_outside, focus_outside, root_scroll } from '../actions';
66
import Icon from '../components/Icon.svelte';
7-
import { mql, nav_open, on_this_page_open, overlay_open, reduced_motion, theme } from '../stores';
7+
import { mql, nav_open, on_this_page_open, overlay_open, reduced_motion } from '../stores';
88
import { createEventDispatcher, onMount, tick } from 'svelte';
99
import { expoOut } from 'svelte/easing';
1010
import { readable } from 'svelte/store';
@@ -175,7 +175,6 @@
175175
<aside
176176
class="on-this-page"
177177
class:mobile={$is_mobile}
178-
class:dark={$theme.current === 'dark'}
179178
style:z-index={mobile_z_index}
180179
bind:this={containerEl}
181180
use:click_outside={() => $is_mobile && ($on_this_page_open = false)}
@@ -332,7 +331,6 @@
332331
}
333332
334333
.on-this-page.mobile {
335-
--shadow: 0px 0px 14px rgba(0, 0, 0, 0.1);
336334
position: relative;
337335
top: 0;
338336
left: 0;
@@ -349,10 +347,6 @@
349347
overflow-y: initial;
350348
}
351349
352-
.on-this-page.mobile.dark {
353-
--shadow: 0 0 0 1px var(--sk-back-4);
354-
}
355-
356350
.on-this-page.mobile .desktop-only-heading {
357351
display: none;
358352
}
@@ -369,7 +363,7 @@
369363
370364
z-index: 2;
371365
372-
box-shadow: var(--shadow);
366+
box-shadow: var(--sk-shadow);
373367
border-radius: var(--sk-border-radius);
374368
box-sizing: border-box;
375369
@@ -410,7 +404,7 @@
410404
background-color: var(--sk-back-3);
411405
412406
border-radius: 0 0 var(--sk-border-radius) var(--sk-border-radius);
413-
box-shadow: var(--shadow);
407+
box-shadow: var(--sk-shadow);
414408
}
415409
416410
.on-this-page.mobile ul {

packages/site-kit/src/lib/nav/Nav.svelte

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Top navigation bar for the application. It provides a slot for the left side, th
1313
import Separator from './Separator.svelte';
1414
import type { NavigationLink } from '../types';
1515
import type { Snippet } from 'svelte';
16+
import LinksDropdown from '../components/LinksDropdown.svelte';
1617
1718
interface Props {
1819
home_title?: string;
@@ -103,12 +104,16 @@ Top navigation bar for the application. It provides a slot for the left side, th
103104

104105
<div class="menu">
105106
{#each links as link}
106-
<a
107-
href={link.pathname}
108-
aria-current={$page.url.pathname.startsWith(`/${link.prefix}`) ? 'page' : null}
109-
>
110-
{link.title}
111-
</a>
107+
{#if link.sections?.[0].path}
108+
<LinksDropdown links={link} prefix={link.prefix} />
109+
{:else}
110+
<a
111+
href={link.pathname}
112+
aria-current={$page.url.pathname.startsWith(`/${link.prefix}`) ? 'page' : null}
113+
>
114+
{link.title}
115+
</a>
116+
{/if}
112117
{/each}
113118

114119
<Separator />

packages/site-kit/src/lib/styles/tokens.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ body {
6868
--sk-text-4: hsl(0, 0%, 45%);
6969
--sk-text-translucent: hsla(0, 0%, 100%, 0.9);
7070
--sk-scrollbar: rgba(255, 255, 255, 0.3);
71+
--sk-shadow: 0px 0px 0 1px var(--sk-back-4);
7172

7273
--sk-theme-1-variant: hsl(15, 100%, 40%);
7374
--sk-theme-2-variant: hsl(240, 8%, 35%);
@@ -107,6 +108,7 @@ body {
107108
--sk-text-3: var(--sk-theme-2);
108109
--sk-text-4: hsl(0, 0%, 65%);
109110
--sk-scrollbar: rgba(0, 0, 0, 0.3);
111+
--sk-shadow: 0px 0px 14px rgba(0, 0, 0, 0.1);
110112

111113
/* same in light mode, different in dark mode */
112114
--sk-theme-1-variant: hsl(15, 100%, 50%);
@@ -168,6 +170,7 @@ body {
168170
--sk-text-3: var(--sk-theme-2);
169171
--sk-text-4: hsl(0, 0%, 65%);
170172
--sk-scrollbar: rgba(0, 0, 0, 0.3);
173+
--sk-shadow: 0px 0px 14px rgba(0, 0, 0, 0.1);
171174

172175
/* same in light mode, different in dark mode */
173176
--sk-theme-1-variant: hsl(15, 100%, 50%);
@@ -214,6 +217,7 @@ body {
214217
--sk-text-4: hsl(0, 0%, 45%);
215218
--sk-text-translucent: hsla(0, 0%, 100%, 0.9);
216219
--sk-scrollbar: rgba(255, 255, 255, 0.3);
220+
--sk-shadow: 0px 0px 0 1px var(--sk-back-4);
217221

218222
--sk-back-3-hsl: 0, 0%, 14%;
219223
--sk-theme-1-variant: hsl(15, 100%, 40%);

packages/site-kit/src/lib/types.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ export interface NavigationLink {
44
pathname: string;
55
sections?: {
66
title: string;
7+
path?: string;
78
sections: {
89
title: string;
10+
path?: string;
911
sections: {
1012
title: string;
1113
path: string;

0 commit comments

Comments
 (0)