Skip to content

Instantly share code, notes, and snippets.

@lorenzolewis
Last active May 8, 2024 20:21
Show Gist options
  • Save lorenzolewis/a2bccd387090b9374aa24f52d434917a to your computer and use it in GitHub Desktop.
Save lorenzolewis/a2bccd387090b9374aa24f52d434917a to your computer and use it in GitHub Desktop.
Use the built-in Starlight sidebar config to create a multi-sidebar layout
---
import type { Props } from "@astrojs/starlight/props";
import Default from "@astrojs/starlight/components/Sidebar.astro";
import { AstroError } from "astro/errors";
// Styles and CSS logic derived from https://daisyui.com/components/collapse/
// This Sidebar override uses the top-level items from the Starlight sidebar config to create sidebars.
// Go through each top-level sidebar item from the Astro config to...
// 1. Validate the config is set up correctly
// 2. Create a new derived set of `Astro.props` that only contains one set of sidebar entries
// 3. Check if the current page being rendered is current page to determine if this sidebar group should be selected
const multiSidebarConfig: [string, boolean, Props][] = Astro.props.sidebar.map(
(entry) => {
if (entry.type !== "group") {
throw new AstroError(
`\`${entry.label}\` cannot be used with multiple Starlight sidebars.
Each top-level \`sidebar\` item in the Starlight config must be either a group or autogenerated.
See https://starlight.astro.build/guides/sidebar/#groups and https://starlight.astro.build/guides/sidebar/#autogenerated-groups`
);
}
// Recursively check if a group of sidebar entries contains the current page
const findIfIsCurrent = (
entry: (typeof Astro.props.sidebar)[number]
): boolean => {
if (entry.type === "link") {
return entry.isCurrent;
}
return entry.entries.some((item) => findIfIsCurrent(item));
};
const isCurrentPage = findIfIsCurrent(entry);
return [
entry.label,
isCurrentPage,
{ ...Astro.props, sidebar: [...entry.entries] },
];
}
);
---
<div class="__collapse">
{
multiSidebarConfig.map(([label, isCurrentPage, config]) => (
<>
<input
type="radio"
name="sidebar"
role="tab"
aria-label={label}
checked={isCurrentPage}
/>
<div class="__collapse-content">
<Default {...config}>
<slot />
</Default>
</div>
</>
))
}
</div>
<style>
.__collapse {
display: grid;
}
.__collapse > input {
/* Layout */
position: relative;
display: inline-flex;
grid-row-start: 1;
appearance: none;
/* Styles */
border-radius: 0.25rem;
padding: 0.2em 0.5rem;
line-height: 1.4;
font-size: var(--sl-text-lg);
font-weight: 600;
cursor: pointer;
user-select: none;
margin-bottom: var(--sl-nav-pad-y);
}
.__collapse > input::after {
content: attr(aria-label);
}
.__collapse > input:checked {
color: var(--sl-color-text-invert);
background-color: var(--sl-color-text-accent);
}
.__collapse > .__collapse-content {
display: none;
grid-column-start: 1;
grid-column-end: span 999;
grid-row-start: 2;
}
.__collapse > input:checked + .__collapse-content {
display: block;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment