Trois surfaces partagent désormais le même design system, Tailwind v4
et React 19 — au lieu d'avoir landing en HTML vanilla, app en React, et
blog en Adonis SSR :
* packages/ui — design system partagé (tokens Tailwind v4 + composants
TSX) extrait depuis apps/web : Brand, Gem, Button, Card, Chip, Eyebrow,
EmptyState. apps/web migre 41 imports vers @rubis/ui.
* apps/landing — nouvelle app Astro 6 SSR (rubis.pro), remplace l'ancienne
landing nginx vanilla. Embarque :
- Landing complète portée en sections React (Hero, Stats, Promise,
HowItWorks, Gamification, Legal, Pricing, FAQ, FinalCTA, Footnotes)
- Pages légales (mentions, confidentialité, CGV) via LegalLayout.astro
- Blog SSR (/blog, /blog/:slug) qui consomme /api/v1/posts
- sitemap.xml, blog/rss.xml, robots.txt en endpoints Astro
- SEO complet (canonical, hreflang, OG, Twitter Card, JSON-LD
Article/BreadcrumbList/Blog/SoftwareApplication)
* apps/api — BlogController réduit à 2 endpoints JSON (GET /api/v1/posts
+ GET /api/v1/posts/:slug). Suppression des templates SSR Adonis
(apps/api/app/blog/), de l'alias #blog/*, des deps react-dom et
@types/react-dom. PostTransformer + PostSummaryTransformer ajoutés.
Le service blog_renderer + le seeder + les 3 articles fondateurs
restent intacts (réutilisés par futurs admin + cron IA).
* Infra :
- Dockerfile.landing (multi-stage Node 22 + tini, Astro standalone)
- k3s/app/landing.yml (Deployment + Service rubis-landing:4321 +
ConfigMap avec API_URL=http://rubis-api.rubis.svc.cluster.local:3333)
- .gitea/workflows/deploy.yml mis à jour pour build rubis-landing
- .gitea/workflows/deploy-web.yml + Dockerfile.web : prennent en
compte packages/ui/ comme dépendance
- Suppression du Dockerfile nginx legacy + k3s/{deployment,service}.yml
- Suppression de landing/ (assets favicons migrés vers
apps/landing/public/)
* Docs : architecture.md (vue d'ensemble + §4bis apps/landing complet,
§3 endpoints JSON blog, layout monorepo), CLAUDE.md (stack technique,
documents associés, déploiement).
Note infra : l'ancien Deployment "rubis" (nginx) et son Service ne sont
PAS supprimés par la CI — à nettoyer manuellement après validation que
Traefik a été repointé sur rubis-landing:4321 dans le repo proxmox.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
205 lines
5.3 KiB
Plaintext
205 lines
5.3 KiB
Plaintext
---
|
|
/**
|
|
* Layout pour pages légales (mentions, confidentialité, CGV).
|
|
* Wrappe Layout.astro et fournit un container `prose` cohérent pour
|
|
* les textes longs : eyebrow + h1 italique + lede + sections h2 ancrées.
|
|
*/
|
|
import Layout from "./Layout.astro";
|
|
|
|
interface Props {
|
|
title: string;
|
|
description: string;
|
|
/** Eyebrow affiché au-dessus du titre. */
|
|
eyebrow: string;
|
|
/** Titre affiché (peut contenir <em>...</em>). */
|
|
h1: string;
|
|
/** Paragraphe d'introduction. */
|
|
lede: string;
|
|
/** Date "Dernière mise à jour" — ISO ou littérale. */
|
|
lastUpdated: string;
|
|
}
|
|
|
|
const { title, description, eyebrow, h1, lede, lastUpdated } = Astro.props;
|
|
---
|
|
|
|
<Layout title={title} description={description} solidHeader>
|
|
<article class="legal-prose">
|
|
<div class="max-w-[760px] mx-auto px-5 sm:px-8 py-16 lg:py-20">
|
|
<p
|
|
class="text-[11px] uppercase tracking-[0.16em] font-semibold text-rubis flex items-center gap-2"
|
|
>
|
|
<span aria-hidden class="size-[7px] bg-current rotate-45 inline-block"></span>
|
|
{eyebrow}
|
|
</p>
|
|
|
|
<h1
|
|
class="mt-5 font-display font-bold text-ink leading-[1.05] tracking-[-0.025em] text-[40px] sm:text-[52px]"
|
|
set:html={h1}
|
|
/>
|
|
|
|
<p class="mt-6 text-[18px] text-ink-2 leading-relaxed">{lede}</p>
|
|
<p class="mt-3 text-[13px] text-ink-3 italic">Dernière mise à jour : {lastUpdated}</p>
|
|
|
|
<div class="mt-12 prose-content">
|
|
<slot />
|
|
</div>
|
|
</div>
|
|
</article>
|
|
|
|
<style is:global>
|
|
.prose-content {
|
|
color: var(--color-ink);
|
|
}
|
|
.prose-content > * + * {
|
|
margin-top: 1.4em;
|
|
}
|
|
.prose-content h2 {
|
|
font-family: var(--font-display);
|
|
font-weight: 700;
|
|
font-size: 26px;
|
|
letter-spacing: -0.02em;
|
|
line-height: 1.25;
|
|
margin-top: 2.6em;
|
|
color: var(--color-ink);
|
|
}
|
|
.prose-content h3 {
|
|
font-family: var(--font-display);
|
|
font-weight: 700;
|
|
font-size: 19px;
|
|
letter-spacing: -0.015em;
|
|
line-height: 1.3;
|
|
margin-top: 2em;
|
|
color: var(--color-ink);
|
|
}
|
|
.prose-content p,
|
|
.prose-content li {
|
|
font-size: 16.5px;
|
|
line-height: 1.7;
|
|
color: var(--color-ink-2);
|
|
}
|
|
.prose-content strong {
|
|
color: var(--color-ink);
|
|
font-weight: 600;
|
|
}
|
|
.prose-content em {
|
|
font-style: italic;
|
|
color: var(--color-rubis);
|
|
}
|
|
.prose-content a {
|
|
color: var(--color-rubis);
|
|
text-decoration: underline;
|
|
text-underline-offset: 3px;
|
|
}
|
|
.prose-content a:hover {
|
|
color: var(--color-rubis-deep);
|
|
}
|
|
.prose-content ul,
|
|
.prose-content ol {
|
|
padding-left: 1.6em;
|
|
}
|
|
.prose-content ul {
|
|
list-style: disc;
|
|
}
|
|
.prose-content ol {
|
|
list-style: decimal;
|
|
}
|
|
.prose-content li + li {
|
|
margin-top: 0.5em;
|
|
}
|
|
.prose-content code {
|
|
background: var(--color-cream-2);
|
|
padding: 2px 6px;
|
|
border-radius: 4px;
|
|
font-size: 0.92em;
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
}
|
|
.prose-content .toc {
|
|
background: var(--color-cream-2);
|
|
border: 1px solid var(--color-line);
|
|
border-radius: 14px;
|
|
padding: 22px 28px;
|
|
margin: 2.5em 0;
|
|
}
|
|
.prose-content .toc-title {
|
|
font-family: var(--font-display);
|
|
font-size: 13px;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.08em;
|
|
color: var(--color-ink);
|
|
margin-bottom: 12px;
|
|
}
|
|
.prose-content .toc ol {
|
|
counter-reset: toc;
|
|
padding-left: 0;
|
|
list-style: none;
|
|
margin: 0;
|
|
}
|
|
.prose-content .toc li {
|
|
counter-increment: toc;
|
|
padding-left: 32px;
|
|
position: relative;
|
|
margin-top: 8px;
|
|
font-size: 14.5px;
|
|
}
|
|
.prose-content .toc li::before {
|
|
content: counter(toc, decimal-leading-zero);
|
|
position: absolute;
|
|
left: 0;
|
|
top: 1px;
|
|
font-family: var(--font-display);
|
|
font-weight: 700;
|
|
color: var(--color-rubis);
|
|
font-size: 12.5px;
|
|
letter-spacing: 0.04em;
|
|
}
|
|
.prose-content .toc a {
|
|
text-decoration: none;
|
|
color: var(--color-ink-2);
|
|
}
|
|
.prose-content .toc a:hover {
|
|
color: var(--color-rubis);
|
|
text-decoration: underline;
|
|
}
|
|
.prose-content .callout {
|
|
background: var(--color-rubis-glow);
|
|
border-left: 3px solid var(--color-rubis);
|
|
padding: 16px 20px;
|
|
border-radius: 0 8px 8px 0;
|
|
margin: 1.6em 0;
|
|
}
|
|
.prose-content .callout p {
|
|
margin: 0;
|
|
font-size: 15.5px;
|
|
}
|
|
.prose-content .def-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
font-size: 14.5px;
|
|
margin: 1.4em 0;
|
|
}
|
|
.prose-content .def-table thead th {
|
|
text-align: left;
|
|
font-family: var(--font-display);
|
|
font-size: 12px;
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
font-weight: 700;
|
|
color: var(--color-ink);
|
|
padding: 12px 14px;
|
|
background: var(--color-cream-2);
|
|
border-bottom: 2px solid var(--color-line);
|
|
}
|
|
.prose-content .def-table tbody td {
|
|
padding: 12px 14px;
|
|
vertical-align: top;
|
|
border-bottom: 1px solid var(--color-line);
|
|
color: var(--color-ink-2);
|
|
}
|
|
.prose-content .def-table tbody td:first-child {
|
|
width: 38%;
|
|
color: var(--color-ink);
|
|
}
|
|
</style>
|
|
</Layout>
|