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"
|
||||
>
|
||||
<div class="flex items-center gap-x-2">
|
||||
<span class="text-muted-foreground text-center text-sm">
|
||||
© {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">
|
||||
© {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} />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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
|
||||
|
|
|
@ -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 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">
|
||||
|
|
|
@ -86,7 +86,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.disable-transitions,
|
||||
.disable-transitions * {
|
||||
@apply transition-none!;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue