- Only preload the post cover and latest-post image instead of every image - Drop in-post image quality from 100 to 90 and add responsive sizes - Downscale 2940px screenshots to 1800px; convert linkboard-old to WebP (3.1 MB -> 60 KB) - Make CodeWrapper a server component; only CopyButton ships to the client - Pin the Mermaid CDN script to an exact version for reproducible builds
55 lines
2 KiB
TypeScript
55 lines
2 KiB
TypeScript
import { cn } from '@/utils/cn'
|
|
import Image from 'next/image'
|
|
|
|
type ImageWrapperProps = {
|
|
src: string
|
|
alt?: string
|
|
/** Visible caption. Falls back to the alt text when omitted. */
|
|
caption?: string
|
|
isListing?: boolean
|
|
/** Preload the image. Only set this for above-the-fold images (covers, latest post). */
|
|
priority?: boolean
|
|
}
|
|
|
|
export function ImageWrapper({ src, alt, caption, isListing, priority }: ImageWrapperProps) {
|
|
const captionText = caption ?? alt
|
|
// When the caption just repeats the alt text, hide it from screen readers so it isn't
|
|
// announced twice
|
|
const captionIsAlt = caption === undefined
|
|
|
|
return (
|
|
<figure className={cn('flex flex-col items-center gap-1 mb-4 w-full', isListing && 'h-full')}>
|
|
<div className="border border-border rounded-xl w-full overflow-hidden flex-1">
|
|
<div
|
|
className="flex items-center gap-2 px-2 h-6 border-b border-border w-full bg-fg/10"
|
|
aria-hidden="true"
|
|
>
|
|
<div className="space-x-1">
|
|
<span className="w-2 h-2 bg-border rounded-full inline-block"></span>
|
|
<span className="w-2 h-2 bg-border rounded-full inline-block"></span>
|
|
<span className="w-2 h-2 bg-border rounded-full inline-block"></span>
|
|
</div>
|
|
<span className="text-xs text-fg-dim font-mono">~/{src}</span>
|
|
</div>
|
|
<Image
|
|
src={`/posts/${src}`}
|
|
alt={alt ?? ''}
|
|
width={isListing ? 400 : 900}
|
|
height={isListing ? 240 : 600}
|
|
sizes={isListing ? '(min-width: 768px) 400px, 100vw' : '(min-width: 896px) 900px, 100vw'}
|
|
quality={isListing ? 75 : 90}
|
|
priority={priority}
|
|
className="w-full mx-auto object-contain h-full max-h-150"
|
|
/>
|
|
</div>
|
|
{!isListing && captionText && (
|
|
<figcaption
|
|
className="block text-xs text-fg-dim font-mono text-center"
|
|
aria-hidden={captionIsAlt || undefined}
|
|
>
|
|
{captionText}
|
|
</figcaption>
|
|
)}
|
|
</figure>
|
|
)
|
|
}
|