refactor: update footer, revamp theme toggle, add head metadata, post navigation spacing
All checks were successful
build dist / build-dist (push) Successful in 35s
All checks were successful
build dist / build-dist (push) Successful in 35s
This commit is contained in:
parent
81d454824a
commit
a522447570
9 changed files with 129 additions and 144 deletions
|
@ -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"
|
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">
|
<div class="flex items-center gap-x-2">
|
||||||
<span class="text-muted-foreground text-center text-sm">
|
<span class="text-muted-foreground text-sm">
|
||||||
© {new Date().getFullYear()} • <Link href="https://z0x.ca?utm_source=blog.z0x.ca" class="hover:text-primary">z0x</Link>
|
© {new Date().getFullYear()} All rights reserved.
|
||||||
</span>
|
</span>
|
||||||
<Separator orientation="vertical" className="h-4!" />
|
<Separator orientation="vertical" className="hidden h-4! sm:block" />
|
||||||
<p class="text-muted-foreground text-center text-sm">
|
<p class="text-muted-foreground text-sm">
|
||||||
All rights reserved.
|
<Link
|
||||||
|
href="https://z0x.ca?utm_source=blog.z0x.ca"
|
||||||
|
class="text-foreground"
|
||||||
|
external
|
||||||
|
underline>z0x</Link
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<SocialIcons links={SOCIAL_LINKS} />
|
<SocialIcons links={SOCIAL_LINKS} />
|
||||||
|
|
|
@ -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 { ClientRouter } from "astro:transitions";
|
||||||
import { SITE } from "@/consts";
|
import { SITE } from "@/consts";
|
||||||
|
|
||||||
|
@ -16,15 +10,18 @@ interface Props {
|
||||||
|
|
||||||
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
|
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 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 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="mobile-web-app-capable" content="yes" />
|
||||||
<meta name="apple-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
|
<meta
|
||||||
name="format-detection"
|
name="format-detection"
|
||||||
content="telephone=no,date=no,address=no,email=no,url=no"
|
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="canonical" href={canonicalURL} />
|
||||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
<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>
|
<title>{title}</title>
|
||||||
<meta name="title" content={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} />
|
<meta property="twitter:description" content={description} />
|
||||||
|
|
||||||
<ClientRouter />
|
<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>
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
import Container from "@/components/Container.astro";
|
import Container from "@/components/Container.astro";
|
||||||
import Link from "@/components/Link.astro";
|
import Link from "@/components/Link.astro";
|
||||||
import { ModeToggle } from "@/components/ui/mode-toggle";
|
import ThemeToggle from "@/components/ThemeToggle.astro";
|
||||||
import { SITE } from "@/consts";
|
import { SITE } from "@/consts";
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -11,15 +11,11 @@ import { SITE } from "@/consts";
|
||||||
>
|
>
|
||||||
<Container>
|
<Container>
|
||||||
<div class="flex flex-wrap items-center justify-between gap-4 py-4">
|
<div class="flex flex-wrap items-center justify-between gap-4 py-4">
|
||||||
<Link
|
<Link href="/" class="hover:text-muted-foreground text-primary text-xl font-medium">
|
||||||
href="/"
|
|
||||||
class="hover:text-muted-foreground text-primary text-xl font-medium"
|
|
||||||
>
|
|
||||||
{SITE.title}
|
{SITE.title}
|
||||||
</Link>
|
</Link>
|
||||||
<div class="flex items-center gap-2 md:gap-4">
|
<div class="flex items-center gap-2 md:gap-4">
|
||||||
<ModeToggle client:load transition:persist />
|
<ThemeToggle transition:persist />
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</header>
|
</header>
|
|
@ -12,7 +12,7 @@ const { prevPost, nextPost } = Astro.props;
|
||||||
href={nextPost ? `/blog/${nextPost.id}` : '#'}
|
href={nextPost ? `/blog/${nextPost.id}` : '#'}
|
||||||
class={cn(
|
class={cn(
|
||||||
buttonVariants({ variant: 'outline' }),
|
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',
|
!nextPost && 'pointer-events-none opacity-50 cursor-not-allowed',
|
||||||
)}
|
)}
|
||||||
aria-disabled={!nextPost}
|
aria-disabled={!nextPost}
|
||||||
|
@ -23,28 +23,27 @@ const { prevPost, nextPost } = Astro.props;
|
||||||
class="size-4 transition-transform group-hover:-translate-x-1"
|
class="size-4 transition-transform group-hover:-translate-x-1"
|
||||||
/>
|
/>
|
||||||
</div>
|
</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="text-muted-foreground text-left text-xs">Next Post</span>
|
||||||
<span class="w-full text-left text-sm text-pretty text-ellipsis"
|
<span class="w-full text-left text-sm text-ellipsis">
|
||||||
>{nextPost?.data.title || 'Latest post!'}</span
|
{nextPost?.data.title || 'Latest post!'}
|
||||||
>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href={prevPost ? `/blog/${prevPost.id}` : '#'}
|
href={prevPost ? `/blog/${prevPost.id}` : '#'}
|
||||||
class={cn(
|
class={cn(
|
||||||
buttonVariants({ variant: 'outline' }),
|
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',
|
!prevPost && 'pointer-events-none opacity-50 cursor-not-allowed',
|
||||||
)}
|
)}
|
||||||
aria-disabled={!prevPost}
|
aria-disabled={!prevPost}
|
||||||
>
|
>
|
||||||
<div class="flex flex-col items-end text-wrap">
|
<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="text-muted-foreground text-right text-xs">Previous Post</span>
|
||||||
>
|
<span class="w-full text-right text-sm text-ellipsis">
|
||||||
<span class="w-full text-right text-sm text-pretty text-ellipsis"
|
{prevPost?.data.title || 'Last post!'}
|
||||||
>{prevPost?.data.title || 'Last post!'}</span
|
</span>
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-2 flex-shrink-0">
|
<div class="ml-2 flex-shrink-0">
|
||||||
<Icon
|
<Icon
|
||||||
|
|
|
@ -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>
|
|
85
src/components/ThemeToggle.astro
Normal file
85
src/components/ThemeToggle.astro
Normal 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>
|
|
@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -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 Footer from "@/components/Footer.astro";
|
||||||
import Head from "@/components/Head.astro";
|
import Head from "@/components/Head.astro";
|
||||||
import Header from "@/components/Header.astro";
|
import Header from "@/components/Header.astro";
|
||||||
|
@ -19,7 +25,7 @@ const { title, description } = Astro.props;
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div
|
<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 />
|
<Header />
|
||||||
<main class="grow">
|
<main class="grow">
|
||||||
|
|
|
@ -86,7 +86,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.disable-transitions,
|
|
||||||
.disable-transitions * {
|
.disable-transitions * {
|
||||||
@apply transition-none!;
|
@apply transition-none!;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue