ergonomie

This commit is contained in:
2026-02-08 21:18:38 +04:00
parent 2436f3cf28
commit 412148a254
10 changed files with 488 additions and 541 deletions

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="1024" fill="none"><path fill="url(#a)" fill-rule="evenodd" d="M-217.58 475.75c91.82-72.02 225.52-29.38 341.2-44.74C240 415.56 372.33 315.14 466.77 384.9c102.9 76.02 44.74 246.76 90.31 366.31 29.83 78.24 90.48 136.14 129.48 210.23 57.92 109.99 169.67 208.23 155.9 331.77-13.52 121.26-103.42 264.33-224.23 281.37-141.96 20.03-232.72-220.96-374.06-196.99-151.7 25.73-172.68 330.24-325.85 315.72-128.6-12.2-110.9-230.73-128.15-358.76-12.16-90.14 65.87-176.25 44.1-264.57-26.42-107.2-167.12-163.46-176.72-273.45-10.15-116.29 33.01-248.75 124.87-320.79Z" clip-rule="evenodd" style="opacity:.154"/><path fill="url(#b)" fill-rule="evenodd" d="M1103.43 115.43c146.42-19.45 275.33-155.84 413.5-103.59 188.09 71.13 409 212.64 407.06 413.88-1.94 201.25-259.28 278.6-414.96 405.96-130 106.35-240.24 294.39-405.6 265.3-163.7-28.8-161.93-274.12-284.34-386.66-134.95-124.06-436-101.46-445.82-284.6-9.68-180.38 247.41-246.3 413.54-316.9 101.01-42.93 207.83 21.06 316.62 6.61Z" clip-rule="evenodd" style="opacity:.154"/><defs><linearGradient id="b" x1="373" x2="1995.44" y1="1100" y2="118.03" gradientUnits="userSpaceOnUse"><stop stop-color="#D83333"/><stop offset="1" stop-color="#F041FF"/></linearGradient><linearGradient id="a" x1="107.37" x2="1130.66" y1="1993.35" y2="1026.31" gradientUnits="userSpaceOnUse"><stop stop-color="#3245FF"/><stop offset="1" stop-color="#BC52EE"/></linearGradient></defs></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,15 +1,24 @@
--- ---
import type { CollectionEntry } from "astro:content"; import type { CollectionEntry } from "astro:content";
import GoldButton from "./ui/GoldButton.astro";
import CodexCard from "./ui/CodexCard.astro";
interface Props { interface Props {
posts: CollectionEntry<"journal" | "logs" | "codex">[]; posts: CollectionEntry<"journal">[];
tags: string[]; tags: string[];
basePath: "journal" | "logs" | "codex"; basePath: "journal";
} }
const { posts, tags, basePath } = Astro.props; const { posts, tags, basePath } = Astro.props;
// Groupement simple
const groupedTags: Record<string, string[]> = { Général: [] };
tags.forEach((tag) => {
if (tag.includes(":")) {
const cat = tag.split(":")[0];
const catName = cat.charAt(0).toUpperCase() + cat.slice(1);
if (!groupedTags[catName]) groupedTags[catName] = [];
groupedTags[catName].push(tag);
} else {
groupedTags["Général"].push(tag);
}
});
--- ---
<div id="content-search-component"> <div id="content-search-component">
@@ -17,174 +26,190 @@ const { posts, tags, basePath } = Astro.props;
<input <input
type="search" type="search"
id="search-input" id="search-input"
placeholder="Rechercher dans le grimoire..." placeholder="Rechercher dans le journal..."
/> />
</div> </div>
<div id="tag-filters" class="tags-container"> <div class="filter-box">
<button class="tag-btn active" data-tag="all">Toutes</button> <div class="category-nav">
{ <span class="nav-label">Filtres :</span>
tags.map((tag) => ( <button class="cat-nav-btn active" data-target="all">Toutes</button>
<button class="tag-btn" data-tag={tag}> {
{tag} Object.keys(groupedTags).map(
</button> (cat) =>
)) cat !== "Général" && (
} <button class="cat-nav-btn" data-target={cat}>
{cat}
</button>
),
)
}
</div>
<div class="tags-reveal-area">
{
Object.entries(groupedTags).map(([category, categoryTags]) => (
<div
class="tag-subgroup"
id={`group-${category}`}
style={category === "Général" ? "" : "display: none;"}
>
{categoryTags.map((tag) => (
<button class="js-tag-filter" data-tag={tag}>
{tag.includes(":") ? tag.split(":")[1] : tag}
</button>
))}
</div>
))
}
</div>
</div> </div>
<div id="search-results" class="card-grid"> <div id="search-results" class="card-grid">
{ {
basePath === "codex" posts.map((post) => (
? posts.map((post) => ( <a href={`/${basePath}/${post.id}/`} class="gold-button">
<CodexCard entry={post as CollectionEntry<"codex">} /> <div class="title">{post.data.title}</div>
)) <p class="body">
: posts.map((post) => ( Publié le{" "}
<GoldButton {new Date(post.data.publishDate).toLocaleDateString(
href={`/${basePath}/${post.id}/`} "fr-FR",
title={post.data.title} )}
body={`Publié le ${new Date(post.data.publishDate).toLocaleDateString("fr-FR")}`} </p>
/> </a>
)) ))
} }
</div> </div>
<p id="no-results" class="no-results-message" style="display: none;">
Aucun parchemin ne correspond à votre recherche.
</p>
<div <div
id="search-data-bridge" id="search-data-bridge"
data-posts={JSON.stringify(posts)} data-posts={JSON.stringify(posts)}
data-basepath={basePath}
style="display: none;" style="display: none;"
> >
</div> </div>
</div> </div>
<script> <script>
// On récupère les données via le DOM pour que l'éditeur reste coloré function init() {
const bridge = document.getElementById("search-data-bridge"); const bridge = document.getElementById("search-data-bridge");
const posts = JSON.parse(bridge.dataset.posts); const input = document.getElementById(
const basePath = bridge.dataset.basepath; "search-input",
) as HTMLInputElement;
const container = document.getElementById("search-results");
if (!bridge || !input || !container) return;
const searchInput = document.body.querySelector("#search-input"); const posts = JSON.parse(bridge.getAttribute("data-posts") || "[]");
const resultsContainer = document.body.querySelector("#search-results"); let activeTag = "all";
const noResultsMessage = document.body.querySelector("#no-results");
const tagFiltersContainer = document.body.querySelector("#tag-filters");
let currentQuery = ""; function update() {
let activeTag = "all"; const query = input.value
.toLowerCase()
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "");
const filtered = posts.filter((p) => {
const mQuery = p.data.title.toLowerCase().includes(query);
const mTag =
activeTag === "all" || p.data.tags?.includes(activeTag);
return mQuery && mTag;
});
function normalizeText(text) { container!.innerHTML = filtered
if (!text) return ""; .map(
return text (p) => `
.toLowerCase() <a href="/journal/${p.id}/" class="gold-button">
.normalize("NFD") <div class="title">${p.data.title}</div>
.replace(/[\u0300-\u036f]/g, ""); <p class="body">Publié le ${new Date(p.data.publishDate).toLocaleDateString("fr-FR")}</p>
} </a>
`,
function createCodexCardHTML(post) { )
const statusClass = (post.data.status || "draft") .join("");
.toLowerCase() }
.replace(" ", "-");
return ` input.addEventListener("input", update);
<a href="/codex/${post.id}" class="codex-card">
<div class="card-header"> document.querySelectorAll(".cat-nav-btn").forEach((btn) => {
<div class="card-title">${post.data.title}</div> btn.addEventListener("click", () => {
<div class="card-subtitle">${post.data.subtitle || ""}</div> document
<span class="status status-${statusClass}">${post.data.status || "Inconnu"}</span> .querySelectorAll(".cat-nav-btn")
</div> .forEach((b) => b.classList.remove("active"));
<div class="skill-section"> btn.classList.add("active");
<div class="skill-header">Compétence</div> const target = btn.getAttribute("data-target");
<p class="skill-content">${post.data.mecanique || "En cours..."}</p> document.querySelectorAll(".tag-subgroup").forEach((g) => {
<div class="skill-header">Application</div> (g as HTMLElement).style.display =
<p class="skill-content">${post.data.vision3D || "À venir..."}</p> g.id === `group-${target}` ? "block" : "none";
</div> });
</a> if (target === "all") {
`; activeTag = "all";
} update();
}
function createGoldButtonHTML(post) { });
const dateStr = new Date(post.data.publishDate).toLocaleDateString( });
"fr-FR",
); document.querySelectorAll(".js-tag-filter").forEach((t) => {
return ` t.addEventListener("click", () => {
<a href="/${basePath}/${post.id}/" class="gold-button"> document
<div class="title">${post.data.title}</div> .querySelectorAll(".js-tag-filter")
<p class="body">Publié le ${dateStr}</p> .forEach((el) => el.classList.remove("active-tag"));
</a> t.classList.add("active-tag");
`; activeTag = (t as HTMLElement).dataset.tag || "all";
update();
});
});
} }
document.addEventListener("astro:page-load", init);
</script> </script>
<style> <style>
/* Conteneur des tags : centrage et espacement */
.tags-container {
display: flex;
justify-content: center;
gap: 0.6rem;
flex-wrap: wrap;
margin-bottom: 3rem;
width: 100%;
}
/* Le bouton de tag : on force les styles pour contrer Tailwind */
.tag-btn {
all: unset; /* On réinitialise les styles par défaut du navigateur */
display: inline-block;
cursor: pointer;
background-color: #f3ece0;
color: #4a4130;
padding: 0.4rem 1.2rem;
border-radius: 9999px; /* Forme pilule parfaite */
font-family: "Cinzel", serif;
font-size: 0.85rem;
border: 1px solid #dcd0b9;
transition: all 0.2s ease-in-out;
text-align: center;
}
/* État survolé */
.tag-btn:hover {
background-color: #fdf6e8;
border-color: #c89b3c;
color: #c89b3c;
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
/* État actif (celui qui est doré sur ton image 2) */
.tag-btn.active {
background-color: #c89b3c !important;
color: #ffffff !important;
border-color: #b8860b !important;
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2);
}
/* Centrage de la barre de recherche */
.search-wrapper { .search-wrapper {
margin-bottom: 2rem;
display: flex; display: flex;
justify-content: center; justify-content: center;
margin-bottom: 2.5rem;
width: 100%;
} }
#search-input { #search-input {
width: 100%; width: 100%;
max-width: 450px; max-width: 400px;
padding: 0.75rem 1.2rem; padding: 0.7rem 1rem;
border-radius: 20px;
border: 1px solid var(--color-gold);
outline: none;
font-family: inherit;
}
.filter-box {
border: 1px solid #dcd0b9; border: 1px solid #dcd0b9;
border-radius: 8px; border-radius: 8px;
background-color: rgba(253, 246, 232, 0.9); background: #fffaf0;
font-family: "EB Garamond", serif; margin-bottom: 2rem;
font-size: 1.1rem; overflow: hidden;
color: #4a4130;
outline: none;
transition: border-color 0.2s;
} }
.category-nav {
#search-input:focus { background: #f3ece0;
border-color: #c89b3c; padding: 0.5rem;
box-shadow: 0 0 0 2px rgba(200, 155, 60, 0.1); display: flex;
gap: 0.5rem;
flex-wrap: wrap;
align-items: center;
border-bottom: 1px solid #dcd0b9;
}
.nav-label {
font-family: "Cinzel", serif;
font-size: 0.75rem;
color: var(--color-gold-dark);
margin-right: 0.5rem;
}
.cat-nav-btn {
background: none;
border: none;
font-family: "Cinzel", serif;
font-size: 0.7rem;
cursor: pointer;
color: var(--color-ink);
}
.cat-nav-btn.active {
color: var(--color-gold-dark);
font-weight: bold;
border-bottom: 2px solid var(--color-gold);
}
.tags-reveal-area {
padding: 0.8rem;
min-height: 40px;
} }
</style> </style>

View File

@@ -2,7 +2,22 @@
title: "L'Éveil du Cul Brillant" title: "L'Éveil du Cul Brillant"
author: "G'Mas" author: "G'Mas"
publishDate: 2026-01-10 publishDate: 2026-01-10
tags: ["Yeuze-sur-Chenarde", "Exploration", "Gurdil", "Bulle", "Social", "Mystère"] tags: [
"lieu:Yeuze-sur-Chenarde",
"lieu:Grand-Chêne",
"lieu:Mine",
"aventurier:Gmas",
"aventurier:Nyrae",
"aventurier:Bulle",
"aventurier:Gurdil",
"aventurier:Jinn",
"perso:Kwel",
"perso:Mara",
"plot:Enfants-Disparus",
"plot:Cul-Brillant",
"plot:Mystère",
"lore:Légendes"
]
--- ---
### Chroniques de Yeuze-sur-Chenarde ### Chroniques de Yeuze-sur-Chenarde

View File

@@ -2,7 +2,21 @@
title: "Le Murmure des Rouages" title: "Le Murmure des Rouages"
author: "G'Mas" author: "G'Mas"
publishDate: 2026-01-18 publishDate: 2026-01-18
tags: ["Mine", "Combat", "Kruthik", "Nyrae", "Jinn", "Vase Grise"] tags: [
"lieu:Yeuze-sur-Chenarde",
"lieu:Mine",
"aventurier:Gmas",
"aventurier:Nyrae",
"aventurier:Bulle",
"aventurier:Gurdil",
"aventurier:Jinn",
"perso:Maielan",
"mobs:Kruthik",
"mobs:Vase-Grise",
"plot:Enfants-Disparus",
"plot:Cul-Brillant",
"event:Combat"
]
--- ---
### Les Griffes de l'Obscurité ### Les Griffes de l'Obscurité

View File

@@ -2,7 +2,25 @@
title: "Le printemps sanglant" title: "Le printemps sanglant"
author: "G'Mas" author: "G'Mas"
publishDate: 2026-02-08 publishDate: 2026-02-08
tags: ["Vampire","Enfant perdu", "Maçonnerie"] tags: [
"lieu:Yeuze-sur-Chenarde",
"lieu:Mine",
"lieu:Grand-Chêne",
"aventurier:Gmas",
"aventurier:Nyrae",
"aventurier:Bulle",
"aventurier:Gurdil",
"aventurier:Jinn",
"perso:Kwel",
"perso:Maielan",
"antagoniste:Constancia-Denney",
"plot:Enfants-Disparus",
"plot:Cul-Brillant",
"plot:Couronne-d-Argent",
"lore:Eladrin",
"event:Combat",
"event:Level-Up"
]
--- ---
## L'Ombre de Constancia ## L'Ombre de Constancia

View File

@@ -1,10 +1,8 @@
--- ---
import { ClientRouter } from "astro:transitions"; import { ClientRouter } from "astro:transitions";
interface Props { interface Props {
title: string; title: string;
} }
const { title } = Astro.props; const { title } = Astro.props;
--- ---
@@ -12,14 +10,8 @@ const { title } = Astro.props;
<html lang="fr"> <html lang="fr">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta <meta name="viewport" content="width=device-width, initial-scale=1" />
name="description" <title>{title} | Hebel</title>
content="Clone de AFK Journey avec Astro et Three.js"
/>
<meta name="viewport" content="width=device-width" initial-scale="1" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
<ClientRouter /> <ClientRouter />
</head> </head>
<body> <body>
@@ -30,283 +22,163 @@ const { title } = Astro.props;
</html> </html>
<style is:global> <style is:global>
@import url("https://fonts.googleapis.com/css2?family=Cinzel:wght@700&family=EB+Garamond&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Cinzel:wght@700&display=swap");
html { :root {
background-color: #fdf6e8; /* Ton crème/parchemin */ --color-parchment: #fdf6e8;
--color-ink: #4a4130;
--color-gold: #c89b3c;
--color-gold-dark: #b8860b;
--obsidian-border: #dcd0b9;
}
* {
box-sizing: border-box;
} }
body { body {
/* La couleur de fond est maintenant sur l'élément HTML */ background-color: var(--color-parchment);
background-color: transparent; color: var(--color-ink);
color: #4a4a4a; /* Calibri en priorité */
font-family: "EB Garamond", serif; font-family: "Calibri", "Candara", "Segoe UI", sans-serif;
margin: 0; margin: 0;
/* Ajout d'un padding en bas pour ne pas que la nav masque le contenu */ line-height: 1.6;
padding: 2rem 2rem 100px 2rem; overflow-x: hidden;
position: relative;
} }
/* Applique une texture de grain de papier en arrière-plan */ main {
body::before {
content: "";
position: fixed;
top: 0;
left: 0;
width: 100%; width: 100%;
height: 100%; max-width: 1000px;
/* Assurez-vous d'avoir une image de texture dans public/textures/ */ margin: 0 auto;
background-image: url("/textures/paper-grain.png"); padding: 1rem;
opacity: 0.5; /* J'augmente l'opacité pour qu'elle soit bien visible */
pointer-events: none;
z-index: -1;
} }
/* --- Styles des Titres --- */ /* --- TABLEAUX OBSIDIAN --- */
h1, table {
h2 {
font-family: "Cinzel", serif;
text-align: center;
color: #3a352a; /* Une couleur de texte sombre pour un bon contraste */
margin-bottom: 2rem; /* Espace après l'ornement */
}
/* Ajoute un ornement doré sous les titres */
h1::after,
h2::after {
content: "";
display: block;
width: 100px; /* Largeur de l'ornement */
height: 2px;
background: linear-gradient(
90deg,
transparent,
#c89b3c,
transparent
); /* Dégradé doré */
margin: 0.75rem auto 0; /* Espace entre le texte et l'ornement */
opacity: 0.8;
/* Animation pour "dessiner" la ligne */
transform: scaleX(0);
animation: drawLine 1s cubic-bezier(0.22, 1, 0.36, 1) 0.3s forwards;
}
/* On peut différencier légèrement le h2 */
h2 {
font-size: 1.8rem;
color: #4a4130;
}
/* --- Styles pour le contenu Markdown (.prose) --- */
.prose h3 {
font-family: "Cinzel", serif;
text-align: left;
font-size: 1.5rem;
border-bottom: 1px solid #dcd0b9;
padding-bottom: 0.5rem;
margin-top: 2.5rem;
}
/* On retire l'ornement pour les h3 */
.prose h3::after {
content: none;
}
.prose p {
line-height: 1.7;
font-size: 1.1rem;
}
.parchment-card a {
color: #c89b3c; /* Or vif */
text-decoration: none;
font-weight: bold;
transition: color 0.2s;
}
.parchment-card a:hover {
filter: brightness(1.2);
}
.prose ul {
list-style: none;
padding-left: 1rem;
}
.prose ul li::before {
content: "•";
color: #c89b3c;
font-weight: bold;
display: inline-block;
width: 1em;
margin-left: -1em;
}
.prose hr {
border: 0;
height: 2px;
background: linear-gradient(90deg, transparent, #c89b3c, transparent);
margin: 3rem 0;
}
.prose table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
margin: 2rem 0; margin: 1.5rem 0;
border: 1px solid var(--obsidian-border);
display: block;
overflow-x: auto;
} }
.prose th, th,
.prose td { td {
padding: 0.75rem; border: 1px solid var(--obsidian-border);
padding: 0.8rem;
text-align: left; text-align: left;
border-bottom: 1px solid #dcd0b9; /* Ligne de séparation parchemin */
} }
.prose th { th {
background: rgba(220, 208, 185, 0.3);
font-family: "Cinzel", serif; font-family: "Cinzel", serif;
background-color: rgba(
253,
246,
232,
0.5
); /* Fond de cellule plus clair */
} }
/* --- Styles pour GoldButton --- */ /* --- LISTES MOBILE --- */
.gold-button { ul,
background: linear-gradient( ol {
145deg, padding-left: 1.5rem;
#fefbf3, margin: 1rem 0;
#f8f1e4
); /* Dégradé parchemin clair */
border: 2px solid #c89b3c;
border-radius: 15px;
color: #4a4130; /* Texte sombre pour le contraste */
padding: 1.5rem;
text-align: center;
text-decoration: none;
transition: all 0.3s ease;
display: block;
box-shadow:
0 4px 15px rgba(0, 0, 0, 0.1),
inset 0 0 5px rgba(200, 155, 60, 0.1);
} }
li {
margin-bottom: 0.4rem;
}
@media (max-width: 640px) {
ul ul,
ol ol {
padding-left: 1rem;
}
main {
padding: 0.8rem;
}
}
/* --- CITATIONS (BLOCKQUOTES) --- */
blockquote {
margin: 1.5rem 0;
padding: 0.5rem 1rem;
border-left: 4px solid var(--color-gold);
background: rgba(200, 155, 60, 0.05);
font-style: italic;
}
/* --- STYLE DES TAGS --- */
/* On utilise une classe explicite pour éviter l'erreur de build */
.tag-link {
display: inline-block !important;
text-decoration: none !important;
background: white !important;
border: 1px solid var(--color-gold) !important;
padding: 0.2rem 0.7rem !important;
border-radius: 15px !important;
font-family: "Cinzel", serif !important;
font-size: 0.7rem !important;
color: var(--color-ink) !important;
margin: 0.2rem !important;
transition: 0.2s;
}
.tag-link:hover {
background: var(--color-gold) !important;
color: white !important;
}
/* STYLE DES CARTES (Gold Button) */
.gold-button {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background: #fffaf0; /* Fond parchemin clair */
border: 2px solid var(--color-gold);
border-radius: 12px;
padding: 1.5rem;
text-decoration: none !important;
transition: all 0.3s ease;
text-align: center;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
min-height: 120px;
}
.gold-button:hover { .gold-button:hover {
transform: translateY(-5px); transform: translateY(-5px);
filter: brightness(1.05); box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1);
box-shadow: border-color: var(--color-gold-dark);
0 8px 25px rgba(0, 0, 0, 0.15), background: white;
inset 0 0 10px rgba(200, 155, 60, 0.2);
border-color: #f0e6d2;
} }
.gold-button .title { .gold-button .title {
/* Ciblage plus spécifique */
font-family: "Cinzel", serif; font-family: "Cinzel", serif;
font-size: 1.5rem; font-size: 1.2rem;
color: #b8860b; /* Titre en couleur or */ color: var(--color-gold-dark);
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
text-transform: uppercase;
} }
.gold-button .body { .gold-button .body {
/* Ciblage plus spécifique */ font-family: "Calibri", sans-serif; /* Ta police demandée */
font-family: "EB Garamond", serif; font-size: 0.9rem;
font-size: 1rem; color: var(--color-ink);
color: #4a4130; /* Texte sombre pour le contraste */ opacity: 0.8;
margin: 0;
} }
/* GRILLE FLEXBOX */
.card-grid { .card-grid {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: center;
gap: 2rem; gap: 2rem;
padding: 0;
margin: 0;
}
/* --- Styles pour les Tags --- */
.tags-container {
display: flex;
justify-content: center; justify-content: center;
gap: 0.5rem; padding: 2rem 0;
flex-wrap: wrap;
margin-bottom: 2rem;
}
.tag {
background-color: rgba(200, 155, 60, 0.2);
color: #b8860b;
padding: 0.25rem 0.75rem;
border-radius: 15px;
font-size: 0.8rem;
font-family: "Cinzel", serif;
border: 1px solid rgba(200, 155, 60, 0.3);
text-decoration: none;
transition: all 0.2s ease;
}
.tag:hover {
background-color: #c89b3c;
color: #2c2a24;
} }
/* Keyframes pour l'animation de l'ornement */ .card-grid > * {
@keyframes drawLine { flex: 1 1 300px;
from { max-width: 400px;
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
} }
/* Animation pour la nouvelle page (le "pop-up") */ @media (max-width: 640px) {
@keyframes slide-in { .card-grid > * {
from { flex: 1 1 100%;
transform: translateY(15%) scale(0.95);
opacity: 0;
}
to {
transform: translateY(0) scale(1);
opacity: 1;
}
}
/* Animation pour l'ancienne page qui disparaît */
@keyframes fade-out {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
/* On applique nos animations aux pseudo-éléments de View Transitions */
::view-transition-new(root) {
animation: 0.4s cubic-bezier(0.22, 1, 0.36, 1) 0s 1 normal both running
slide-in;
}
::view-transition-old(root) {
animation: 0.2s ease-out 0s 1 normal both running fade-out;
}
/* --- Ajustements pour les écrans mobiles --- */
@media (max-width: 768px) {
body {
padding: 1.5rem 1rem 100px 1rem; /* Réduction du padding latéral */
}
h1 {
font-size: 2rem; /* Réduction de la taille des titres principaux */
}
.parchment-card {
padding: 1.5rem; /* Moins de padding dans les cartes */
}
.gold-button {
padding: 1rem; /* Boutons moins hauts */
}
.gold-button .title {
font-size: 1.2rem; /* Titres des boutons plus petits */
}
.card-grid {
gap: 1rem; /* Moins d'espace entre les cartes */
} }
} }
</style> </style>

25
src/pages/index.astro Normal file
View File

@@ -0,0 +1,25 @@
---
import { getCollection } from "astro:content";
import GameLayout from "../layouts/GameLayout.astro";
import ContentSearch from "../components/ContentSearch.astro";
// 1. Récupère TOUS les articles et les trie.
const allPosts = await getCollection("journal");
const sortedPosts = allPosts.sort(
(a, b) => b.data.publishDate.valueOf() - a.data.publishDate.valueOf(),
);
// 2. Extrait toutes les balises uniques
const allTags = [...new Set(allPosts.flatMap((post) => post.data.tags || []))];
---
<GameLayout title="Journal d'Aventure">
<h1>📖 Journal d'Aventure</h1>
<p style="text-align: center; margin-bottom: 3rem;">
Les chroniques de nos voyages. Utilisez la barre de recherche pour
trouver un récit.
</p>
<!-- 2. On passe tous les articles au composant client -->
<ContentSearch posts={sortedPosts} tags={allTags} basePath="journal" />
</GameLayout>

View File

@@ -1,5 +1,4 @@
--- ---
// 1. Ajoute "render" dans ton import
import { getCollection, render } from "astro:content"; import { getCollection, render } from "astro:content";
import GameLayout from "../../layouts/GameLayout.astro"; import GameLayout from "../../layouts/GameLayout.astro";
@@ -12,136 +11,114 @@ export async function getStaticPaths() {
} }
const { entry } = Astro.props; const { entry } = Astro.props;
if (!entry) return Astro.redirect("/404");
if (!entry) {
return Astro.redirect("/404");
}
// 2. Utilise la fonction render(entry) au lieu de entry.render()
const { Content } = await render(entry); const { Content } = await render(entry);
--- ---
<GameLayout title={entry.data.title}> <GameLayout title={entry.data.title}>
<main class="codex-entry-container"> <article class="journal-post">
<article class="parchment-card"> <header class="post-header">
<div class="header"> <a href="/" class="back-nav">← Retour au Journal</a>
<h1>{entry.data.title}</h1> <h1>{entry.data.title}</h1>
<p class="publish-date"> <p class="date">
Savoir acquis le : { Chroniqué le {
entry.data.publishDate.toLocaleDateString("fr-FR", { entry.data.publishDate.toLocaleDateString("fr-FR", {
year: "numeric", day: "numeric",
month: "long", month: "long",
day: "numeric", year: "numeric",
}) })
} }
</p> </p>
</div>
{ {
entry.data.tags && ( entry.data.tags && (
<div class="tags-container"> <div class="tags-row">
{entry.data.tags.map((tag) => ( {entry.data.tags.map((tag) => (
<a <a
href={`/journal/tags/${tag.toLowerCase()}`} href={`/journal/tags/${tag.toLowerCase()}`}
class="tag" class="tag-link"
> >
{tag} {tag.includes(":") ? tag.split(":")[1] : tag}
</a> </a>
))} ))}
</div> </div>
) )
} }
</header>
<div class="prose"> <hr class="separator" />
<Content />
</div>
<a href="/journal" class="back-link"> <div class="prose-content">
&larr; Retourner au Journal <Content />
</a> </div>
</article> </article>
</main>
</GameLayout> </GameLayout>
<style> <style>
.codex-entry-container { .journal-post {
max-width: 900px; background: white;
margin: 0 auto; border: 1px solid #dcd0b9;
padding: 2rem 1rem; border-radius: 8px;
padding: 2.5rem;
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.03);
} }
.parchment-card { .back-nav {
background: linear-gradient(145deg, #fefbf3, #f8f1e4); font-family: "Cinzel", serif;
border: 2px solid #dcd0b9; font-size: 0.8rem;
border-radius: 15px; color: #b8860b;
padding: 2rem 3rem; text-decoration: none;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05); display: block;
margin-bottom: 1.5rem;
} }
.header { .post-header {
text-align: center; text-align: center;
margin-bottom: 2rem; margin-bottom: 2rem;
} }
.header h1 { h1 {
margin-bottom: 0.5rem; margin: 0.5rem 0;
} font-family: "Cinzel", serif;
.header h2 { font-size: 2.2rem;
font-family: "EB Garamond", serif; color: #3a352a;
font-style: italic;
font-size: 1.4rem;
color: #4a4130;
margin-top: 0;
}
/* On retire l'ornement pour le h2 de la carte */
.header h2::after {
content: none;
} }
.publish-date { .date {
font-style: italic; font-style: italic;
opacity: 0.7; opacity: 0.7;
}
.transmutation-table {
margin: 2rem 0;
font-size: 0.9rem; font-size: 0.9rem;
background-color: rgba(253, 246, 232, 0.5); }
border: 1px solid #dcd0b9; .tags-row {
border-radius: 8px; margin-top: 1.2rem;
overflow: hidden;
} }
.table-row { .separator {
display: grid; border: 0;
grid-template-columns: 1fr 1.5fr 1.5fr; height: 1px;
gap: 1rem; background-image: linear-gradient(
padding: 0.75rem 1rem; to right,
transparent,
#dcd0b9,
transparent
);
margin: 2.5rem 0;
} }
.table-row.header { .prose-content {
background-color: rgba(220, 208, 185, 0.4); font-size: 1.1rem;
} }
.table-row:not(.header) { @media (max-width: 640px) {
border-top: 1px solid #dcd0b9; .journal-post {
} padding: 1.5rem 1rem;
border: none;
.col-title { background: transparent;
font-family: "Cinzel", serif; box-shadow: none;
font-weight: bold; }
color: #4a4130; h1 {
} font-size: 1.6rem;
}
.back-link {
display: inline-block;
margin-top: 3rem;
font-family: "Cinzel", serif;
color: #b8860b;
text-decoration: none;
transition: all 0.2s ease;
}
.back-link:hover {
transform: translateX(-5px);
} }
</style> </style>

View File

@@ -1,26 +0,0 @@
---
import { getCollection } from 'astro:content';
import GameLayout from '../../layouts/GameLayout.astro';
import ContentSearch from '../../components/ContentSearch.astro';
// 1. Récupère TOUS les articles et les trie.
const allPosts = await getCollection('journal');
const sortedPosts = allPosts.sort(
(a, b) => b.data.publishDate.valueOf() - a.data.publishDate.valueOf()
);
// 2. Extrait toutes les balises uniques
const allTags = [...new Set(allPosts.flatMap(post => post.data.tags || []))];
---
<GameLayout title="Journal d'Aventure">
<h1>📖 Journal d'Aventure</h1>
<p style="text-align: center; margin-bottom: 3rem;">
Les chroniques de nos voyages. Utilisez la barre de recherche pour trouver un récit.
</p>
<!-- 2. On passe tous les articles au composant client -->
<ContentSearch posts={sortedPosts} tags={allTags} basePath="journal" />
</GameLayout>

View File

@@ -1,47 +1,75 @@
--- ---
// [tag].astro
import { getCollection } from "astro:content"; import { getCollection } from "astro:content";
import GameLayout from "../../../layouts/GameLayout.astro"; import GameLayout from "../../../layouts/GameLayout.astro";
import GoldButton from "../../../components/ui/GoldButton.astro"; import GoldButton from "../../../components/ui/GoldButton.astro";
export async function getStaticPaths() { export async function getStaticPaths() {
const allPosts = await getCollection("journal"); const allPosts = await getCollection("journal");
const allTags = [ const tags = [...new Set(allPosts.flatMap((p) => p.data.tags || []))];
...new Set(allPosts.flatMap((post) => post.data.tags || [])), return tags.map((tag) => ({
]; params: { tag: tag.toLowerCase() },
props: {
return allTags.map((tag) => { posts: allPosts.filter((p) => p.data.tags?.includes(tag)),
const filteredPosts = allPosts.filter((post) => originalTag: tag,
post.data.tags?.includes(tag), },
); }));
return {
params: { tag },
props: { posts: filteredPosts },
};
});
} }
const { posts, originalTag } = Astro.props;
const { tag } = Astro.params;
const { posts } = Astro.props;
--- ---
<GameLayout title={`Récits: ${tag}`}> <GameLayout title={`Archive : ${originalTag}`}>
<h1>Récits avec la balise : <span class="tag-title">{tag}</span></h1> <div class="tag-page-container">
<a href="/" class="back-link">← Retour au Journal</a>
<div class="card-grid"> <div class="tag-header">
{ <h1>
posts.map((post) => ( Récits marqués : <span
<GoldButton >{
href={`/journal/${post.id}/`} originalTag.includes(":")
title={post.data.title} ? originalTag.split(":")[1]
body={`Publié le ${post.data.publishDate.toLocaleDateString("fr-FR")}`} : originalTag
/> }</span
)) >
} </h1>
</div>
<div class="card-grid">
{
posts.map((post) => (
<GoldButton
href={`/journal/${post.id}/`}
title={post.data.title}
body={`Chronique du ${new Date(post.data.publishDate).toLocaleDateString("fr-FR")}`}
/>
))
}
</div>
</div> </div>
</GameLayout> </GameLayout>
<style> <style>
.tag-title { .tag-page-container {
color: #c89b3c; max-width: 1000px;
margin: 0 auto;
padding: 2rem 1rem;
}
.back-link {
color: var(--color-gold-dark);
text-decoration: none;
font-family: "Cinzel", serif;
font-size: 0.9rem;
}
.tag-header h1 {
font-size: 1.8rem; /* Titre réduit */
margin: 1.5rem 0 3rem;
}
.tag-header span {
color: var(--color-gold);
}
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 2rem;
} }
</style> </style>