611af1ac67
Deploy Documentation / check-and-deploy (push) Successful in 20s
- Implemented HomeView with a timeline layout, featuring sections for hero, profile, projects, stack, and contact. - Created ProjectView to display individual project details, including status, highlights, and tech stack. - Developed WorkIndexView to list all projects with breadcrumb navigation. - Enhanced TypeScript configuration for improved module resolution.
119 lines
3.0 KiB
Plaintext
119 lines
3.0 KiB
Plaintext
---
|
|
import { Badge, Button } from '@unkn0wndo3s/nova-design-system';
|
|
import TechIcon from './TechIcon.astro';
|
|
import type { Project } from '../data/projects';
|
|
import { getLocaleFromUrl, useTranslations, localizePath } from '../i18n';
|
|
|
|
export interface Props {
|
|
project: Project;
|
|
index?: number;
|
|
headingLevel?: 'h2' | 'h3';
|
|
}
|
|
|
|
const { project, index, headingLevel: Heading = 'h3' } = Astro.props;
|
|
const locale = getLocaleFromUrl(Astro.url);
|
|
const t = useTranslations(locale);
|
|
|
|
const statusType = { live: 'success', maintained: 'info', wip: 'warning' } as const;
|
|
const statusLabel = {
|
|
live: t('work.status.live'),
|
|
maintained: t('work.status.maintained'),
|
|
wip: t('work.status.wip'),
|
|
} as const;
|
|
|
|
const href = localizePath(`/work/${project.slug}`, locale);
|
|
---
|
|
|
|
<article class="project-card">
|
|
<header class="project-card__head">
|
|
{typeof index === 'number' && (
|
|
<span class="project-card__index" aria-hidden="true">{String(index + 1).padStart(2, '0')}</span>
|
|
)}
|
|
<Badge type={statusType[project.status]}>{statusLabel[project.status]}</Badge>
|
|
</header>
|
|
|
|
<Heading class="project-card__title">
|
|
<a href={href}>{project.name}</a>
|
|
</Heading>
|
|
<p class="project-card__tagline">{project.tagline[locale]}</p>
|
|
<p class="project-card__summary">{project.summary[locale]}</p>
|
|
|
|
<ul class="project-card__stack" aria-label={t('work.stack')}>
|
|
{project.stack.map((tech) => (
|
|
<li><TechIcon name={tech.name} icon={tech.icon} size={16} /></li>
|
|
))}
|
|
</ul>
|
|
|
|
<footer class="project-card__footer">
|
|
<Button href={href} type="secondary" size="sm">{t('home.projects.view')}</Button>
|
|
</footer>
|
|
</article>
|
|
|
|
<style lang="scss">
|
|
@use '../styles/type' as type;
|
|
|
|
.project-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--nds-spacing-sm);
|
|
height: 100%;
|
|
padding: clamp(20px, 3vw, 32px);
|
|
background: color-mix(in srgb, var(--nds-surface) 88%, transparent);
|
|
border: var(--nds-border-width-thin) solid var(--nds-border);
|
|
border-radius: var(--nds-radius-xl);
|
|
transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
|
|
|
|
&:hover,
|
|
&:focus-within {
|
|
transform: translateY(-4px);
|
|
border-color: var(--nds-border-strong);
|
|
box-shadow: var(--nds-shadow-lg);
|
|
}
|
|
}
|
|
|
|
.project-card__head {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.project-card__index {
|
|
font-family: var(--nds-font-mono);
|
|
color: var(--nds-neutral);
|
|
}
|
|
|
|
.project-card__title {
|
|
@include type.text-2xl;
|
|
|
|
a {
|
|
color: var(--nds-text);
|
|
text-decoration: none;
|
|
|
|
&:hover { color: var(--nds-primary); }
|
|
}
|
|
}
|
|
|
|
.project-card__tagline {
|
|
@include type.text-lg;
|
|
color: var(--nds-primary);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.project-card__summary {
|
|
@include type.text-base;
|
|
color: var(--nds-neutral);
|
|
flex: 1;
|
|
}
|
|
|
|
.project-card__stack {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: var(--nds-spacing-xs);
|
|
margin: 0;
|
|
padding: 0;
|
|
list-style: none;
|
|
}
|
|
|
|
.project-card__footer { margin-top: var(--nds-spacing-xs); }
|
|
</style>
|