refactor: update footer, revamp theme toggle, add head metadata, post navigation spacing
All checks were successful
build dist / build-dist (push) Successful in 35s

This commit is contained in:
z0x 2025-05-07 10:01:47 -04:00
parent 81d454824a
commit a522447570
9 changed files with 129 additions and 144 deletions

View file

@ -11,12 +11,17 @@ import SocialIcons from "./SocialIcons.astro";---
class="flex flex-col items-center justify-center gap-y-2 sm:flex-row sm:justify-between"
>
<div class="flex items-center gap-x-2">
<span class="text-muted-foreground text-center text-sm">
&copy; {new Date().getFullYear()} • <Link href="https://z0x.ca?utm_source=blog.z0x.ca" class="hover:text-primary">z0x</Link>
<span class="text-muted-foreground text-sm">
&copy; {new Date().getFullYear()} All rights reserved.
</span>
<Separator orientation="vertical" className="h-4!" />
<p class="text-muted-foreground text-center text-sm">
All rights reserved.
<Separator orientation="vertical" className="hidden h-4! sm:block" />
<p class="text-muted-foreground text-sm">
<Link
href="https://z0x.ca?utm_source=blog.z0x.ca"
class="text-foreground"
external
underline>z0x</Link
>
</p>
</div>
<SocialIcons links={SOCIAL_LINKS} />

View file

@ -1,10 +1,4 @@
---
import "@fontsource-variable/geist";
import "@fontsource-variable/geist-mono";
import "../styles/callout.css";
import "../styles/global.css";
import "../styles/typography.css";
import { ClientRouter } from "astro:transitions";
import { SITE } from "@/consts";
@ -16,15 +10,18 @@ interface Props {
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
const { title, description, image = "/static/twitter-card.png" } = Astro.props;
const { title, description } = Astro.props;
---
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="generator" content={Astro.generator} />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<meta name="generator" content={Astro.generator} />
<meta name="HandheldFriendly" content="True" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
<meta
name="format-detection"
content="telephone=no,date=no,address=no,email=no,url=no"
@ -32,6 +29,12 @@ const { title, description, image = "/static/twitter-card.png" } = Astro.props;
<link rel="canonical" href={canonicalURL} />
<link rel="sitemap" href="/sitemap-index.xml" />
<link
rel="alternate"
type="application/rss+xml"
title={SITE.title}
href={new URL('rss.xml', Astro.site)}
/>
<title>{title}</title>
<meta name="title" content={title} />
@ -69,38 +72,3 @@ const { title, description, image = "/static/twitter-card.png" } = Astro.props;
<meta property="twitter:description" content={description} />
<ClientRouter />
<script is:inline>
function setDarkMode(document) {
const getThemePreference = () => {
if (
typeof localStorage !== 'undefined' &&
localStorage.getItem('theme')
) {
return localStorage.getItem('theme')
}
return window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'theme-light'
}
const isDark = getThemePreference() === 'dark'
document.documentElement.classList[isDark ? 'add' : 'remove']('dark')
if (typeof localStorage !== 'undefined') {
const observer = new MutationObserver(() => {
const isDark = document.documentElement.classList.contains('dark')
localStorage.setItem('theme', isDark ? 'dark' : 'theme-light')
})
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class'],
})
}
}
setDarkMode(document)
document.addEventListener('astro:before-swap', (ev) => {
setDarkMode(ev.newDocument)
})
</script>

View file

@ -1,7 +1,7 @@
---
import Container from "@/components/Container.astro";
import Link from "@/components/Link.astro";
import { ModeToggle } from "@/components/ui/mode-toggle";
import ThemeToggle from "@/components/ThemeToggle.astro";
import { SITE } from "@/consts";
---
@ -11,15 +11,11 @@ import { SITE } from "@/consts";
>
<Container>
<div class="flex flex-wrap items-center justify-between gap-4 py-4">
<Link
href="/"
class="hover:text-muted-foreground text-primary text-xl font-medium"
>
<Link href="/" class="hover:text-muted-foreground text-primary text-xl font-medium">
{SITE.title}
</Link>
<div class="flex items-center gap-2 md:gap-4">
<ModeToggle client:load transition:persist />
</div>
<ThemeToggle transition:persist />
</div>
</Container>
</header>
</header>

View file

@ -12,7 +12,7 @@ const { prevPost, nextPost } = Astro.props;
href={nextPost ? `/blog/${nextPost.id}` : '#'}
class={cn(
buttonVariants({ variant: 'outline' }),
'rounded-xl group flex items-center justify-start w-full h-full',
'rounded-xl group flex items-center justify-start size-full',
!nextPost && 'pointer-events-none opacity-50 cursor-not-allowed',
)}
aria-disabled={!nextPost}
@ -23,28 +23,27 @@ const { prevPost, nextPost } = Astro.props;
class="size-4 transition-transform group-hover:-translate-x-1"
/>
</div>
<div class="flex flex-col items-start text-wrap">
<div class="flex flex-col items-start overflow-hidden text-wrap">
<span class="text-muted-foreground text-left text-xs">Next Post</span>
<span class="w-full text-left text-sm text-pretty text-ellipsis"
>{nextPost?.data.title || 'Latest post!'}</span
>
<span class="w-full text-left text-sm text-ellipsis">
{nextPost?.data.title || 'Latest post!'}
</span>
</div>
</Link>
<Link
href={prevPost ? `/blog/${prevPost.id}` : '#'}
class={cn(
buttonVariants({ variant: 'outline' }),
'rounded-xl group flex items-center justify-end w-full h-full',
'rounded-xl group flex items-center justify-end size-full',
!prevPost && 'pointer-events-none opacity-50 cursor-not-allowed',
)}
aria-disabled={!prevPost}
>
<div class="flex flex-col items-end text-wrap">
<span class="text-muted-foreground text-right text-xs">Previous Post</span
>
<span class="w-full text-right text-sm text-pretty text-ellipsis"
>{prevPost?.data.title || 'Last post!'}</span
>
<div class="flex flex-col items-end overflow-hidden text-wrap">
<span class="text-muted-foreground text-right text-xs">Previous Post</span>
<span class="w-full text-right text-sm text-ellipsis">
{prevPost?.data.title || 'Last post!'}
</span>
</div>
<div class="ml-2 flex-shrink-0">
<Icon

View file

@ -1,26 +0,0 @@
---
import Link from "./Link.astro";
import type { Heading } from "./TableOfContents.astro";
const { heading } = Astro.props;
---
<li
class="text-foreground/60 list-inside list-disc px-4 text-sm xl:list-none xl:p-0"
>
<Link
href={'#' + heading.slug}
class="py-1 underline decoration-transparent underline-offset-[3px] transition-colors duration-200 hover:decoration-inherit xl:py-0"
>
{heading.text}
</Link>
{
heading.subheadings.length > 0 && (
<ul class="translate-x-3 xl:mt-2 xl:ml-4 xl:flex xl:translate-x-0 xl:flex-col xl:gap-2">
{heading.subheadings.map((subheading: Heading) => (
<Astro.self heading={subheading} />
))}
</ul>
)
}
</li>

View file

@ -0,0 +1,85 @@
---
import { Button } from "@/components/ui/button";
import { Icon } from "astro-icon/components";
---
<Button id="theme-toggle" variant="outline" size="icon" title="Toggle theme">
<Icon
name="lucide:sun"
class="size-4 scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90"
/>
<Icon
name="lucide:moon"
class="absolute size-4 scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0"
/>
<span class="sr-only">Toggle theme</span>
</Button>
<script is:inline data-astro-rerun>
const theme = (() => {
const localStorageTheme = localStorage?.getItem('theme') ?? ''
if (['dark', 'light'].includes(localStorageTheme)) {
return localStorageTheme
}
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
return 'dark'
}
return 'light'
})()
if (theme === 'light') {
document.documentElement.classList.remove('dark')
} else {
document.documentElement.classList.add('dark')
}
window.localStorage.setItem('theme', theme)
</script>
<script>
function handleToggleClick() {
const element = document.documentElement
element.classList.add('disable-transitions')
element.classList.toggle('dark')
window.getComputedStyle(element).getPropertyValue('opacity')
requestAnimationFrame(() => {
element.classList.remove('disable-transitions')
})
const isDark = element.classList.contains('dark')
localStorage.setItem('theme', isDark ? 'dark' : 'light')
}
function initThemeToggle() {
const themeToggle = document.getElementById('theme-toggle')
if (themeToggle) {
themeToggle.addEventListener('click', handleToggleClick)
}
}
initThemeToggle()
document.addEventListener('astro:after-swap', () => {
const storedTheme = localStorage.getItem('theme')
const element = document.documentElement
element.classList.add('disable-transitions')
window.getComputedStyle(element).getPropertyValue('opacity')
if (storedTheme === 'dark') {
element.classList.add('dark')
} else {
element.classList.remove('dark')
}
requestAnimationFrame(() => {
element.classList.remove('disable-transitions')
})
initThemeToggle()
})
</script>

View file

@ -1,47 +0,0 @@
import { Button } from "@/components/ui/button";
import { Moon, Sun } from "lucide-react";
import * as React from "react";
export function ModeToggle() {
const [theme, setTheme] = React.useState<"theme-light" | "dark" | "system">(
"theme-light",
);
React.useEffect(() => {
const isDarkMode = document.documentElement.classList.contains("dark");
setTheme(isDarkMode ? "dark" : "theme-light");
}, []);
React.useEffect(() => {
const isDark = theme === "dark";
document.documentElement.classList.add("disable-transitions");
document.documentElement.classList[isDark ? "add" : "remove"]("dark");
window
.getComputedStyle(document.documentElement)
.getPropertyValue("opacity");
requestAnimationFrame(() => {
document.documentElement.classList.remove("disable-transitions");
});
}, [theme]);
const toggleTheme = () => {
setTheme(theme === "dark" ? "theme-light" : "dark");
};
return (
<Button
variant="outline"
size="icon"
className="relative overflow-hidden"
onClick={toggleTheme}
title="Toggle theme"
>
<Sun className="size-4 absolute inset-0 m-auto scale-100 rotate-0 transition-all duration-500 dark:scale-0 dark:-rotate-90" />
<Moon className="size-4 absolute inset-0 m-auto scale-0 rotate-90 transition-all duration-500 dark:scale-100 dark:rotate-0" />
<span className="sr-only">Toggle theme</span>
</Button>
);
}

View file

@ -1,4 +1,10 @@
---
import "@/styles/callout.css";
import "@/styles/global.css";
import "@/styles/typography.css";
import "@fontsource-variable/geist";
import "@fontsource-variable/geist-mono";
import Footer from "@/components/Footer.astro";
import Head from "@/components/Head.astro";
import Header from "@/components/Header.astro";
@ -19,7 +25,7 @@ const { title, description } = Astro.props;
</head>
<body>
<div
class="box-border flex h-fit min-h-screen flex-col gap-y-6 font-sans antialiased"
class="flex h-fit min-h-screen flex-col gap-y-6"
>
<Header />
<main class="grow">

View file

@ -86,7 +86,6 @@
}
}
.disable-transitions,
.disable-transitions * {
@apply transition-none!;
}