Portfolio/app/components/ProjectCard.vue
2026-09-08 19:54:06 +02:00

98 lines
2.2 KiB
Vue

<script setup lang="ts">
import type { Project } from '#shared/site'
import type { RepoProject } from '#shared/types/projects'
const props = defineProps<{ project: Project | RepoProject; compact?: boolean }>()
const statusGlyph: Record<NonNullable<Project['status']>, string> = {
active: '●',
wip: '◐',
shipped: '✓',
archived: '□',
}
const sourceGlyph: Record<RepoProject['source'], string> = {
github: 'gh',
gitea: 'gitea',
manual: '',
}
/** repo metadata is absent on hand-written entries */
const repo = computed(() => ('source' in props.project ? props.project : null))
</script>
<template>
<FrameBox :to="props.project.href" hover :tag="props.project.year" class="card">
<div class="head">
<h2>{{ props.project.name }}</h2>
<span class="arrow muted">↗</span>
</div>
<p class="muted small tagline">{{ props.project.tagline }}</p>
<ul v-if="!props.compact && props.project.bullets?.length" class="bullets">
<li v-for="bullet in props.project.bullets" :key="bullet">{{ bullet }}</li>
</ul>
<div class="foot row small">
<span class="stack-line muted">{{ props.project.stack.join(' · ') }}</span>
<span class="meta faint">
<span v-if="repo && sourceGlyph[repo.source]" class="origin">
{{ sourceGlyph[repo.source] }}
</span>
<span v-if="repo && repo.stars" class="stars">★ {{ repo.stars }}</span>
<span v-if="props.project.status" class="status">
<span class="accent">{{ statusGlyph[props.project.status] }}</span>
{{ props.project.status }}
</span>
</span>
</div>
</FrameBox>
</template>
<style scoped>
.card {
height: 100%;
display: flex;
flex-direction: column;
}
.head {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 0.5rem;
}
h2 {
font-size: 1.05rem;
text-transform: lowercase;
letter-spacing: -0.01em;
}
.tagline {
margin-top: 0.35rem;
}
.foot {
margin-top: auto;
padding-top: 1rem;
justify-content: space-between;
gap: 0.75rem;
}
.stack-line {
font-size: 0.8rem;
}
.meta {
display: flex;
align-items: baseline;
gap: 0.6rem;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.04em;
white-space: nowrap;
}
</style>