refonte: portfolio statique Astro (design moderne développeur)
Some checks failed
Deploy production / build-deploy (push) Failing after 56s
Some checks failed
Deploy production / build-deploy (push) Failing after 56s
- Remplace l'ancien Astro hybrid + admin par un site one-page statique (sortie static) - Thème clair/sombre sans flash, contenu typé (src/data/content.ts), zéro admin - Icônes simple-icons (SVG monochrome), polices auto-hébergées (RGPD) - SEO complet : canonical, Open Graph, Twitter Card, JSON-LD Person, robots.txt, sitemap.xml, image OG 1200x630 - CI Gitea de déploiement FTPS vers Plesk (.gitea/workflows/prod.yml)
This commit is contained in:
@@ -1,93 +1,40 @@
|
||||
---
|
||||
import type { Project } from '../content/schemas';
|
||||
import Icon from "./Icon.astro";
|
||||
import type { Project } from "../data/content";
|
||||
|
||||
interface Props {
|
||||
project: Project;
|
||||
}
|
||||
const { project } = Astro.props;
|
||||
const isArchived = project.status === 'archived';
|
||||
const isPrivate = project.status === 'private';
|
||||
const statusLabel =
|
||||
project.status === 'live'
|
||||
? 'Live'
|
||||
: project.status === 'archived'
|
||||
? 'Archivé'
|
||||
: project.status === 'private'
|
||||
? 'Privé'
|
||||
: 'WIP';
|
||||
const STATUS: Record<Project["status"], { label: string; color: string }> = {
|
||||
live: { label: "en ligne", color: "var(--accent)" },
|
||||
archived: { label: "archivé", color: "var(--text-dim)" },
|
||||
oss: { label: "open-source", color: "var(--info)" },
|
||||
};
|
||||
|
||||
const Tag = project.url && !isArchived && !isPrivate ? 'a' : 'div';
|
||||
const tagProps = project.url && !isArchived && !isPrivate
|
||||
? { href: project.url, target: '_blank', rel: 'noopener noreferrer' }
|
||||
: {};
|
||||
interface Props { project: Project; }
|
||||
const { project: p } = Astro.props;
|
||||
const st = STATUS[p.status];
|
||||
const mono = p.title.replace(/[^A-Za-z0-9]/g, "").slice(0, 2);
|
||||
---
|
||||
|
||||
<Tag
|
||||
{...tagProps}
|
||||
class:list={[
|
||||
'group card-neon block h-full overflow-hidden',
|
||||
isArchived && 'opacity-70',
|
||||
]}
|
||||
>
|
||||
<div class="relative aspect-[16/9] -mx-6 -mt-6 mb-4 overflow-hidden bg-[color:var(--color-bg)]">
|
||||
{project.image ? (
|
||||
<img
|
||||
src={project.image}
|
||||
alt={project.title}
|
||||
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
||||
class:list={[isArchived && 'grayscale']}
|
||||
/>
|
||||
<article class="card">
|
||||
<div class="card-media">
|
||||
{p.img ? (
|
||||
<img src={p.img} alt={p.title} loading="lazy" />
|
||||
) : (
|
||||
<div class="w-full h-full flex items-center justify-center text-[color:var(--color-text-dim)] font-mono text-xs aurora-bg">
|
||||
// no preview
|
||||
</div>
|
||||
<div class="card-mono"><span>{mono}</span></div>
|
||||
)}
|
||||
|
||||
<div class="absolute top-3 right-3 flex gap-2">
|
||||
<span
|
||||
class:list={[
|
||||
'text-[10px] font-mono uppercase tracking-wider px-2 py-1 rounded-sm border backdrop-blur-md',
|
||||
project.status === 'live' && 'border-[color:var(--color-cyan)] text-[color:var(--color-cyan)] bg-[rgba(0,240,255,0.1)]',
|
||||
project.status === 'archived' && 'border-[color:var(--color-text-dim)] text-[color:var(--color-text-dim)] bg-[rgba(0,0,0,0.5)]',
|
||||
project.status === 'private' && 'border-[color:var(--color-magenta)] text-[color:var(--color-magenta)] bg-[rgba(255,42,109,0.1)]',
|
||||
project.status === 'wip' && 'border-[color:var(--color-violet)] text-[color:var(--color-violet)] bg-[rgba(185,103,255,0.1)]',
|
||||
]}
|
||||
>
|
||||
{statusLabel}
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="card-head">
|
||||
<span class="status"><span class="status-dot" style={`background:${st.color}`}></span>{st.label}</span>
|
||||
{p.link && (
|
||||
<a class="ext-link" href={p.link} target="_blank" rel="noopener" aria-label="Voir le projet (nouvel onglet)">
|
||||
<Icon name="external" size={16} />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
<h3 class="card-title">{p.title}</h3>
|
||||
<p class="card-sub">{p.subtitle}</p>
|
||||
<p class="card-desc">{p.desc}</p>
|
||||
<div class="chips">
|
||||
{p.stack.map((s) => <span class="chip">{s}</span>)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="font-display text-lg mb-1 text-[color:var(--color-text)] group-hover:glow-text-cyan group-hover:text-[color:var(--color-cyan)] transition-colors">
|
||||
{project.title}
|
||||
</h3>
|
||||
{project.subtitle && (
|
||||
<p class="text-xs font-mono text-[color:var(--color-text-dim)] mb-3 uppercase tracking-wider">
|
||||
{project.subtitle}
|
||||
</p>
|
||||
)}
|
||||
<p class="text-sm text-[color:var(--color-text-muted)] mb-4 line-clamp-3">
|
||||
{project.description}
|
||||
</p>
|
||||
|
||||
{project.stack.length > 0 && (
|
||||
<div class="flex flex-wrap gap-1.5 mb-4">
|
||||
{project.stack.slice(0, 5).map((tech) => (
|
||||
<span class="chip">{tech}</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{project.url && !isArchived && !isPrivate && (
|
||||
<span class="text-xs font-mono text-[color:var(--color-cyan)] group-hover:underline">
|
||||
→ Voir le projet
|
||||
</span>
|
||||
)}
|
||||
{isArchived && (
|
||||
<span class="text-xs font-mono text-[color:var(--color-text-dim)]">
|
||||
// projet fermé
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</Tag>
|
||||
</article>
|
||||
|
||||
Reference in New Issue
Block a user