First Release v1.0.0
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
---
|
||||
import { Image } from 'astro:assets';
|
||||
import type { ImageMetadata } from 'astro';
|
||||
import Button from '@/components/ui/form/Button/Button.astro';
|
||||
import Icon from '@/components/ui/primitives/Icon/Icon.astro';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
tags?: string[];
|
||||
year?: number;
|
||||
client?: string;
|
||||
role?: string;
|
||||
services?: string[];
|
||||
url?: string;
|
||||
repo?: string;
|
||||
image?: ImageMetadata;
|
||||
imageAlt?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
title,
|
||||
description,
|
||||
tags = [],
|
||||
year,
|
||||
client,
|
||||
role,
|
||||
url,
|
||||
repo,
|
||||
image,
|
||||
imageAlt,
|
||||
} = Astro.props;
|
||||
|
||||
const hasMeta = year || client || role;
|
||||
---
|
||||
|
||||
<header class="relative overflow-hidden pt-[var(--space-page-top-sm)] pb-[var(--space-section)]">
|
||||
<div class="relative mx-auto max-w-4xl px-6 animate-hero-slide-up">
|
||||
|
||||
<!-- Tags -->
|
||||
{tags.length > 0 && (
|
||||
<div class="mb-[var(--space-heading-gap)] flex flex-wrap gap-2">
|
||||
{tags.map((tag) => (
|
||||
<span class="inline-flex items-center rounded-full bg-brand-100 dark:bg-brand-900/30 px-3 py-1 text-xs font-semibold text-brand-700 dark:text-brand-300 ring-1 ring-inset ring-brand-200 dark:ring-brand-800">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<!-- Title -->
|
||||
<h1 class="font-display text-4xl font-bold tracking-tight text-foreground md:text-5xl lg:text-6xl mb-[var(--space-heading-gap)]">
|
||||
{title}
|
||||
</h1>
|
||||
|
||||
<!-- Description -->
|
||||
<p class="text-xl text-foreground-muted leading-relaxed max-w-3xl mb-[var(--space-stack-lg)]">
|
||||
{description}
|
||||
</p>
|
||||
|
||||
<!-- Meta row -->
|
||||
{hasMeta && (
|
||||
<div class="flex flex-wrap items-center gap-[var(--space-stack-lg)] text-sm text-foreground-muted mb-[var(--space-stack-lg)]">
|
||||
{year && (
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="h-5 w-5 text-foreground-subtle" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 012.25-2.25h13.5A2.25 2.25 0 0121 7.5v11.25m-18 0A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75m-18 0v-7.5A2.25 2.25 0 015.25 9h13.5A2.25 2.25 0 0121 11.25v7.5" />
|
||||
</svg>
|
||||
<span>{year}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{client && (
|
||||
<>
|
||||
{year && <div class="h-8 w-px bg-border hidden md:block" aria-hidden="true"></div>}
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="h-5 w-5 text-foreground-subtle" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z" />
|
||||
</svg>
|
||||
<span>{client}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{role && (
|
||||
<>
|
||||
{(year || client) && <div class="h-8 w-px bg-border hidden md:block" aria-hidden="true"></div>}
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="h-5 w-5 text-foreground-subtle" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M20.25 14.15v4.25c0 1.094-.787 2.036-1.872 2.18-2.087.277-4.216.42-6.378.42s-4.291-.143-6.378-.42c-1.085-.144-1.872-1.086-1.872-2.18v-4.25m16.5 0a2.18 2.18 0 00.75-1.661V8.706c0-1.081-.768-2.015-1.837-2.175a48.114 48.114 0 00-3.413-.387m4.5 8.006c-.194.165-.42.295-.673.38A23.978 23.978 0 0112 15.75c-2.648 0-5.195-.429-7.577-1.22a2.016 2.016 0 01-.673-.38m0 0A2.18 2.18 0 013 12.489V8.706c0-1.081.768-2.015 1.837-2.175a48.111 48.111 0 013.413-.387m7.5 0V5.25A2.25 2.25 0 0013.5 3h-3a2.25 2.25 0 00-2.25 2.25v.894m7.5 0a48.667 48.667 0 00-7.5 0M12 12.75h.008v.008H12v-.008z" />
|
||||
</svg>
|
||||
<span>{role}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<!-- Action buttons -->
|
||||
{(url || repo) && (
|
||||
<div class="flex flex-wrap gap-3">
|
||||
{url && (
|
||||
<Button href={url} target="_blank" rel="noopener noreferrer" size="md">
|
||||
<Icon name="external-link" size="sm" />
|
||||
View live site
|
||||
</Button>
|
||||
)}
|
||||
{repo && (
|
||||
<Button href={repo} target="_blank" rel="noopener noreferrer" variant="outline" size="md">
|
||||
<Icon name="github" size="sm" />
|
||||
View source
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{image && (
|
||||
<div class="relative mx-auto max-w-5xl px-6 mt-[var(--space-section)] animate-hero-slide-up [animation-delay:200ms]">
|
||||
<div class="relative overflow-hidden rounded-xl border border-border shadow-2xl">
|
||||
<Image
|
||||
src={image}
|
||||
alt={imageAlt || title}
|
||||
widths={[640, 960, 1280, 1920]}
|
||||
sizes="(max-width: 640px) 640px, (max-width: 960px) 960px, (max-width: 1280px) 1280px, 1920px"
|
||||
class="aspect-video w-full object-cover"
|
||||
loading="eager"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
Reference in New Issue
Block a user