feat: opengraph design
This commit is contained in:
parent
0b430e5d43
commit
c410c499e1
38 changed files with 179 additions and 66 deletions
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
import type { CollectionEntry } from 'astro:content'
|
||||
import { Image } from 'astro:assets'
|
||||
import Link from '@components/Link.astro'
|
||||
import AvatarComponent from '@/components/ui/avatar'
|
||||
|
||||
type Props = {
|
||||
author: CollectionEntry<'authors'>
|
||||
|
@ -15,12 +15,12 @@ const { name, avatar, bio } = author.data
|
|||
class="rounded-xl border p-4 transition-colors duration-300 ease-in-out hover:bg-secondary/50"
|
||||
>
|
||||
<Link href={`/authors/${author.slug}`} class="flex flex-wrap gap-4">
|
||||
<Image
|
||||
<AvatarComponent
|
||||
client:load
|
||||
src={avatar}
|
||||
alt={`Avatar of ${name}`}
|
||||
width={128}
|
||||
height={128}
|
||||
class="rounded-xl object-cover"
|
||||
fallback={name[0]}
|
||||
className="size-32 rounded-md"
|
||||
/>
|
||||
<div class="flex-grow">
|
||||
<h3 class="mb-1 text-lg font-semibold">{name}</h3>
|
||||
|
|
|
@ -4,6 +4,7 @@ import { formatDate, readingTime, parseAuthors } from '@lib/utils'
|
|||
import { Image } from 'astro:assets'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import AvatarComponent from '@/components/ui/avatar'
|
||||
import Link from './Link.astro'
|
||||
|
||||
type Props = {
|
||||
|
@ -54,12 +55,12 @@ const authors = await parseAuthors(entry.data.authors ?? [])
|
|||
<>
|
||||
{authors.map((author) => (
|
||||
<div class="flex items-center gap-x-1.5">
|
||||
<Image
|
||||
<AvatarComponent
|
||||
client:load
|
||||
src={author.avatar}
|
||||
alt={author.name}
|
||||
width={18}
|
||||
height={18}
|
||||
class="rounded-full"
|
||||
fallback={author.name[0]}
|
||||
className="size-5 rounded-full"
|
||||
/>
|
||||
<span>{author.name}</span>
|
||||
</div>
|
||||
|
|
|
@ -15,7 +15,7 @@ interface Props {
|
|||
|
||||
const canonicalURL = new URL(Astro.url.pathname, Astro.site)
|
||||
|
||||
const { title, description, image = '/blog-placeholder-1.jpg' } = Astro.props
|
||||
const { title, description, image = '/static/twitter-card.png' } = Astro.props
|
||||
---
|
||||
|
||||
<meta charset="utf-8" />
|
||||
|
@ -28,6 +28,30 @@ const { title, description, image = '/blog-placeholder-1.jpg' } = Astro.props
|
|||
<meta name="title" content={title} />
|
||||
<meta name="description" content={description} />
|
||||
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="/favicons/apple-touch-icon.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="/favicons/favicon-32x32.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="/favicons/favicon-16x16.png"
|
||||
/>
|
||||
<link rel="manifest" href="/favicons/site.webmanifest" />
|
||||
<link rel="mask-icon" href="/favicons/safari-pinned-tab.svg" color="#5bbad5" />
|
||||
<link rel="shortcut icon" href="/favicons/favicon.ico" />
|
||||
<meta name="msapplication-TileColor" content="#da532c" />
|
||||
<meta name="msapplication-config" content="/favicons/browserconfig.xml" />
|
||||
<meta name="theme-color" content="#121212" />
|
||||
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content={Astro.url} />
|
||||
<meta property="og:title" content={title} />
|
||||
|
|
|
@ -3,6 +3,8 @@ import Container from '@components/Container.astro'
|
|||
import Link from '@components/Link.astro'
|
||||
import { SITE } from '@consts'
|
||||
import { ModeToggle } from '@/components/ui/mode-toggle'
|
||||
import { Image } from 'astro:assets'
|
||||
import logo from '../../public/static/logo.svg'
|
||||
|
||||
const items = [
|
||||
{ href: '/blog', label: 'blog' },
|
||||
|
@ -16,11 +18,12 @@ const items = [
|
|||
transition:persist
|
||||
>
|
||||
<Container>
|
||||
<div class="flex items-center justify-between py-4">
|
||||
<div class="flex flex-wrap items-center justify-between gap-x-4 py-4">
|
||||
<Link
|
||||
href="/"
|
||||
class="text-xl font-semibold transition-colors duration-300 hover:text-primary"
|
||||
class="flex flex-shrink-0 items-center gap-2 text-xl font-semibold transition-colors duration-300 hover:text-primary"
|
||||
>
|
||||
<Image src={logo} alt="Logo" class="size-8 rounded-sm" />
|
||||
{SITE.TITLE}
|
||||
</Link>
|
||||
<div class="flex items-center gap-4">
|
||||
|
|
|
@ -10,7 +10,7 @@ const Avatar = React.forwardRef<
|
|||
<AvatarPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full',
|
||||
'relative flex h-10 w-10 shrink-0 overflow-hidden',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
@ -37,7 +37,7 @@ const AvatarFallback = React.forwardRef<
|
|||
<AvatarPrimitive.Fallback
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex h-full w-full items-center justify-center rounded-full bg-muted',
|
||||
'flex h-full w-full items-center justify-center bg-muted',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
@ -45,4 +45,25 @@ const AvatarFallback = React.forwardRef<
|
|||
))
|
||||
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
|
||||
|
||||
export { Avatar, AvatarFallback, AvatarImage }
|
||||
interface AvatarComponentProps {
|
||||
src?: string
|
||||
alt?: string
|
||||
fallback?: string
|
||||
className?: string
|
||||
}
|
||||
|
||||
const AvatarComponent: React.FC<AvatarComponentProps> = ({
|
||||
src,
|
||||
alt,
|
||||
fallback,
|
||||
className,
|
||||
}) => {
|
||||
return (
|
||||
<Avatar className={className}>
|
||||
<AvatarImage src={src} alt={alt} />
|
||||
<AvatarFallback>{fallback}</AvatarFallback>
|
||||
</Avatar>
|
||||
)
|
||||
}
|
||||
|
||||
export default AvatarComponent
|
||||
|
|
|
@ -5,7 +5,7 @@ import * as React from 'react'
|
|||
import { cn } from '@/lib/utils'
|
||||
|
||||
const badgeVariants = cva(
|
||||
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
||||
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs transition-colors focus:outline-none focus:ring focus:ring-ring',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
|
|
@ -8,7 +8,7 @@ const Card = React.forwardRef<
|
|||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('rounded-xl border bg-card text-card-foreground', className)}
|
||||
className={cn('bg-card text-card-foreground rounded-xl border', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
|
|
|
@ -41,7 +41,7 @@ export function ModeToggle() {
|
|||
<span className="sr-only">Toggle theme</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuContent align="end" className="bg-background">
|
||||
<DropdownMenuItem onClick={() => setThemeState('theme-light')}>
|
||||
<Sun className="mr-2 size-4" />
|
||||
<span>Light</span>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue