refactor: update site metadata structure
This commit is contained in:
parent
71d1df3bd7
commit
931bf7277c
15 changed files with 123 additions and 114 deletions
38
README.md
38
README.md
|
@ -94,28 +94,30 @@ This is a list of the various technologies used to build this template:
|
|||
|
||||
Edit the `src/consts.ts` file to update your site's metadata, navigation links, and social links:
|
||||
|
||||
```typescript
|
||||
```ts
|
||||
export const SITE: Site = {
|
||||
TITLE: 'astro-erudite',
|
||||
DESCRIPTION:
|
||||
title: 'astro-erudite',
|
||||
description:
|
||||
'astro-erudite is a opinionated, unstyled blogging template—built with Astro, Tailwind, and shadcn/ui.',
|
||||
EMAIL: 'jason@enscribe.dev',
|
||||
NUM_POSTS_ON_HOMEPAGE: 2,
|
||||
SITEURL: 'https://astro-erudite.vercel.app',
|
||||
href: 'https://astro-erudite.vercel.app',
|
||||
featuredPostCount: 2,
|
||||
postsPerPage: 3,
|
||||
}
|
||||
|
||||
export const NAV_LINKS: Link[] = [
|
||||
{ href: '/blog', label: 'blog' },
|
||||
{ href: '/authors', label: 'authors' },
|
||||
{ href: '/about', label: 'about' },
|
||||
{ href: '/tags', label: 'tags' },
|
||||
export const NAV_LINKS: SocialLink[] = [
|
||||
{
|
||||
href: '/blog',
|
||||
label: 'blog',
|
||||
},
|
||||
// ...
|
||||
]
|
||||
|
||||
export const SOCIAL_LINKS: Link[] = [
|
||||
{ href: 'https://github.com/jktrn', label: 'GitHub' },
|
||||
{ href: 'https://twitter.com/enscry', label: 'Twitter' },
|
||||
{ href: 'jason@enscribe.dev', label: 'Email' },
|
||||
{ href: '/rss.xml', label: 'RSS' },
|
||||
export const SOCIAL_LINKS: SocialLink[] = [
|
||||
{
|
||||
href: 'https://github.com/jktrn',
|
||||
label: 'GitHub',
|
||||
},
|
||||
// ...
|
||||
]
|
||||
```
|
||||
|
||||
|
@ -185,8 +187,8 @@ The blog post schema is defined as follows:
|
|||
|
||||
| Field | Type (Zod) | Requirements | Required |
|
||||
| ------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
|
||||
| `title` | `string` | Must be ≤60 characters. | Yes |
|
||||
| `description` | `string` | Must be ≤155 characters. | Yes |
|
||||
| `title` | `string` | Should be ≤60 characters. | Yes |
|
||||
| `description` | `string` | Should be ≤155 characters. | Yes |
|
||||
| `date` | `coerce.date()` | Must be in `YYYY-MM-DD` format. | Yes |
|
||||
| `image` | `image()` | Should be exactly 1200px × 630px. | Optional |
|
||||
| `tags` | `string[]` | Preferably use kebab-case for these. | Optional |
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
import Link from '@/components/Link.astro'
|
||||
import AvatarComponent from '@/components/ui/avatar'
|
||||
import type { Link as SocialLink } from '@/consts'
|
||||
import { cn } from '@/lib/utils'
|
||||
import type { SocialLink } from '@/types'
|
||||
import type { CollectionEntry } from 'astro:content'
|
||||
import SocialIcons from './SocialIcons.astro'
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ const { title, description, image = '/static/twitter-card.png' } = Astro.props
|
|||
<title>{title}</title>
|
||||
<meta name="title" content={title} />
|
||||
<meta name="description" content={description} />
|
||||
<meta name="author" content={SITE.TITLE} />
|
||||
<meta name="author" content={SITE.title} />
|
||||
|
||||
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
|
@ -44,7 +44,7 @@ const { title, description, image = '/static/twitter-card.png' } = Astro.props
|
|||
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content={Astro.url} />
|
||||
<meta property="og:site_name" content={SITE.TITLE} />
|
||||
<meta property="og:site_name" content={SITE.title} />
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:image" content={new URL(image, Astro.url)} />
|
||||
|
|
|
@ -19,7 +19,7 @@ import logo from '../../public/static/logo.svg'
|
|||
class="hover:text-primary flex shrink-0 items-center gap-2 text-xl font-medium transition-colors duration-300"
|
||||
>
|
||||
<Image src={logo} alt="Logo" class="size-8" />
|
||||
{SITE.TITLE}
|
||||
{SITE.title}
|
||||
</Link>
|
||||
<div class="flex items-center gap-2 md:gap-4">
|
||||
<nav class="hidden items-center gap-4 text-sm sm:gap-6 md:flex">
|
||||
|
|
|
@ -1,51 +1,37 @@
|
|||
---
|
||||
import Link from '@/components/Link.astro'
|
||||
import { buttonVariants } from '@/components/ui/button'
|
||||
import type { Link as SocialLink } from '@/consts'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { ICON_MAP } from '@/consts'
|
||||
import type { SocialLink } from '@/types'
|
||||
import { Icon } from 'astro-icon/components'
|
||||
|
||||
interface Props {
|
||||
links: SocialLink[]
|
||||
className?: string
|
||||
}
|
||||
|
||||
const { links, className } = Astro.props
|
||||
|
||||
const iconMap = {
|
||||
Website: 'lucide:globe',
|
||||
GitHub: 'lucide:github',
|
||||
LinkedIn: 'lucide:linkedin',
|
||||
Twitter: 'lucide:twitter',
|
||||
Email: 'lucide:mail',
|
||||
RSS: 'lucide:rss',
|
||||
}
|
||||
|
||||
const getSocialLink = ({ href, label }: SocialLink) => ({
|
||||
href: label === 'Email' ? `mailto:${href}` : href,
|
||||
ariaLabel: label,
|
||||
iconName:
|
||||
iconMap[label as keyof typeof iconMap] || 'lucide:message-circle-question',
|
||||
})
|
||||
const { links } = Astro.props
|
||||
---
|
||||
|
||||
<ul class={cn('flex flex-wrap gap-2', className)} role="list">
|
||||
<ul class="flex flex-wrap gap-2" role="list">
|
||||
{
|
||||
links.map((link) => {
|
||||
const { href, ariaLabel, iconName } = getSocialLink(link)
|
||||
return (
|
||||
links.map(({ href, label }) => (
|
||||
<li>
|
||||
<Link
|
||||
href={href}
|
||||
aria-label={ariaLabel}
|
||||
title={ariaLabel}
|
||||
aria-label={label}
|
||||
title={label}
|
||||
class={buttonVariants({ variant: 'outline', size: 'icon' })}
|
||||
external
|
||||
>
|
||||
<Icon name={iconName} class="size-4" />
|
||||
<Icon
|
||||
name={
|
||||
ICON_MAP[label as keyof typeof ICON_MAP] ||
|
||||
'lucide:message-circle-question'
|
||||
}
|
||||
class="size-4"
|
||||
/>
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
|
|
|
@ -1,37 +1,53 @@
|
|||
export type Site = {
|
||||
TITLE: string
|
||||
DESCRIPTION: string
|
||||
EMAIL: string
|
||||
NUM_POSTS_ON_HOMEPAGE: number
|
||||
POSTS_PER_PAGE: number
|
||||
SITEURL: string
|
||||
}
|
||||
|
||||
export type Link = {
|
||||
href: string
|
||||
label: string
|
||||
}
|
||||
import type { IconMap, SocialLink, Site } from '@/types'
|
||||
|
||||
export const SITE: Site = {
|
||||
TITLE: 'astro-erudite',
|
||||
DESCRIPTION:
|
||||
title: 'astro-erudite',
|
||||
description:
|
||||
'astro-erudite is a opinionated, unstyled blogging template—built with Astro, Tailwind, and shadcn/ui.',
|
||||
EMAIL: 'jason@enscribe.dev',
|
||||
NUM_POSTS_ON_HOMEPAGE: 2,
|
||||
POSTS_PER_PAGE: 3,
|
||||
SITEURL: 'https://astro-erudite.vercel.app',
|
||||
href: 'https://astro-erudite.vercel.app',
|
||||
featuredPostCount: 2,
|
||||
postsPerPage: 3,
|
||||
}
|
||||
|
||||
export const NAV_LINKS: Link[] = [
|
||||
{ href: '/blog', label: 'blog' },
|
||||
{ href: '/authors', label: 'authors' },
|
||||
{ href: '/about', label: 'about' },
|
||||
{ href: '/tags', label: 'tags' },
|
||||
export const NAV_LINKS: SocialLink[] = [
|
||||
{
|
||||
href: '/blog',
|
||||
label: 'blog',
|
||||
},
|
||||
{
|
||||
href: '/authors',
|
||||
label: 'authors',
|
||||
},
|
||||
{
|
||||
href: '/about',
|
||||
label: 'about',
|
||||
},
|
||||
]
|
||||
|
||||
export const SOCIAL_LINKS: Link[] = [
|
||||
{ href: 'https://github.com/jktrn', label: 'GitHub' },
|
||||
{ href: 'https://twitter.com/enscry', label: 'Twitter' },
|
||||
{ href: 'jason@enscribe.dev', label: 'Email' },
|
||||
{ href: '/rss.xml', label: 'RSS' },
|
||||
export const SOCIAL_LINKS: SocialLink[] = [
|
||||
{
|
||||
href: 'https://github.com/jktrn',
|
||||
label: 'GitHub',
|
||||
},
|
||||
{
|
||||
href: 'https://twitter.com/enscry',
|
||||
label: 'Twitter',
|
||||
},
|
||||
{
|
||||
href: 'mailto:jason@enscribe.dev',
|
||||
label: 'Email',
|
||||
},
|
||||
{
|
||||
href: '/rss.xml',
|
||||
label: 'RSS',
|
||||
},
|
||||
]
|
||||
|
||||
export const ICON_MAP: IconMap = {
|
||||
Website: 'lucide:globe',
|
||||
GitHub: 'lucide:github',
|
||||
LinkedIn: 'lucide:linkedin',
|
||||
Twitter: 'lucide:twitter',
|
||||
Email: 'lucide:mail',
|
||||
RSS: 'lucide:rss',
|
||||
}
|
||||
|
|
|
@ -5,18 +5,8 @@ const blog = defineCollection({
|
|||
loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
|
||||
schema: ({ image }) =>
|
||||
z.object({
|
||||
title: z
|
||||
.string()
|
||||
.max(
|
||||
60,
|
||||
'Title should be 60 characters or less for optimal Open Graph display.',
|
||||
),
|
||||
description: z
|
||||
.string()
|
||||
.max(
|
||||
155,
|
||||
'Description should be 155 characters or less for optimal Open Graph display.',
|
||||
),
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
date: z.coerce.date(),
|
||||
image: image().optional(),
|
||||
tags: z.array(z.string()).optional(),
|
||||
|
|
|
@ -17,7 +17,7 @@ const { title, description, image } = Astro.props
|
|||
<html lang="en">
|
||||
<head>
|
||||
<Head
|
||||
title={`${title} | ${SITE.TITLE}`}
|
||||
title={`${title} | ${SITE.title}`}
|
||||
description={description}
|
||||
image={image}
|
||||
/>
|
||||
|
|
|
@ -8,7 +8,7 @@ import Layout from '@/layouts/Layout.astro'
|
|||
import { cn } from '@/lib/utils'
|
||||
---
|
||||
|
||||
<Layout title="404" description={SITE.DESCRIPTION}>
|
||||
<Layout title="404" description={SITE.description}>
|
||||
<Container class="flex grow flex-col gap-y-6">
|
||||
<Breadcrumbs items={[{ label: '???', icon: 'lucide:circle-help' }]} />
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import { getCollection } from 'astro:content'
|
|||
const projects = await getCollection('projects')
|
||||
---
|
||||
|
||||
<Layout title="About" description={SITE.DESCRIPTION}>
|
||||
<Layout title="About" description={SITE.description}>
|
||||
<Container class="flex flex-col gap-y-6">
|
||||
<Breadcrumbs items={[{ label: 'About', icon: 'lucide:info' }]} />
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ export async function getStaticPaths({
|
|||
const allPosts = await getCollection('blog', ({ data }) => !data.draft)
|
||||
return paginate(
|
||||
allPosts.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf()),
|
||||
{ pageSize: SITE.POSTS_PER_PAGE },
|
||||
{ pageSize: SITE.postsPerPage },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -10,10 +10,10 @@ import { getCollection } from 'astro:content'
|
|||
const blog = (await getCollection('blog'))
|
||||
.filter((post) => !post.data.draft)
|
||||
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf())
|
||||
.slice(0, SITE.NUM_POSTS_ON_HOMEPAGE)
|
||||
.slice(0, SITE.featuredPostCount)
|
||||
---
|
||||
|
||||
<Layout title="Home" description={SITE.DESCRIPTION}>
|
||||
<Layout title="Home" description={SITE.description}>
|
||||
<Container class="flex flex-col gap-y-6">
|
||||
<section>
|
||||
<div class="rounded-lg border">
|
||||
|
|
|
@ -9,9 +9,9 @@ export async function GET(context: APIContext) {
|
|||
posts.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf())
|
||||
|
||||
return rss({
|
||||
title: SITE.TITLE,
|
||||
description: SITE.DESCRIPTION,
|
||||
site: context.site ?? SITE.SITEURL,
|
||||
title: SITE.title,
|
||||
description: SITE.description,
|
||||
site: context.site ?? SITE.href,
|
||||
items: posts.map((post) => ({
|
||||
title: post.data.title,
|
||||
description: post.data.description,
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
@font-face {
|
||||
font-family: 'Geist';
|
||||
src: url('/fonts/GeistVF.woff2') format('woff2-variations');
|
||||
font-weight: 100 500;
|
||||
font-weight: 100 900;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
@ -47,7 +47,7 @@
|
|||
@font-face {
|
||||
font-family: 'Geist Mono';
|
||||
src: url('/fonts/GeistMonoVF.woff2') format('woff2-variations');
|
||||
font-weight: 100 600;
|
||||
font-weight: 100 900;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
@ -94,11 +94,10 @@
|
|||
}
|
||||
|
||||
html {
|
||||
color-scheme: light;
|
||||
@apply bg-background text-foreground;
|
||||
@apply bg-background text-foreground scheme-light;
|
||||
|
||||
&.dark {
|
||||
color-scheme: dark;
|
||||
@apply scheme-dark;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
|
|
16
src/types.ts
Normal file
16
src/types.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
export type Site = {
|
||||
title: string
|
||||
description: string
|
||||
href: string
|
||||
featuredPostCount: number
|
||||
postsPerPage: number
|
||||
}
|
||||
|
||||
export type SocialLink = {
|
||||
href: string
|
||||
label: string
|
||||
}
|
||||
|
||||
export type IconMap = {
|
||||
[key: string]: string
|
||||
}
|
Loading…
Add table
Reference in a new issue