chore: layout
This commit is contained in:
parent
b9561ad2d0
commit
230dca64ca
27 changed files with 446 additions and 339 deletions
|
@ -2,4 +2,4 @@
|
|||
|
||||
---
|
||||
|
||||
<div class="mx-auto max-w-screen-md px-3"><slot /></div>
|
||||
<div class="mx-auto max-w-screen-md"><slot /></div>
|
||||
|
|
|
@ -5,7 +5,7 @@ import BackToTop from '@components/BackToTop.astro'
|
|||
import SocialIcons from './SocialIcons.astro'
|
||||
---
|
||||
|
||||
<footer class="animate">
|
||||
<footer>
|
||||
<Container>
|
||||
<div class="my-2 flex justify-between">
|
||||
<SocialIcons icon_size={'text-xl'} />
|
||||
|
@ -16,78 +16,6 @@ import SocialIcons from './SocialIcons.astro'
|
|||
© {new Date().getFullYear()} • {SITE.TITLE} 👀<br />
|
||||
Built with Astro
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-1.5">
|
||||
<button
|
||||
id="light-theme-button"
|
||||
aria-label="Light theme"
|
||||
class="group flex size-9 items-center justify-center rounded border border-black/15 hover:bg-black/5 focus-visible:bg-black/5 dark:border-white/20 dark:hover:bg-white/5 dark:focus-visible:bg-white/5"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="transition-colors duration-300 ease-in-out group-hover:animate-pulse group-hover:stroke-black group-focus-visible:animate-pulse group-focus-visible:stroke-black group-hover:dark:stroke-white dark:group-focus-visible:stroke-white"
|
||||
>
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
id="dark-theme-button"
|
||||
aria-label="Dark theme"
|
||||
class="group flex size-9 items-center justify-center rounded border border-black/15 hover:bg-black/5 focus-visible:bg-black/5 dark:border-white/20 dark:hover:bg-white/5 dark:focus-visible:bg-white/5"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="transition-colors duration-300 ease-in-out group-hover:animate-pulse group-hover:stroke-black group-focus-visible:animate-pulse group-focus-visible:stroke-black group-hover:dark:stroke-white dark:group-focus-visible:stroke-white"
|
||||
>
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
id="system-theme-button"
|
||||
aria-label="System theme"
|
||||
class="group flex size-9 items-center justify-center rounded border border-black/15 hover:bg-black/5 focus-visible:bg-black/5 dark:border-white/20 dark:hover:bg-white/5 dark:focus-visible:bg-white/5"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="transition-colors duration-300 ease-in-out group-hover:animate-pulse group-hover:stroke-black group-focus-visible:animate-pulse group-focus-visible:stroke-black group-hover:dark:stroke-white dark:group-focus-visible:stroke-white"
|
||||
>
|
||||
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
|
||||
<line x1="8" y1="21" x2="16" y2="21"></line>
|
||||
<line x1="12" y1="17" x2="12" y2="21"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</footer>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
import '../styles/global.css'
|
||||
import { ViewTransitions } from 'astro:transitions'
|
||||
|
||||
import '@fontsource/geist-sans'
|
||||
import '@fontsource/geist-mono'
|
||||
|
@ -16,46 +15,31 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site)
|
|||
const { title, description, image = '/blog-placeholder-1.jpg' } = Astro.props
|
||||
---
|
||||
|
||||
<!-- Global Metadata -->
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link
|
||||
rel="icon"
|
||||
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🍄</text></svg>"
|
||||
/>
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
|
||||
<!-- Canonical URL -->
|
||||
<link rel="canonical" href={canonicalURL} />
|
||||
|
||||
<!-- Primary Meta Tags -->
|
||||
<title>{title}</title>
|
||||
<meta name="title" content={title} />
|
||||
<meta name="description" content={description} />
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content={Astro.url} />
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:image" content={new URL(image, Astro.url)} />
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:url" content={Astro.url} />
|
||||
<meta property="twitter:title" content={title} />
|
||||
<meta property="twitter:description" content={description} />
|
||||
<meta property="twitter:image" content={new URL(image, Astro.url)} />
|
||||
|
||||
<ViewTransitions />
|
||||
|
||||
<script is:inline>
|
||||
function init() {
|
||||
preloadTheme()
|
||||
onScroll()
|
||||
animate()
|
||||
updateThemeButtons()
|
||||
addCopyCodeButtons()
|
||||
|
||||
const backToTop = document.getElementById('back-to-top')
|
||||
|
@ -64,77 +48,9 @@ const { title, description, image = '/blog-placeholder-1.jpg' } = Astro.props
|
|||
const backToPrev = document.getElementById('back-to-prev')
|
||||
backToPrev?.addEventListener('click', () => window.history.back())
|
||||
|
||||
const lightThemeButton = document.getElementById('light-theme-button')
|
||||
lightThemeButton?.addEventListener('click', () => {
|
||||
localStorage.setItem('theme', 'light')
|
||||
toggleTheme(false)
|
||||
updateThemeButtons()
|
||||
})
|
||||
|
||||
const darkThemeButton = document.getElementById('dark-theme-button')
|
||||
darkThemeButton?.addEventListener('click', () => {
|
||||
localStorage.setItem('theme', 'dark')
|
||||
toggleTheme(true)
|
||||
updateThemeButtons()
|
||||
})
|
||||
|
||||
const systemThemeButton = document.getElementById('system-theme-button')
|
||||
systemThemeButton?.addEventListener('click', () => {
|
||||
localStorage.setItem('theme', 'system')
|
||||
toggleTheme(window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||
updateThemeButtons()
|
||||
})
|
||||
|
||||
window
|
||||
.matchMedia('(prefers-color-scheme: dark)')
|
||||
.addEventListener('change', (event) => {
|
||||
if (localStorage.theme === 'system') {
|
||||
toggleTheme(event.matches)
|
||||
}
|
||||
})
|
||||
|
||||
document.addEventListener('scroll', onScroll)
|
||||
}
|
||||
|
||||
function updateThemeButtons() {
|
||||
const theme = localStorage.getItem('theme')
|
||||
const lightThemeButton = document.getElementById('light-theme-button')
|
||||
const darkThemeButton = document.getElementById('dark-theme-button')
|
||||
const systemThemeButton = document.getElementById('system-theme-button')
|
||||
|
||||
function removeActiveButtonTheme(button) {
|
||||
button?.classList.remove('bg-black/5')
|
||||
button?.classList.remove('dark:bg-white/5')
|
||||
}
|
||||
|
||||
function addActiveButtonTheme(button) {
|
||||
button?.classList.add('bg-black/5')
|
||||
button?.classList.add('dark:bg-white/5')
|
||||
}
|
||||
|
||||
removeActiveButtonTheme(lightThemeButton)
|
||||
removeActiveButtonTheme(darkThemeButton)
|
||||
removeActiveButtonTheme(systemThemeButton)
|
||||
|
||||
if (theme === 'light') {
|
||||
addActiveButtonTheme(lightThemeButton)
|
||||
} else if (theme === 'dark') {
|
||||
addActiveButtonTheme(darkThemeButton)
|
||||
} else {
|
||||
addActiveButtonTheme(systemThemeButton)
|
||||
}
|
||||
}
|
||||
|
||||
function animate() {
|
||||
const animateElements = document.querySelectorAll('.animate')
|
||||
|
||||
animateElements.forEach((element, index) => {
|
||||
setTimeout(() => {
|
||||
element.classList.add('show')
|
||||
}, index * 100)
|
||||
})
|
||||
}
|
||||
|
||||
function onScroll() {
|
||||
if (window.scrollY > 0) {
|
||||
document.documentElement.classList.add('scrolled')
|
||||
|
@ -151,44 +67,6 @@ const { title, description, image = '/blog-placeholder-1.jpg' } = Astro.props
|
|||
})
|
||||
}
|
||||
|
||||
function toggleTheme(dark) {
|
||||
const css = document.createElement('style')
|
||||
|
||||
css.appendChild(
|
||||
document.createTextNode(
|
||||
`* {
|
||||
-webkit-transition: none !important;
|
||||
-moz-transition: none !important;
|
||||
-o-transition: none !important;
|
||||
-ms-transition: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
`,
|
||||
),
|
||||
)
|
||||
|
||||
document.head.appendChild(css)
|
||||
|
||||
if (dark) {
|
||||
document.documentElement.classList.add('dark')
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
|
||||
window.getComputedStyle(css).opacity
|
||||
document.head.removeChild(css)
|
||||
}
|
||||
|
||||
function preloadTheme() {
|
||||
const userTheme = localStorage.theme
|
||||
|
||||
if (userTheme === 'light' || userTheme === 'dark') {
|
||||
toggleTheme(true) // set default to dark theme
|
||||
} else {
|
||||
toggleTheme(window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||
}
|
||||
}
|
||||
|
||||
function addCopyCodeButtons() {
|
||||
let copyButtonLabel = '📋'
|
||||
let codeBlocks = Array.from(document.querySelectorAll('pre'))
|
||||
|
@ -228,5 +106,4 @@ const { title, description, image = '/blog-placeholder-1.jpg' } = Astro.props
|
|||
|
||||
document.addEventListener('DOMContentLoaded', () => init())
|
||||
document.addEventListener('astro:after-swap', () => init())
|
||||
preloadTheme()
|
||||
</script>
|
||||
|
|
|
@ -2,33 +2,30 @@
|
|||
import Container from '@components/Container.astro'
|
||||
import Link from '@components/Link.astro'
|
||||
import { SITE } from '@consts'
|
||||
|
||||
const items = [
|
||||
{ href: '/blog', label: 'blog' },
|
||||
{ href: '/authors', label: 'authors' },
|
||||
{ href: '/about', label: 'about' },
|
||||
{ href: '/tags', label: 'tags' },
|
||||
]
|
||||
---
|
||||
|
||||
<header transition:persist>
|
||||
<header class="sticky top-0 z-10" transition:persist>
|
||||
<Container>
|
||||
<div class="flex flex-wrap justify-between gap-y-2">
|
||||
<div class="flex items-center justify-between py-4">
|
||||
<Link href="/" underline={false}>
|
||||
<div class="font-semibold">
|
||||
{SITE.TITLE} 🍄
|
||||
</div>
|
||||
{SITE.TITLE}<span class="ml-1">🍄</span>
|
||||
</Link>
|
||||
<nav class="flex items-center gap-1 text-sm">
|
||||
<Link href="/blog">blog</Link>
|
||||
<span>
|
||||
{`/`}
|
||||
</span>
|
||||
<Link href="/authors">authors</Link>
|
||||
<span>
|
||||
{`/`}
|
||||
</span>
|
||||
<Link href="/about">about</Link>
|
||||
<span>
|
||||
{`/`}
|
||||
</span>
|
||||
<Link href="/tags">tags</Link>
|
||||
<span>
|
||||
{`/`}
|
||||
</span>
|
||||
<nav class="flex items-center space-x-1 text-sm">
|
||||
{
|
||||
items.map((item, index) => (
|
||||
<>
|
||||
<Link href={item.href}>{item.label}</Link>
|
||||
{index < items.length - 1 && <span class="text-gray-400">/</span>}
|
||||
</>
|
||||
))
|
||||
}
|
||||
</nav>
|
||||
</div>
|
||||
</Container>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
---
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import { Image } from "astro:assets";
|
||||
import type { CollectionEntry } from 'astro:content'
|
||||
import { Image } from 'astro:assets'
|
||||
|
||||
type Props = {
|
||||
member: CollectionEntry<"authors">;
|
||||
};
|
||||
member: CollectionEntry<'authors'>
|
||||
}
|
||||
|
||||
const { member } = Astro.props;
|
||||
const { name, avatar, bio } = member.data;
|
||||
const { member } = Astro.props
|
||||
const { name, avatar, bio } = member.data
|
||||
---
|
||||
|
||||
<div
|
||||
class="animate not-prose flex flex-col sm:flex-row size-full sm:items-center gap-4 overflow-hidden rounded-xl border border-foreground bg-background p-6 hover:bg-secondary"
|
||||
class="not-prose flex size-full flex-col gap-4 overflow-hidden rounded-xl border border-foreground bg-background p-6 hover:bg-secondary sm:flex-row sm:items-center"
|
||||
>
|
||||
<Image
|
||||
src={avatar}
|
||||
|
|
|
@ -27,10 +27,7 @@ function buildToc(headings: Heading[]) {
|
|||
}
|
||||
---
|
||||
|
||||
<details
|
||||
open
|
||||
class="animate rounded-lg border border-black/15 dark:border-white/20"
|
||||
>
|
||||
<details open class="rounded-lg border border-black/15 dark:border-white/20">
|
||||
<summary>Table of Contents</summary>
|
||||
<nav class="">
|
||||
<ul class="py-3">
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
name: "enscribe"
|
||||
avatar: "https://gravatar.com/avatar/9bfdc4ec972793cf05cb91efce5f4aaaec2a0da1bf4ec34dad0913f1d845faf6.webp?size=256"
|
||||
bio: "d(-_-)b"
|
||||
website: "https://enscribe.dev"
|
||||
twitter: "enscry"
|
||||
github: "jktrn"
|
||||
---
|
||||
name: 'enscribe'
|
||||
avatar: 'https://gravatar.com/avatar/9bfdc4ec972793cf05cb91efce5f4aaaec2a0da1bf4ec34dad0913f1d845faf6.webp?size=256'
|
||||
bio: 'd(-_-)b'
|
||||
website: 'https://enscribe.dev'
|
||||
twitter: 'enscry'
|
||||
github: 'jktrn'
|
||||
---
|
||||
|
|
|
@ -62,7 +62,7 @@ When developing you can continue to use `npm run dev` and Pagefind will use the
|
|||
|
||||
## UI enhancements 🎨
|
||||
|
||||
- Elements are styled and animate on focus
|
||||
- Elements are styled and on focus
|
||||
- Increased contrast in light mode
|
||||
- Active theme is indicated by theme buttons
|
||||
- Separate syntax highlight themes for light and dark mode
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { defineCollection, reference, z } from "astro:content";
|
||||
import { defineCollection, reference, z } from 'astro:content'
|
||||
|
||||
const blog = defineCollection({
|
||||
type: "content",
|
||||
type: 'content',
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
|
@ -9,12 +9,12 @@ const blog = defineCollection({
|
|||
draft: z.boolean().optional(),
|
||||
|
||||
tags: z.array(z.string()).optional(),
|
||||
author: z.union([reference("authors"), z.string()]).optional(),
|
||||
author: z.union([reference('authors'), z.string()]).optional(),
|
||||
}),
|
||||
});
|
||||
})
|
||||
|
||||
const authors = defineCollection({
|
||||
type: "content",
|
||||
type: 'content',
|
||||
schema: z.object({
|
||||
name: z.string(),
|
||||
avatar: z.string().url(),
|
||||
|
@ -26,6 +26,6 @@ const authors = defineCollection({
|
|||
mail: z.string().email().optional(),
|
||||
discord: z.string().optional(),
|
||||
}),
|
||||
});
|
||||
})
|
||||
|
||||
export const collections = { blog, authors };
|
||||
export const collections = { blog, authors }
|
||||
|
|
|
@ -4,6 +4,7 @@ import Header from '@components/Header.astro'
|
|||
import Footer from '@components/Footer.astro'
|
||||
import { SITE } from '@consts'
|
||||
import '../styles/katex.css'
|
||||
import { ViewTransitions } from 'astro:transitions'
|
||||
|
||||
type Props = {
|
||||
title: string
|
||||
|
@ -17,10 +18,13 @@ const { title, description } = Astro.props
|
|||
<html lang="en">
|
||||
<head>
|
||||
<Head title={`${title} | ${SITE.TITLE}`} description={description} />
|
||||
<ViewTransitions />
|
||||
</head>
|
||||
<body>
|
||||
<body
|
||||
class="box-border flex h-fit min-h-screen flex-col px-4 font-sans antialiased"
|
||||
>
|
||||
<Header />
|
||||
<main>
|
||||
<main class="flex-grow">
|
||||
<slot />
|
||||
</main>
|
||||
<Footer />
|
||||
|
|
|
@ -9,10 +9,10 @@ import { SITE } from '@consts'
|
|||
<Layout title="404" description={SITE.DESCRIPTION}>
|
||||
<Container>
|
||||
<div class="mt-16 grid place-items-center gap-3">
|
||||
<h4 class="animate text-2xl font-semibold text-black dark:text-white">
|
||||
<h4 class="text-2xl font-semibold text-black dark:text-white">
|
||||
404: Page not found
|
||||
</h4>
|
||||
<span class="animate">
|
||||
<span>
|
||||
<BackToPrevious href="/">Go to home page</BackToPrevious>
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -8,8 +8,8 @@ import { Image } from 'astro:assets'
|
|||
<Layout title="About" description="About">
|
||||
<Container>
|
||||
<div class="space-y-10">
|
||||
<div class="animate font-semibold text-black dark:text-white">About</div>
|
||||
<section class="animate not-prose flex flex-col gap-4 text-justify">
|
||||
<div class="font-semibold text-black dark:text-white">About</div>
|
||||
<section class="not-prose flex flex-col gap-4 text-justify">
|
||||
<p class="text-justify">
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolores porro
|
||||
hic minima incidunt explicabo obcaecati consectetur consequuntur at
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
---
|
||||
import { type CollectionEntry, getCollection, getEntry } from "astro:content";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import MemberCard from "@components/MemberCard.astro";
|
||||
import { type CollectionEntry, getCollection, getEntry } from 'astro:content'
|
||||
import Layout from '@layouts/Layout.astro'
|
||||
import MemberCard from '@components/MemberCard.astro'
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const authors = await getCollection("authors");
|
||||
const authors = await getCollection('authors')
|
||||
return authors.map((member) => ({
|
||||
params: { slug: member.slug },
|
||||
props: { member },
|
||||
}));
|
||||
}))
|
||||
}
|
||||
|
||||
type Props = {
|
||||
member: CollectionEntry<"authors">;
|
||||
};
|
||||
member: CollectionEntry<'authors'>
|
||||
}
|
||||
|
||||
const { member } = Astro.props;
|
||||
const { member } = Astro.props
|
||||
|
||||
const allPosts = await getCollection("blog");
|
||||
const allPosts = await getCollection('blog')
|
||||
const memberPosts = allPosts
|
||||
.filter((post) => {
|
||||
if (typeof post.data.author === 'string') {
|
||||
return post.data.author === member.data.name && !post.data.draft;
|
||||
return post.data.author === member.data.name && !post.data.draft
|
||||
} else if (post.data.author && 'slug' in post.data.author) {
|
||||
return post.data.author.slug === member.slug && !post.data.draft;
|
||||
return post.data.author.slug === member.slug && !post.data.draft
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
})
|
||||
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
|
||||
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf())
|
||||
---
|
||||
|
||||
<Layout
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import MemberCard from "@components/MemberCard.astro";
|
||||
import { getCollection } from 'astro:content'
|
||||
import Layout from '@layouts/Layout.astro'
|
||||
import MemberCard from '@components/MemberCard.astro'
|
||||
|
||||
const authors = await getCollection("authors");
|
||||
const authors = await getCollection('authors')
|
||||
---
|
||||
|
||||
<Layout title="authors" description="authors">
|
||||
<section>
|
||||
<ul
|
||||
class="animate not-prose grid grid-cols-1 gap-4 lg:grid-cols-2 xl:grid-cols-3"
|
||||
>
|
||||
<ul class="not-prose grid grid-cols-1 gap-4 lg:grid-cols-2 xl:grid-cols-3">
|
||||
{
|
||||
authors.map((member) => (
|
||||
<li>
|
||||
|
|
|
@ -53,7 +53,7 @@ const { Content, headings } = await post.render()
|
|||
<BackToPrevious href="/blog">Back to blog</BackToPrevious>
|
||||
</div>
|
||||
<div class="my-10 space-y-4">
|
||||
<div class="animate flex items-center gap-1.5">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<div class="font-base text-sm">
|
||||
<FormattedDate date={post.data.date} />
|
||||
</div>
|
||||
|
@ -62,7 +62,7 @@ const { Content, headings } = await post.render()
|
|||
{readingTime(post.body)}
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="animate text-4xl font-semibold text-black dark:text-white">
|
||||
<h1 class="text-4xl font-semibold text-black dark:text-white">
|
||||
{post.data.title}
|
||||
</h1>
|
||||
<div class="font-base text-sm">
|
||||
|
|
|
@ -30,7 +30,7 @@ const years = Object.keys(posts).sort((a, b) => parseInt(b) - parseInt(a))
|
|||
<div class="space-y-4">
|
||||
{
|
||||
years.map((year) => (
|
||||
<section class="animate space-y-4">
|
||||
<section class="space-y-4">
|
||||
<div class="font-semibold text-black dark:text-white">{year}</div>
|
||||
<div>
|
||||
<ul class="not-prose flex flex-col gap-4">
|
||||
|
|
|
@ -16,7 +16,7 @@ const blog = (await getCollection('blog'))
|
|||
|
||||
<Layout title="Home" description="Home">
|
||||
<Container>
|
||||
<section class="animate space-y-6">
|
||||
<section class="space-y-6">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<h2 class="font-semibold text-black dark:text-white">Latest posts</h2>
|
||||
<Link href="/blog"> See all posts </Link>
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
import rss from '@astrojs/rss'
|
||||
import { SITE } from '@consts'
|
||||
import { getCollection } from 'astro:content'
|
||||
import type { APIContext } from 'astro'
|
||||
import { getCollection } from 'astro:content'
|
||||
|
||||
export async function GET(context: APIContext) {
|
||||
try {
|
||||
const blog = (await getCollection('blog')).filter(
|
||||
(post) => !post.data.draft
|
||||
(post) => !post.data.draft,
|
||||
)
|
||||
|
||||
// Filter posts by tag 'rss-feed'
|
||||
const filteredBlogs = blog.filter(
|
||||
(post) => post.data.tags && post.data.tags.includes('rss-feed')
|
||||
(post) => post.data.tags && post.data.tags.includes('rss-feed'),
|
||||
)
|
||||
|
||||
// Sort posts by date
|
||||
const items = [...filteredBlogs].sort(
|
||||
(a, b) =>
|
||||
new Date(b.data.date).valueOf() - new Date(a.data.date).valueOf()
|
||||
new Date(b.data.date).valueOf() - new Date(a.data.date).valueOf(),
|
||||
)
|
||||
|
||||
// Return RSS feed
|
||||
|
|
|
@ -36,7 +36,7 @@ export async function getStaticPaths() {
|
|||
>
|
||||
<Container>
|
||||
<div class="space-y-10">
|
||||
<div class="animate font-semibold text-black dark:text-white">
|
||||
<div class="font-semibold text-black dark:text-white">
|
||||
Tag: <span
|
||||
class="mx-2 rounded-full bg-orange-300 px-3 py-2 transition-colors duration-300 ease-in-out hover:bg-cyan-200 dark:bg-orange-500 dark:hover:bg-cyan-500"
|
||||
>#{tag}</span
|
||||
|
@ -45,7 +45,7 @@ export async function getStaticPaths() {
|
|||
<div class="space-y-4">
|
||||
{
|
||||
posts.map((post) => (
|
||||
<section class="animate space-y-4">
|
||||
<section class="space-y-4">
|
||||
<div>
|
||||
<ul class="not-prose flex flex-col gap-4">
|
||||
<li>
|
||||
|
|
|
@ -14,7 +14,7 @@ const tags = blog
|
|||
<Layout title="Tags" description="Tags">
|
||||
<Container>
|
||||
<div class="space-y-10">
|
||||
<div class="animate font-semibold text-black dark:text-white">Tags</div>
|
||||
<div class="font-semibold text-black dark:text-white">Tags</div>
|
||||
<ul class="flex flex-wrap">
|
||||
{
|
||||
tags.map((tag) => (
|
||||
|
|
|
@ -2,42 +2,6 @@
|
|||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
html {
|
||||
overflow-y: auto;
|
||||
color-scheme: light;
|
||||
scroll-padding-top: 100px;
|
||||
}
|
||||
|
||||
html.dark {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
@apply size-full;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply font-sans antialiased;
|
||||
@apply flex flex-col;
|
||||
@apply bg-neutral-300 dark:bg-slate-900;
|
||||
@apply text-black/75 dark:text-white/75;
|
||||
}
|
||||
|
||||
header {
|
||||
@apply fixed left-0 right-0 top-0 z-50 py-6;
|
||||
@apply bg-neutral-100/75 dark:bg-neutral-900/75;
|
||||
@apply saturate-200 backdrop-blur-sm;
|
||||
}
|
||||
|
||||
main {
|
||||
@apply flex-1 py-32;
|
||||
}
|
||||
|
||||
footer {
|
||||
@apply py-6 text-sm;
|
||||
}
|
||||
|
||||
article {
|
||||
@apply prose prose-neutral max-w-full dark:prose-invert prose-img:mx-auto prose-img:my-auto;
|
||||
@apply prose-headings:font-semibold;
|
||||
|
@ -61,15 +25,6 @@ article {
|
|||
scrollbar-color: #1e293b #0f172a;
|
||||
}
|
||||
|
||||
.animate {
|
||||
@apply -translate-y-3 opacity-0;
|
||||
@apply transition-all duration-300 ease-out;
|
||||
}
|
||||
|
||||
.animate.show {
|
||||
@apply translate-y-0 opacity-100;
|
||||
}
|
||||
|
||||
html #back-to-top {
|
||||
@apply pointer-events-none opacity-0;
|
||||
}
|
||||
|
@ -123,3 +78,65 @@ pre {
|
|||
.copy-code:active {
|
||||
@apply scale-90 transition-transform;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 0 0% 3.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 0 0% 3.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 0 0% 3.9%;
|
||||
--primary: 0 0% 9%;
|
||||
--primary-foreground: 0 0% 98%;
|
||||
--secondary: 0 0% 96.1%;
|
||||
--secondary-foreground: 0 0% 9%;
|
||||
--muted: 0 0% 96.1%;
|
||||
--muted-foreground: 0 0% 45.1%;
|
||||
--accent: 0 0% 96.1%;
|
||||
--accent-foreground: 0 0% 9%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 0 0% 89.8%;
|
||||
--input: 0 0% 89.8%;
|
||||
--ring: 0 0% 3.9%;
|
||||
--chart-1: 12 76% 61%;
|
||||
--chart-2: 173 58% 39%;
|
||||
--chart-3: 197 37% 24%;
|
||||
--chart-4: 43 74% 66%;
|
||||
--chart-5: 27 87% 67%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.dark {
|
||||
--background: 0 0% 3.9%;
|
||||
--foreground: 0 0% 98%;
|
||||
--card: 0 0% 3.9%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
--popover: 0 0% 3.9%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
--primary: 0 0% 98%;
|
||||
--primary-foreground: 0 0% 9%;
|
||||
--secondary: 0 0% 14.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
--muted: 0 0% 14.9%;
|
||||
--muted-foreground: 0 0% 63.9%;
|
||||
--accent: 0 0% 14.9%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 0 0% 14.9%;
|
||||
--input: 0 0% 14.9%;
|
||||
--ring: 0 0% 83.1%;
|
||||
--chart-1: 220 70% 50%;
|
||||
--chart-2: 160 60% 45%;
|
||||
--chart-3: 30 80% 55%;
|
||||
--chart-4: 280 65% 60%;
|
||||
--chart-5: 340 75% 55%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue