Skip to content

Commit ba8f495

Browse files
committed
Add breadcrumbs
1 parent 50d4956 commit ba8f495

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed

src/components/Breadcrumbs.astro

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
---
2+
import Section from "@ui/Section.astro";
3+
4+
export interface Props {
5+
linksData: any;
6+
currentPath: string;
7+
homeLabel?: string;
8+
homePath?: string;
9+
separator?: string;
10+
}
11+
12+
const {
13+
linksData,
14+
currentPath,
15+
homeLabel = "Home",
16+
homePath = "/",
17+
separator = "/"
18+
} = Astro.props;
19+
20+
interface BreadcrumbItem {
21+
name: string;
22+
path: string;
23+
isActive: boolean;
24+
}
25+
26+
function findBreadcrumbTrail(items: any[], currentPath: string, parentName?: string): BreadcrumbItem[] {
27+
for (const item of items) {
28+
if (item.path === currentPath) {
29+
const breadcrumb: BreadcrumbItem = {
30+
name: item.name,
31+
path: item.path,
32+
isActive: true
33+
};
34+
35+
if (parentName) {
36+
return [
37+
{ name: parentName, path: '', isActive: false },
38+
breadcrumb
39+
];
40+
}
41+
42+
return [breadcrumb];
43+
}
44+
45+
if (item.items) {
46+
const subTrail = findBreadcrumbTrail(item.items, currentPath, item.name);
47+
if (subTrail.length > 0) {
48+
return subTrail;
49+
}
50+
}
51+
}
52+
53+
return [];
54+
}
55+
56+
const breadcrumbs: BreadcrumbItem[] = [];
57+
58+
breadcrumbs.push({
59+
name: homeLabel,
60+
path: homePath,
61+
isActive: currentPath === homePath
62+
});
63+
64+
if (currentPath !== homePath && linksData?.header) {
65+
const trail = findBreadcrumbTrail(linksData.header, currentPath);
66+
breadcrumbs.push(...trail);
67+
}
68+
69+
if (breadcrumbs.length === 1 && currentPath !== homePath) {
70+
const pathSegments = currentPath.split('/').filter(Boolean);
71+
const lastSegment = pathSegments[pathSegments.length - 1];
72+
73+
if (lastSegment) {
74+
breadcrumbs.push({
75+
name: lastSegment.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase()),
76+
path: currentPath,
77+
isActive: true
78+
});
79+
}
80+
}
81+
---
82+
83+
<Section>
84+
{ currentPath !== homePath &&
85+
<nav aria-label="Breadcrumb" class="px-4">
86+
<ol class="flex flex-wrap items-center text-sm list-none m-0 p-0">
87+
{breadcrumbs.map((crumb, index) => (
88+
<li class="flex items-center">
89+
{crumb.isActive ? (
90+
<span class="text-gray-900 font-medium px-2 py-1" aria-current="page">
91+
{crumb.name}
92+
</span>
93+
) : crumb.path ? (
94+
<a href={crumb.path} class="underline hover:text-button-hover px-2 py-1 rounded transition-colors duration-200 hover:underline">
95+
{crumb.name}
96+
</a>
97+
) : (
98+
<span class="text-gray-500 italic px-2 py-1">
99+
{crumb.name}
100+
</span>
101+
)}
102+
{index < breadcrumbs.length - 1 && (
103+
<span class="mx-2 text-gray-400 select-none" aria-hidden="true">
104+
{separator}
105+
</span>
106+
)}
107+
</li>
108+
))}
109+
</ol>
110+
</nav>
111+
}
112+
</Section>

src/layouts/Layout.astro

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,23 @@ import BaseHead from "@components/BaseHead.astro";
33
import Header from "@components/Header.astro";
44
import Footer from "@components/Footer.astro";
55
import Offline from "@components/Offline.astro";
6+
import Breadcrumbs from '@components/Breadcrumbs.astro';
7+
8+
import linksData from '@src/data/links.json';
69
710
import "@fortawesome/fontawesome-free/css/all.min.css";
811
import "@styles/tailwind.css";
912
import "@styles/global.css";
1013
import "@styles/search.css";
1114
15+
1216
export interface Props {
1317
title: string;
1418
description: string;
1519
}
1620
21+
const currentPath = Astro.url.pathname;
22+
1723
const { title, description } = Astro.props;
1824
1925
if (!title || !description) {
@@ -37,6 +43,13 @@ const externalDomain = new URL(Astro.site || "").hostname;
3743

3844
<main class="main pt-28" role="main">
3945
<Offline />
46+
<Breadcrumbs
47+
linksData={linksData}
48+
currentPath={currentPath}
49+
homeLabel="EuroPython 2025"
50+
homePath="/"
51+
separator=""
52+
/>
4053
<slot />
4154
</main>
4255

0 commit comments

Comments
 (0)