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
1 change: 1 addition & 0 deletions astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export default defineConfig({
components: {
EditLink: "./src/components/EditLink.astro",
Footer: "./src/components/Footer.astro",
Header: "./src/components/Header.astro",
Hero: "./src/components/Hero.astro",
SiteTitle: "./src/components/SiteTitle.astro",
ThemeProvider: "./src/components/ThemeProvider.astro",
Expand Down
91 changes: 91 additions & 0 deletions src/components/Header.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
import config from 'virtual:starlight/user-config';

import LanguageSelect from 'virtual:starlight/components/LanguageSelect';
import Search from 'virtual:starlight/components/Search';
import SiteTitle from 'virtual:starlight/components/SiteTitle';
import SocialIcons from 'virtual:starlight/components/SocialIcons';
import ThemeSelect from 'virtual:starlight/components/ThemeSelect';

const shouldRenderSearch =
config.pagefind || config.components.Search !== '@astrojs/starlight/components/Search.astro';
---

<div class="header">
<div class="title-wrapper sl-flex">
<SiteTitle />
</div>
<div class="sl-flex print:hidden">
{shouldRenderSearch && <Search />}
</div>
{/* Mobile-only theme toggle — desktop uses the one inside right-group */}
<div class="md:sl-hidden print:hidden mobile-theme">
<ThemeSelect />
</div>
<div class="sl-hidden md:sl-flex print:hidden right-group">
<div class="sl-flex social-icons">
<SocialIcons />
</div>
<ThemeSelect />
<LanguageSelect />
</div>
</div>

<style>
@layer starlight.core {
.header {
display: flex;
gap: var(--sl-nav-gap);
justify-content: space-between;
align-items: center;
height: 100%;
}

.title-wrapper {
overflow: clip;
padding: 0.25rem;
margin: -0.25rem;
min-width: 0;
}

.right-group,
.social-icons {
gap: 1rem;
align-items: center;
}

.social-icons::after {
content: '';
height: 2rem;
border-inline-end: 1px solid var(--sl-color-gray-5);
}

@media (min-width: 50rem) {
:global(:root[data-has-sidebar]) {
--__sidebar-pad: calc(2 * var(--sl-nav-pad-x));
}
:global(:root:not([data-has-toc])) {
--__toc-width: 0rem;
}
.header {
--__sidebar-width: max(0rem, var(--sl-content-inline-start, 0rem) - var(--sl-nav-pad-x));
--__main-column-fr: calc(
(
100% + var(--__sidebar-pad, 0rem) - var(--__toc-width, var(--sl-sidebar-width)) -
(2 * var(--__toc-width, var(--sl-nav-pad-x))) - var(--sl-content-inline-start, 0rem) -
var(--sl-content-width)
) / 2
);
display: grid;
grid-template-columns:
minmax(
calc(var(--__sidebar-width) + max(0rem, var(--__main-column-fr) - var(--sl-nav-gap))),
auto
)
1fr
auto;
align-content: center;
}
}
}
</style>
5 changes: 3 additions & 2 deletions src/components/ThemeProvider.astro
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
---
---

{/* Inlined to avoid FOUC. Defaults to light; dark is opt-in. Never auto-switches on prefers-color-scheme. */}
{/* Inlined to avoid FOUC. Defaults to system prefers-color-scheme; overridden by stored user preference. */}
<script is:inline>
window.StarlightThemeProvider = (() => {
const storedTheme =
typeof localStorage !== 'undefined' && localStorage.getItem('starlight-theme');
const theme = storedTheme || 'light';
const prefersTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
const theme = storedTheme || prefersTheme;
document.documentElement.dataset.theme = theme === 'dark' ? 'dark' : 'light';
return {
updatePickers(theme = storedTheme || 'auto') {
Expand Down
10 changes: 5 additions & 5 deletions src/components/ThemeSelect.astro
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@

const storageKey = 'starlight-theme';

const parseTheme = (theme: unknown): Theme =>
theme === 'dark' ? 'dark' : 'light';

const loadTheme = (): Theme =>
parseTheme(typeof localStorage !== 'undefined' && localStorage.getItem(storageKey));
const loadTheme = (): Theme => {
const stored = typeof localStorage !== 'undefined' ? localStorage.getItem(storageKey) : null;
if (stored === 'dark' || stored === 'light') return stored;
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
};

function storeTheme(theme: Theme): void {
if (typeof localStorage !== 'undefined') {
Expand Down
Loading