Skip to content

Commit

Permalink
Add sidebar navigation (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
KonradSzwarc authored Aug 22, 2024
1 parent 5709d73 commit a80df78
Show file tree
Hide file tree
Showing 15 changed files with 297 additions and 18 deletions.
18 changes: 18 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"nanoid": "5.0.7",
"polished": "4.3.1",
"tailwind-merge": "2.5.2",
"throttle-debounce": "5.0.2",
"type-fest": "4.25.0"
},
"devDependencies": {
Expand All @@ -61,6 +62,7 @@
"@percy/cli": "1.29.1",
"@tailwindcss/typography": "0.5.14",
"@total-typescript/ts-reset": "0.5.1",
"@types/throttle-debounce": "5.0.2",
"chalk": "5.3.0",
"commander": "12.1.0",
"concurrently": "8.2.2",
Expand Down
2 changes: 1 addition & 1 deletion src/components/base-layout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const lang = Astro.props.lang ?? getGlobalContext<ContextSlice<'locale'>>(Astro)
---

<!doctype html>
<html lang={lang}>
<html lang={lang} class="scroll-smooth">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
Expand Down
29 changes: 20 additions & 9 deletions src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { Metadata } from '@/web/components/metadata';
import Project from '@/web/components/project.astro';
import Reference from '@/web/components/reference.astro';
import { Section, SectionTitle, Subsection, SubsectionTitle } from '@/web/components/section';
import { Sidebar, SidebarItem } from '@/web/components/sidebar';
import { Skill } from '@/web/components/skills';
import { initializeWebContext } from '@/web/utils/initialize-web-context';
Expand All @@ -35,16 +36,26 @@ await initializeWebContext(Astro, {
});
---

<Layout class="gap-16">
<Layout class="gap-16 [&_section]:scroll-mt-8">
<Fragment slot="head">
<Font />
<DuskColors />
<AccentColors />
<ElementColors />
<Metadata entry={getEntry('metadata', 'main')} />
</Fragment>
<Basics entry={getEntry('basics', 'main')} />
<Section class="gap-8">
<Sidebar>
<SidebarItem sectionId="basics" icon="fa6-solid:user">About me</SidebarItem>
<SidebarItem sectionId="skills" icon="fa6-solid:bars-progress">Skills</SidebarItem>
<SidebarItem sectionId="jobs" icon="fa6-solid:suitcase">Work experience</SidebarItem>
<SidebarItem sectionId="projects" icon="fa6-solid:rocket">Projects</SidebarItem>
<SidebarItem sectionId="references" icon="fa6-solid:comment">References</SidebarItem>
<SidebarItem sectionId="achievements" icon="fa6-solid:trophy">Achievements</SidebarItem>
<SidebarItem sectionId="education" icon="fa6-solid:graduation-cap">Education</SidebarItem>
<SidebarItem sectionId="favorites" icon="fa6-solid:star">Favorites</SidebarItem>
</Sidebar>
<Basics entry={getEntry('basics', 'main')} id="basics" />
<Section class="gap-8" id="skills">
<SectionTitle>Skills</SectionTitle>
<VerticalList class="gap-8">
<Subsection class="gap-2">
Expand Down Expand Up @@ -84,7 +95,7 @@ await initializeWebContext(Astro, {
</Subsection>
</VerticalList>
</Section>
<Section class="gap-8">
<Section class="gap-8" id="jobs">
<SectionTitle>Work</SectionTitle>
<VerticalList class="gap-10">
<Job entry={getEntry('jobs', 'meta')} />
Expand All @@ -94,23 +105,23 @@ await initializeWebContext(Astro, {
<Job entry={getEntry('jobs', 'codepen')} />
</VerticalList>
</Section>
<Section class="gap-8">
<Section class="gap-8" id="projects">
<SectionTitle>Projects</SectionTitle>
<VerticalList class="gap-10">
<Project entry={getEntry('projects', 'moni-buddy')} />
<Separator />
<Project entry={getEntry('projects', 'user-board')} />
</VerticalList>
</Section>
<Section class="gap-8">
<Section class="gap-8" id="references">
<SectionTitle>References</SectionTitle>
<VerticalList class="gap-10">
<Reference entry={getEntry('references', 'jane-smith')} />
<Separator />
<Reference entry={getEntry('references', 'john-doe')} />
</VerticalList>
</Section>
<Section class="gap-8">
<Section class="gap-8" id="achievements">
<SectionTitle>Achievements</SectionTitle>
<VerticalList class="gap-8">
<Achievement entry={getEntry('achievements', 'advanced-react-developer')} />
Expand All @@ -122,15 +133,15 @@ await initializeWebContext(Astro, {
<Achievement entry={getEntry('achievements', 'redux-toolkit-contributor')} />
</VerticalList>
</Section>
<Section class="gap-8">
<Section class="gap-8" id="education">
<SectionTitle>Education</SectionTitle>
<VerticalList class="gap-10">
<Education entry={getEntry('education', 'master')} />
<Separator />
<Education entry={getEntry('education', 'bachelor')} />
</VerticalList>
</Section>
<Section class="gap-8">
<Section class="gap-8" id="favorites">
<SectionTitle>Favorites</SectionTitle>
<VerticalList class="gap-10">
<Subsection class="gap-2">
Expand Down
10 changes: 10 additions & 0 deletions src/pages/template.astro
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ const props: ComponentProps<typeof MainTemplate> = {
},
},
metadata: 'main',
sidebar: [
{ sectionId: 'basics', icon: 'fa6-solid:user', text: 'About me' },
{ sectionId: 'skills', icon: 'fa6-solid:bars-progress', text: 'Skills' },
{ sectionId: 'jobs', icon: 'fa6-solid:suitcase', text: 'Work experience' },
{ sectionId: 'projects', icon: 'fa6-solid:rocket', text: 'Projects' },
{ sectionId: 'references', icon: 'fa6-solid:comment', text: 'References' },
{ sectionId: 'achievements', icon: 'fa6-solid:trophy', text: 'Achievements' },
{ sectionId: 'education', icon: 'fa6-solid:graduation-cap', text: 'Education' },
{ sectionId: 'favorites', icon: 'fa6-solid:star', text: 'Favorites' },
],
basics: 'main',
sections: [
{
Expand Down
4 changes: 4 additions & 0 deletions src/styles/colors/elements/default.astro
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
--text-title: var(--dusk-900);
--text-primary: var(--dusk-700);
--text-secondary: var(--dusk-500);
--text-contrast: var(--white);

--icon-light: var(--dusk-400);
--icon-main: var(--dusk-500);
Expand All @@ -19,6 +20,7 @@
--bg-body: var(--dusk-50);
--bg-card: var(--white);
--bg-popover: var(--white);
--bg-tooltip: var(--dusk-950);

--bg-light: var(--dusk-100);
--bg-main: var(--dusk-200);
Expand All @@ -34,6 +36,7 @@
--text-title: var(--white);
--text-primary: var(--dusk-50);
--text-secondary: var(--dusk-100);
--text-contrast: var(--dusk-950);

--icon-light: var(--dusk-500);
--icon-main: var(--dusk-400);
Expand All @@ -45,6 +48,7 @@
--bg-body: var(--dusk-950);
--bg-card: var(--dusk-900);
--bg-popover: var(--dusk-950);
--bg-tooltip: var(--white);

--bg-light: var(--dusk-800);
--bg-main: var(--dusk-700);
Expand Down
3 changes: 3 additions & 0 deletions src/types/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ type SectionBase<C extends keyof ContentEntryMap> = {

/** Title displayed above the section content. */
title: string;

/** Unique identifier for the section. Used to display the sidebar. */
id?: string;
};

type SectionEntries<C extends keyof ContentEntryMap, P extends Component> = Omit<ComponentProps<P>, 'entry'> & {
Expand Down
8 changes: 5 additions & 3 deletions src/web/components/basics/basics.astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
---
import type { HTMLAttributes } from 'astro/types';
import type { AsyncEntry } from '@/types/entries';
import RenderedContent from '@/web/components/rendered-content.astro';
Expand All @@ -7,16 +9,16 @@ import MyImage from './my-image.astro';
import PdfButton from './pdf-button.astro';
import Socials from './socials.astro';
interface Props {
interface Props extends HTMLAttributes<'section'> {
/** An entry from the `basics` collection. */
entry: AsyncEntry<'basics'>;
}
const { entry } = Astro.props;
const { entry, ...props } = Astro.props;
const { data: basics, body, render } = await entry;
---

<section>
<section {...props}>
<div class="flex flex-col items-center lg:flex-row lg:items-start">
<MyImage size={224} class="size-28 lg:size-20" basics={basics} />
<div class="mt-4 self-center text-center lg:ml-4 lg:mt-0 lg:space-y-0.5 lg:text-left">
Expand Down
5 changes: 4 additions & 1 deletion src/web/components/layout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ const { class: className, bodyClass, ...props } = Astro.props;
</Fragment>
<ThemeSwitcher />
<main
class={cn('mx-auto grid w-full max-w-screen-lg bg-color-bg-card p-6 shadow-2xl sm:rounded-3xl sm:p-10', className)}
class={cn(
'relative mx-auto grid w-full max-w-screen-lg bg-color-bg-card p-6 shadow-2xl sm:rounded-3xl sm:p-10',
className,
)}
{...props}
>
<slot />
Expand Down
2 changes: 2 additions & 0 deletions src/web/components/sidebar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as Sidebar } from './sidebar.astro';
export { default as SidebarItem } from './sidebar-item.astro';
93 changes: 93 additions & 0 deletions src/web/components/sidebar/sidebar-item.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
import type { HTMLAttributes } from 'astro/types';
import { Icon } from '@/components/icon';
import Tooltip from '@/web/components/tooltip.astro';
export interface Props extends HTMLAttributes<'main'> {
icon: string;
sectionId: string;
}
const { icon, sectionId, title } = Astro.props;
---

<li>
<Tooltip>
<a
slot="trigger"
href={`#${sectionId}`}
class="inline-flex size-10 items-center justify-center rounded-lg transition"
data-sidebar-link
aria-label={`${title} section`}
>
<Icon name={icon} class="size-5" />
</a>
<slot />
</Tooltip>
</li>

<style>
a[aria-current='page'] {
@apply bg-color-button-bg text-color-button-bg-contrast;
}

a:not([aria-current='page']) {
@apply text-color-icon-main hover:bg-color-bg-main;
}
</style>

<script>
import { throttle } from 'throttle-debounce';

// Works best when set to the half of the gap between sections.
const DETECT_SECTION_OFFSET = 32;

const sidebarLinks = [...document.querySelectorAll('[data-sidebar-link]')].flatMap((link) =>
link instanceof HTMLAnchorElement ? [link] : [],
);
const sections = [...document.querySelectorAll('section[id]')].flatMap((section) =>
section instanceof HTMLElement && sidebarLinks.some((link) => link.hash === `#${section.id}`) ? [section] : [],
);

function setCurrentNavlinkFromHash() {
const currentHash = location.hash;

sidebarLinks.forEach((link) => {
if (link.getAttribute('href') === currentHash) {
link.setAttribute('aria-current', 'page');
} else {
link.removeAttribute('aria-current');
}
});
}

function setCurrentHashFromScroll() {
const section = sections.find((section) => {
const { top, bottom } = section.getBoundingClientRect();
return top <= DETECT_SECTION_OFFSET && bottom > -DETECT_SECTION_OFFSET;
});

console.log(section?.id || null);

if (!section && location.hash) {
return setHash(null);
}

if (section && location.hash !== `#${section.id}`) {
return setHash(section.id);
}
}

function setHash(sectionId: string | null) {
history.replaceState(null, '', sectionId ? `#${sectionId}` : location.pathname + location.search);
window.dispatchEvent(new HashChangeEvent('hashchange'));
}

addEventListener('hashchange', setCurrentNavlinkFromHash);
addEventListener('scroll', throttle(250, setCurrentHashFromScroll));
addEventListener('load', () => {
setCurrentNavlinkFromHash();
setCurrentHashFromScroll();
});
</script>
17 changes: 17 additions & 0 deletions src/web/components/sidebar/sidebar.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
import type { HTMLAttributes } from 'astro/types';
import { cn } from '@/utils/cn';
export interface Props extends HTMLAttributes<'nav'> {
className?: string;
}
const { class: className, ...props } = Astro.props;
---

<nav data-sidebar class={cn(['absolute -right-18 hidden h-full w-max xl:block', className])} {...props}>
<ul class={cn('sticky top-4 grid h-fit gap-2 rounded-lg bg-color-bg-card p-2 shadow-lg')}>
<slot />
</ul>
</nav>
Loading

0 comments on commit a80df78

Please sign in to comment.