feat(theme): color package Electric Teal + bascule nom IA qu'à
This commit is contained in:
parent
229f7e879e
commit
cd6a641d72
@ -1,4 +1,4 @@
|
|||||||
# IA qu'à... — Blog
|
# IA qu'à — Blog
|
||||||
|
|
||||||
Blog statique personnel sur l'IA en PME.
|
Blog statique personnel sur l'IA en PME.
|
||||||
|
|
||||||
|
|||||||
@ -3,12 +3,104 @@ import mdx from "@astrojs/mdx";
|
|||||||
import sitemap from "@astrojs/sitemap";
|
import sitemap from "@astrojs/sitemap";
|
||||||
import tailwind from "@astrojs/tailwind";
|
import tailwind from "@astrojs/tailwind";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "IA qu'à" Atom Syntax theme — Electric Teal color package §3/§4.
|
||||||
|
* Dark Slate canvas with the 7-color diagnostic spectrum mapped onto
|
||||||
|
* standard code tokens. Used for all fenced code blocks (light & dark site).
|
||||||
|
*/
|
||||||
|
const iaQuaSyntax = {
|
||||||
|
name: "ia-qua-atom-syntax",
|
||||||
|
type: "dark",
|
||||||
|
colors: {
|
||||||
|
"editor.background": "#0F172A",
|
||||||
|
"editor.foreground": "#F1F5F9",
|
||||||
|
},
|
||||||
|
settings: [
|
||||||
|
{ settings: { background: "#0F172A", foreground: "#F1F5F9" } },
|
||||||
|
// comment / metadata → Orchid
|
||||||
|
{
|
||||||
|
scope: ["comment", "punctuation.definition.comment", "string.comment"],
|
||||||
|
settings: { foreground: "#8B5CF6", fontStyle: "italic" },
|
||||||
|
},
|
||||||
|
// keyword / control → Ember
|
||||||
|
{
|
||||||
|
scope: [
|
||||||
|
"keyword",
|
||||||
|
"keyword.control",
|
||||||
|
"storage",
|
||||||
|
"storage.type",
|
||||||
|
"storage.modifier",
|
||||||
|
"keyword.operator.new",
|
||||||
|
"entity.name.tag",
|
||||||
|
],
|
||||||
|
settings: { foreground: "#FF6B00" },
|
||||||
|
},
|
||||||
|
// string / regex → Gold
|
||||||
|
{
|
||||||
|
scope: [
|
||||||
|
"string",
|
||||||
|
"string.quoted",
|
||||||
|
"string.template",
|
||||||
|
"constant.other.symbol",
|
||||||
|
"string.regexp",
|
||||||
|
],
|
||||||
|
settings: { foreground: "#F59E0B" },
|
||||||
|
},
|
||||||
|
// support / type / class → Emerald
|
||||||
|
{
|
||||||
|
scope: [
|
||||||
|
"entity.name.class",
|
||||||
|
"entity.name.type",
|
||||||
|
"support.type",
|
||||||
|
"support.class",
|
||||||
|
"storage.type.class",
|
||||||
|
"entity.other.attribute-name",
|
||||||
|
],
|
||||||
|
settings: { foreground: "#10B981" },
|
||||||
|
},
|
||||||
|
// entity / function → Indigo
|
||||||
|
{
|
||||||
|
scope: [
|
||||||
|
"entity.name.function",
|
||||||
|
"support.function",
|
||||||
|
"meta.function-call",
|
||||||
|
"variable.function",
|
||||||
|
],
|
||||||
|
settings: { foreground: "#6366F1" },
|
||||||
|
},
|
||||||
|
// variable / parameter & numerics → Sky
|
||||||
|
{
|
||||||
|
scope: [
|
||||||
|
"variable",
|
||||||
|
"variable.other",
|
||||||
|
"variable.parameter",
|
||||||
|
"support.variable",
|
||||||
|
"constant.numeric",
|
||||||
|
"constant.language",
|
||||||
|
"constant",
|
||||||
|
],
|
||||||
|
settings: { foreground: "#0EA5E9" },
|
||||||
|
},
|
||||||
|
// exception / invalid → Rose
|
||||||
|
{
|
||||||
|
scope: ["invalid", "invalid.illegal", "invalid.deprecated"],
|
||||||
|
settings: { foreground: "#F43F5E" },
|
||||||
|
},
|
||||||
|
// punctuation kept muted
|
||||||
|
{
|
||||||
|
scope: ["punctuation", "meta.brace", "punctuation.separator"],
|
||||||
|
settings: { foreground: "#94A3B8" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: "https://ia-qua.fr", // À changer si autre domaine
|
site: "https://ia-qua.fr", // À changer si autre domaine
|
||||||
integrations: [mdx(), sitemap(), tailwind()],
|
integrations: [mdx(), sitemap(), tailwind()],
|
||||||
markdown: {
|
markdown: {
|
||||||
shikiConfig: {
|
shikiConfig: {
|
||||||
theme: "github-dark",
|
theme: iaQuaSyntax,
|
||||||
|
wrap: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# ─── deploy.sh — Blog IA qu'à... ───
|
# ─── deploy.sh — Blog IA qu'à ───
|
||||||
# Usage : sudo bash deploy.sh
|
# Usage : sudo bash deploy.sh
|
||||||
# Depuis le répertoire du repo sur Cloudbreak
|
# Depuis le répertoire du repo sur Cloudbreak
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,10 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||||
<rect width="32" height="32" rx="6" fill="#1B2A4A"/>
|
<defs>
|
||||||
<text x="16" y="22" text-anchor="middle" font-family="Georgia, serif" font-weight="bold" font-size="16" fill="#6B8F71">IA</text>
|
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
||||||
|
<stop offset="0%" stop-color="#06B6D4"/>
|
||||||
|
<stop offset="100%" stop-color="#3B82F6"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect width="32" height="32" rx="7" fill="url(#g)"/>
|
||||||
|
<text x="16" y="22" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-weight="800" font-size="17" fill="#FFFFFF">IA</text>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 254 B After Width: | Height: | Size: 460 B |
14
public/logo.svg
Normal file
14
public/logo.svg
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 196 48" role="img" aria-label="IA qu'à">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="iaGrad" x1="0" y1="0" x2="1" y2="1">
|
||||||
|
<stop offset="0%" stop-color="#06B6D4"/>
|
||||||
|
<stop offset="100%" stop-color="#3B82F6"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<text x="0" y="37" font-family="Inter, system-ui, -apple-system, sans-serif" font-size="38" letter-spacing="-0.5">
|
||||||
|
<tspan fill="url(#iaGrad)" font-weight="800">IA</tspan>
|
||||||
|
<tspan fill="#334155" font-weight="700" dx="6">qu</tspan>
|
||||||
|
<tspan fill="url(#iaGrad)" font-weight="600">’</tspan>
|
||||||
|
<tspan fill="#334155" font-weight="700">à</tspan>
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 669 B |
@ -20,21 +20,44 @@ const formattedDate = pubDate.toLocaleDateString("fr-FR", {
|
|||||||
});
|
});
|
||||||
---
|
---
|
||||||
|
|
||||||
<article class="bg-white rounded-xl border border-border overflow-hidden transition-all hover:-translate-y-1 hover:shadow-lg hover:shadow-blue-night/5">
|
<article
|
||||||
|
class="bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-slate-800 overflow-hidden transition-all hover:-translate-y-1 hover:border-brand-cyan/50 hover:shadow-lg hover:shadow-brand-cyan/10"
|
||||||
|
>
|
||||||
<a href={`/blog/${slug}`} class="no-underline block">
|
<a href={`/blog/${slug}`} class="no-underline block">
|
||||||
{heroImage ? (
|
{
|
||||||
<img src={heroImage} alt={title} class="w-full h-48 object-cover" />
|
heroImage ? (
|
||||||
) : (
|
<img src={heroImage} alt={title} class="w-full h-48 object-cover" />
|
||||||
<div class="w-full h-48 bg-gradient-to-br from-blue-night to-blue-mid flex items-center justify-center">
|
) : (
|
||||||
<span class="text-white/10 text-6xl">◉</span>
|
<div class="w-full h-48 bg-brand-gradient flex items-center justify-center">
|
||||||
</div>
|
<span class="text-white/20 text-6xl">◉</span>
|
||||||
)}
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
<div class="p-5">
|
<div class="p-5">
|
||||||
{series && <div class="mb-2"><SeriesBadge name={series.name} part={series.part} total={series.total} /></div>}
|
{
|
||||||
<span class="text-xs font-semibold text-sage uppercase tracking-wide">{category}</span>
|
series && (
|
||||||
<h3 class="font-heading text-blue-night text-lg font-semibold leading-snug mt-1 mb-2">{title}</h3>
|
<div class="mb-2">
|
||||||
<p class="text-sm text-text-light line-clamp-2 mb-3">{description}</p>
|
<SeriesBadge
|
||||||
<span class="text-xs text-text-light">{formattedDate}</span>
|
name={series.name}
|
||||||
|
part={series.part}
|
||||||
|
total={series.total}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<span
|
||||||
|
class="text-xs font-semibold text-brand-deep dark:text-brand-cyan uppercase tracking-wide"
|
||||||
|
>{category}</span
|
||||||
|
>
|
||||||
|
<h3
|
||||||
|
class="font-heading text-slate-900 dark:text-slate-50 text-lg font-semibold leading-snug mt-1 mb-2"
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</h3>
|
||||||
|
<p class="text-sm text-slate-500 dark:text-slate-400 line-clamp-2 mb-3">
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
|
<span class="text-xs text-slate-400 dark:text-slate-500">{formattedDate}</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
44
src/components/BrandLogo.astro
Normal file
44
src/components/BrandLogo.astro
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
/**
|
||||||
|
* BrandLogo — wordmark "IA qu'à" (color package §1, palette Classic Electric Teal).
|
||||||
|
* "IA" + apostrophe en dégradé cyan→bleu, "qu'à" en slate adaptatif (clair/sombre).
|
||||||
|
* Double sens assumé : "IA qu'à" ↔ "Y'a qu'à". Rendu en Inter (police du site).
|
||||||
|
*/
|
||||||
|
interface Props {
|
||||||
|
class?: string;
|
||||||
|
}
|
||||||
|
const { class: className = "h-7 w-auto" } = Astro.props;
|
||||||
|
// id unique pour éviter les collisions de gradient si plusieurs instances
|
||||||
|
const gid = "iaGrad-" + Math.random().toString(36).slice(2, 8);
|
||||||
|
---
|
||||||
|
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 196 48"
|
||||||
|
role="img"
|
||||||
|
aria-label="IA qu'à"
|
||||||
|
class={className}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id={gid} x1="0" y1="0" x2="1" y2="1">
|
||||||
|
<stop offset="0%" stop-color="#06B6D4"></stop>
|
||||||
|
<stop offset="100%" stop-color="#3B82F6"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<text
|
||||||
|
x="0"
|
||||||
|
y="37"
|
||||||
|
font-family="Inter, system-ui, -apple-system, sans-serif"
|
||||||
|
font-size="38"
|
||||||
|
letter-spacing="-0.5"
|
||||||
|
>
|
||||||
|
<tspan fill={`url(#${gid})`} font-weight="800">IA</tspan>
|
||||||
|
<tspan
|
||||||
|
class="fill-slate-700 dark:fill-slate-100"
|
||||||
|
font-weight="700"
|
||||||
|
dx="6">qu</tspan
|
||||||
|
>
|
||||||
|
<tspan fill={`url(#${gid})`} font-weight="600">’</tspan>
|
||||||
|
<tspan class="fill-slate-700 dark:fill-slate-100" font-weight="700">à</tspan>
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
@ -2,12 +2,20 @@
|
|||||||
const year = new Date().getFullYear();
|
const year = new Date().getFullYear();
|
||||||
---
|
---
|
||||||
|
|
||||||
<footer class="max-w-6xl mx-auto mt-16 px-6 py-8 border-t border-border text-center text-sm text-text-light">
|
<footer
|
||||||
|
class="max-w-6xl mx-auto mt-16 px-6 py-8 border-t border-slate-200 dark:border-slate-800 text-center text-sm text-slate-500 dark:text-slate-400"
|
||||||
|
>
|
||||||
<p>
|
<p>
|
||||||
<strong class="font-heading text-blue-night">IA qu'à...</strong> — Journal de bord d'un DSI en PME face à l'IA
|
<strong class="font-heading text-slate-900 dark:text-slate-100"
|
||||||
|
>IA qu'à</strong
|
||||||
|
> — Journal d'un DSI de PME
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-2">
|
<p class="mt-2">
|
||||||
© {year} — Fait avec <a href="https://astro.build" class="text-sage no-underline hover:underline">Astro</a>
|
© {year} — Fait avec <a
|
||||||
|
href="https://astro.build"
|
||||||
|
class="text-brand-deep dark:text-brand-cyan no-underline hover:underline"
|
||||||
|
>Astro</a
|
||||||
|
>
|
||||||
· Self-hosted
|
· Self-hosted
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
---
|
---
|
||||||
|
import BrandLogo from "./BrandLogo.astro";
|
||||||
|
|
||||||
const currentPath = Astro.url.pathname;
|
const currentPath = Astro.url.pathname;
|
||||||
|
|
||||||
const links = [
|
const links = [
|
||||||
@ -11,57 +13,132 @@ const links = [
|
|||||||
];
|
];
|
||||||
---
|
---
|
||||||
|
|
||||||
<nav class="bg-white border-b border-border sticky top-0 z-50">
|
<nav
|
||||||
|
class="sticky top-0 z-50 border-b border-slate-200 dark:border-slate-800 bg-canvas-light/80 dark:bg-canvas-dark/80 backdrop-blur-md"
|
||||||
|
>
|
||||||
<div class="max-w-6xl mx-auto px-6 py-4 flex items-center justify-between">
|
<div class="max-w-6xl mx-auto px-6 py-4 flex items-center justify-between">
|
||||||
<a href="/" class="font-heading font-extrabold text-xl text-blue-night no-underline">
|
<a href="/" class="no-underline shrink-0" aria-label="Accueil — IA qu'à">
|
||||||
IA qu'à<span class="text-sage">...</span>
|
<BrandLogo class="h-7 w-auto" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- Desktop -->
|
<div class="flex items-center gap-6">
|
||||||
<ul class="hidden md:flex gap-8 list-none">
|
<!-- Desktop -->
|
||||||
{links.map((link) => (
|
<ul class="hidden md:flex gap-7 list-none">
|
||||||
<li>
|
{
|
||||||
<a
|
links.map((link) => (
|
||||||
href={link.href}
|
<li>
|
||||||
class:list={[
|
<a
|
||||||
"text-sm font-medium no-underline transition-colors",
|
href={link.href}
|
||||||
currentPath === link.href
|
class:list={[
|
||||||
? "text-blue-night border-b-2 border-sage pb-0.5"
|
"text-sm font-medium no-underline transition-colors",
|
||||||
: "text-text-light hover:text-blue-night",
|
currentPath === link.href
|
||||||
]}
|
? "text-slate-900 dark:text-white border-b-2 border-brand-cyan pb-0.5"
|
||||||
>
|
: "text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white",
|
||||||
{link.label}
|
]}
|
||||||
</a>
|
>
|
||||||
</li>
|
{link.label}
|
||||||
))}
|
</a>
|
||||||
</ul>
|
</li>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
|
||||||
<!-- Mobile toggle -->
|
<!-- Theme toggle -->
|
||||||
<button id="menu-toggle" class="md:hidden text-blue-night" aria-label="Menu">
|
<button
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
id="theme-toggle"
|
||||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
class="text-slate-500 dark:text-slate-400 hover:text-brand-cyan dark:hover:text-brand-cyan transition-colors"
|
||||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
aria-label="Basculer le thème clair / sombre"
|
||||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
>
|
||||||
</svg>
|
<!-- soleil (visible en mode sombre) -->
|
||||||
</button>
|
<svg
|
||||||
|
class="hidden dark:block"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<circle cx="12" cy="12" r="4"></circle>
|
||||||
|
<path
|
||||||
|
d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<!-- lune (visible en mode clair) -->
|
||||||
|
<svg
|
||||||
|
class="block dark:hidden"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Mobile toggle -->
|
||||||
|
<button
|
||||||
|
id="menu-toggle"
|
||||||
|
class="md:hidden text-slate-700 dark:text-slate-200"
|
||||||
|
aria-label="Menu"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||||
|
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||||
|
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Mobile menu -->
|
<!-- Mobile menu -->
|
||||||
<div id="mobile-menu" class="hidden md:hidden border-t border-border px-6 py-4">
|
<div
|
||||||
|
id="mobile-menu"
|
||||||
|
class="hidden md:hidden border-t border-slate-200 dark:border-slate-800 px-6 py-4"
|
||||||
|
>
|
||||||
<ul class="flex flex-col gap-4 list-none">
|
<ul class="flex flex-col gap-4 list-none">
|
||||||
{links.map((link) => (
|
{
|
||||||
<li>
|
links.map((link) => (
|
||||||
<a href={link.href} class="text-sm font-medium text-text-light no-underline hover:text-blue-night">
|
<li>
|
||||||
{link.label}
|
<a
|
||||||
</a>
|
href={link.href}
|
||||||
</li>
|
class="text-sm font-medium text-slate-500 dark:text-slate-400 no-underline hover:text-slate-900 dark:hover:text-white"
|
||||||
))}
|
>
|
||||||
|
{link.label}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))
|
||||||
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
// Menu mobile
|
||||||
document.getElementById("menu-toggle")?.addEventListener("click", () => {
|
document.getElementById("menu-toggle")?.addEventListener("click", () => {
|
||||||
document.getElementById("mobile-menu")?.classList.toggle("hidden");
|
document.getElementById("mobile-menu")?.classList.toggle("hidden");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Bascule de thème clair / sombre (persistée)
|
||||||
|
document.getElementById("theme-toggle")?.addEventListener("click", () => {
|
||||||
|
const root = document.documentElement;
|
||||||
|
const isDark = root.classList.toggle("dark");
|
||||||
|
localStorage.setItem("theme", isDark ? "dark" : "light");
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -9,15 +9,30 @@ const { name, part, total } = Astro.props;
|
|||||||
const dots = Array.from({ length: total }, (_, i) => i < part);
|
const dots = Array.from({ length: total }, (_, i) => i < part);
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="inline-flex items-center gap-2 bg-sage-light text-sage text-xs font-semibold px-3 py-1 rounded-full">
|
<div
|
||||||
<svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
class="inline-flex items-center gap-2 bg-brand-gradient-soft text-brand-deep dark:text-brand-cyan ring-1 ring-brand-cyan/20 text-xs font-semibold px-3 py-1 rounded-full"
|
||||||
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20" />
|
>
|
||||||
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z" />
|
<svg
|
||||||
|
class="w-3.5 h-3.5"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path>
|
||||||
|
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
<span>Série : {name}</span>
|
<span>Série : {name}</span>
|
||||||
<div class="flex gap-0.5 ml-1">
|
<div class="flex gap-0.5 ml-1">
|
||||||
{dots.map((filled) => (
|
{
|
||||||
<div class:list={["w-1.5 h-1.5 rounded-full bg-sage", filled ? "opacity-100" : "opacity-30"]} />
|
dots.map((filled) => (
|
||||||
))}
|
<div
|
||||||
|
class:list={[
|
||||||
|
"w-1.5 h-1.5 rounded-full bg-brand-cyan",
|
||||||
|
filled ? "opacity-100" : "opacity-30",
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -23,51 +23,53 @@ const seriesPosts = allPosts.sort(
|
|||||||
);
|
);
|
||||||
---
|
---
|
||||||
|
|
||||||
{seriesPosts.length > 1 && (
|
{
|
||||||
<aside class="my-8 p-6 bg-white border border-border rounded-xl">
|
seriesPosts.length > 1 && (
|
||||||
<h3 class="font-heading text-blue-night text-lg font-semibold mb-1">
|
<aside class="my-8 p-6 bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-xl">
|
||||||
Série : {seriesName}
|
<h3 class="font-heading text-slate-900 dark:text-slate-50 text-lg font-semibold mb-1">
|
||||||
</h3>
|
Série : {seriesName}
|
||||||
<p class="text-text-light text-sm mb-4">
|
</h3>
|
||||||
Article {currentPart} sur {seriesPosts.length}
|
<p class="text-slate-500 dark:text-slate-400 text-sm mb-4">
|
||||||
</p>
|
Article {currentPart} sur {seriesPosts.length}
|
||||||
|
</p>
|
||||||
|
|
||||||
<div class="w-full h-1 bg-border rounded-full mb-4 overflow-hidden">
|
<div class="w-full h-1 bg-slate-200 dark:bg-slate-800 rounded-full mb-4 overflow-hidden">
|
||||||
<div
|
<div
|
||||||
class="h-full bg-sage rounded-full transition-all"
|
class="h-full bg-brand-gradient rounded-full transition-all"
|
||||||
style={`width: ${(currentPart / seriesPosts.length) * 100}%`}
|
style={`width: ${(currentPart / seriesPosts.length) * 100}%`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ol class="list-none space-y-2">
|
<ol class="list-none space-y-2">
|
||||||
{seriesPosts.map((post) => {
|
{seriesPosts.map((post) => {
|
||||||
const isCurrent = post.data.series?.part === currentPart;
|
const isCurrent = post.data.series?.part === currentPart;
|
||||||
return (
|
return (
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href={`/blog/${post.id}`}
|
href={`/blog/${post.id}`}
|
||||||
class:list={[
|
|
||||||
"flex items-center gap-3 py-2 px-3 rounded-lg text-sm no-underline transition-colors",
|
|
||||||
isCurrent
|
|
||||||
? "bg-sage-light text-sage font-semibold"
|
|
||||||
: "text-text-light hover:bg-sage-light/50 hover:text-blue-night",
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class:list={[
|
class:list={[
|
||||||
"flex-shrink-0 w-6 h-6 rounded-full flex items-center justify-center text-xs font-bold",
|
"flex items-center gap-3 py-2 px-3 rounded-lg text-sm no-underline transition-colors",
|
||||||
isCurrent
|
isCurrent
|
||||||
? "bg-sage text-white"
|
? "bg-brand-gradient-soft text-brand-deep dark:text-brand-cyan font-semibold"
|
||||||
: "bg-border text-text-light",
|
: "text-slate-500 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-800 hover:text-slate-900 dark:hover:text-white",
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{post.data.series?.part}
|
<span
|
||||||
</span>
|
class:list={[
|
||||||
<span>{post.data.title}</span>
|
"flex-shrink-0 w-6 h-6 rounded-full flex items-center justify-center text-xs font-bold",
|
||||||
</a>
|
isCurrent
|
||||||
</li>
|
? "bg-brand-gradient text-white"
|
||||||
);
|
: "bg-slate-200 dark:bg-slate-700 text-slate-500 dark:text-slate-300",
|
||||||
})}
|
]}
|
||||||
</ol>
|
>
|
||||||
</aside>
|
{post.data.series?.part}
|
||||||
)}
|
</span>
|
||||||
|
<span>{post.data.title}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ol>
|
||||||
|
</aside>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@ -23,11 +23,26 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site);
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
|
||||||
|
<!-- Thème clair/sombre : appliqué avant le paint pour éviter le flash -->
|
||||||
|
<script is:inline>
|
||||||
|
(() => {
|
||||||
|
const stored = localStorage.getItem("theme");
|
||||||
|
const prefersDark = window.matchMedia(
|
||||||
|
"(prefers-color-scheme: dark)"
|
||||||
|
).matches;
|
||||||
|
if (stored === "dark" || (!stored && prefersDark)) {
|
||||||
|
document.documentElement.classList.add("dark");
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<meta name="theme-color" content="#06B6D4" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||||
|
|
||||||
<!-- SEO -->
|
<!-- SEO -->
|
||||||
<title>{title} | IA qu'à...</title>
|
<title>{title} | IA qu'à</title>
|
||||||
<meta name="description" content={description} />
|
<meta name="description" content={description} />
|
||||||
<link rel="canonical" href={canonicalURL} />
|
<link rel="canonical" href={canonicalURL} />
|
||||||
|
|
||||||
|
|||||||
@ -32,14 +32,14 @@ const formattedDate = pubDate.toLocaleDateString("fr-FR", {
|
|||||||
<SeriesBadge name={series.name} part={series.part} total={series.total} />
|
<SeriesBadge name={series.name} part={series.part} total={series.total} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<span class="text-xs font-semibold text-sage uppercase tracking-wide">
|
<span class="text-xs font-semibold text-brand-deep dark:text-brand-cyan uppercase tracking-wide">
|
||||||
{category}
|
{category}
|
||||||
</span>
|
</span>
|
||||||
<h1 class="font-heading text-blue-night text-4xl font-extrabold leading-tight mt-2 mb-3">
|
<h1 class="font-heading text-slate-900 dark:text-slate-50 text-4xl font-extrabold leading-tight mt-2 mb-3">
|
||||||
{title}
|
{title}
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-text-light">{description}</p>
|
<p class="text-slate-500 dark:text-slate-400">{description}</p>
|
||||||
<div class="flex items-center gap-2 mt-4 text-sm text-text-light">
|
<div class="flex items-center gap-2 mt-4 text-sm text-slate-400 dark:text-slate-500">
|
||||||
<time datetime={pubDate.toISOString()}>{formattedDate}</time>
|
<time datetime={pubDate.toISOString()}>{formattedDate}</time>
|
||||||
{updatedDate && (
|
{updatedDate && (
|
||||||
<>
|
<>
|
||||||
@ -62,7 +62,7 @@ const formattedDate = pubDate.toLocaleDateString("fr-FR", {
|
|||||||
<img
|
<img
|
||||||
src={heroImage}
|
src={heroImage}
|
||||||
alt={title}
|
alt={title}
|
||||||
class="w-full rounded-xl mb-8"
|
class="w-full rounded-xl mb-8 ring-1 ring-slate-200 dark:ring-slate-800"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import BaseLayout from "../layouts/BaseLayout.astro";
|
|||||||
|
|
||||||
<BaseLayout title="À propos">
|
<BaseLayout title="À propos">
|
||||||
<article class="max-w-3xl mx-auto px-6 py-16">
|
<article class="max-w-3xl mx-auto px-6 py-16">
|
||||||
<h1 class="font-heading text-blue-night text-4xl font-extrabold leading-tight mb-6">
|
<h1 class="font-heading text-slate-900 dark:text-slate-50 text-4xl font-extrabold leading-tight mb-6">
|
||||||
À propos
|
À propos
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
|
|||||||
@ -34,79 +34,96 @@ const seriesList = Array.from(seriesMap.values());
|
|||||||
<BaseLayout title="Accueil">
|
<BaseLayout title="Accueil">
|
||||||
<!-- Hero -->
|
<!-- Hero -->
|
||||||
<section class="max-w-3xl mx-auto text-center px-6 pt-16 pb-10">
|
<section class="max-w-3xl mx-auto text-center px-6 pt-16 pb-10">
|
||||||
<h1 class="font-heading text-blue-night text-4xl md:text-5xl font-extrabold leading-tight mb-4">
|
<h1
|
||||||
L'IA en PME,<br />sans bullshit.
|
class="font-heading text-slate-900 dark:text-slate-50 text-4xl md:text-5xl font-extrabold leading-tight mb-4"
|
||||||
|
>
|
||||||
|
L'IA en PME,<br /><span class="text-gradient">sans bullshit.</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-lg text-text-light max-w-xl mx-auto">
|
<p class="text-lg text-slate-500 dark:text-slate-400 max-w-xl mx-auto">
|
||||||
Journal de bord d'un DSI qui teste, bidouille, plante et recommence.
|
Journal de bord d'un DSI qui teste, bidouille, plante et recommence. Ce
|
||||||
Ce qui marche vraiment, ce qui ne marche pas, et pourquoi.
|
qui marche vraiment, ce qui ne marche pas, et pourquoi.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Catégories -->
|
<!-- Catégories -->
|
||||||
<div class="flex justify-center gap-3 flex-wrap px-6 mb-10">
|
<div class="flex justify-center gap-3 flex-wrap px-6 mb-10">
|
||||||
{categories.map((cat) => (
|
{
|
||||||
<a
|
categories.map((cat) => (
|
||||||
href={`/categorie/${cat.toLowerCase().replace(/ & /g, "-").replace(/ /g, "-")}`}
|
<a
|
||||||
class="px-4 py-1.5 rounded-full text-xs font-medium border border-border bg-white text-text-light no-underline hover:bg-sage-light hover:border-sage hover:text-sage transition-colors"
|
href={`/categorie/${cat.toLowerCase().replace(/ & /g, "-").replace(/ /g, "-")}`}
|
||||||
>
|
class="px-4 py-1.5 rounded-full text-xs font-medium border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 text-slate-500 dark:text-slate-400 no-underline hover:border-brand-cyan hover:text-brand-deep dark:hover:text-brand-cyan transition-colors"
|
||||||
{cat}
|
>
|
||||||
</a>
|
{cat}
|
||||||
))}
|
</a>
|
||||||
|
))
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Articles -->
|
<!-- Articles -->
|
||||||
<section class="max-w-6xl mx-auto px-6">
|
<section class="max-w-6xl mx-auto px-6">
|
||||||
<div class="flex items-center justify-between mb-6">
|
<div class="flex items-center justify-between mb-6">
|
||||||
<h2 class="font-heading text-xl text-blue-night">Articles récents</h2>
|
<h2 class="font-heading text-xl text-slate-900 dark:text-slate-50">
|
||||||
|
Articles récents
|
||||||
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{posts.map((post) => (
|
{
|
||||||
<ArticleCard
|
posts.map((post) => (
|
||||||
title={post.data.title}
|
<ArticleCard
|
||||||
description={post.data.description}
|
title={post.data.title}
|
||||||
pubDate={post.data.pubDate}
|
description={post.data.description}
|
||||||
category={post.data.category}
|
pubDate={post.data.pubDate}
|
||||||
heroImage={post.data.heroImage}
|
category={post.data.category}
|
||||||
slug={post.id}
|
heroImage={post.data.heroImage}
|
||||||
series={post.data.series}
|
slug={post.id}
|
||||||
/>
|
series={post.data.series}
|
||||||
))}
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Séries en cours -->
|
<!-- Séries en cours -->
|
||||||
{seriesList.length > 0 && (
|
{
|
||||||
<section class="max-w-6xl mx-auto px-6 mt-16">
|
seriesList.length > 0 && (
|
||||||
<div class="flex items-center justify-between mb-6">
|
<section class="max-w-6xl mx-auto px-6 mt-16">
|
||||||
<h2 class="font-heading text-xl text-blue-night">Séries en cours</h2>
|
<div class="flex items-center justify-between mb-6">
|
||||||
<a href="/series" class="text-sm text-sage no-underline font-medium hover:underline">
|
<h2 class="font-heading text-xl text-slate-900 dark:text-slate-50">
|
||||||
Toutes les séries →
|
Séries en cours
|
||||||
</a>
|
</h2>
|
||||||
</div>
|
<a
|
||||||
{seriesList.map((s) => (
|
href="/series"
|
||||||
<a
|
class="text-sm text-brand-deep dark:text-brand-cyan no-underline font-medium hover:underline"
|
||||||
href={`/series/${s.name.toLowerCase().replace(/ /g, "-")}`}
|
>
|
||||||
class="block bg-white border border-border rounded-xl p-5 mb-3 no-underline hover:translate-x-1 transition-transform"
|
Toutes les séries →
|
||||||
>
|
</a>
|
||||||
<div class="flex items-center justify-between">
|
</div>
|
||||||
<div>
|
{seriesList.map((s) => (
|
||||||
<h3 class="font-heading text-blue-night text-base font-semibold">{s.name}</h3>
|
<a
|
||||||
</div>
|
href={`/series/${s.name.toLowerCase().replace(/ /g, "-")}`}
|
||||||
<div class="flex items-center gap-3">
|
class="block bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-xl p-5 mb-3 no-underline hover:border-brand-cyan/50 hover:translate-x-1 transition-all"
|
||||||
<div class="w-28 h-1 bg-border rounded-full overflow-hidden">
|
>
|
||||||
<div
|
<div class="flex items-center justify-between">
|
||||||
class="h-full bg-sage rounded-full"
|
<div>
|
||||||
style={`width: ${(s.count / s.total) * 100}%`}
|
<h3 class="font-heading text-slate-900 dark:text-slate-50 text-base font-semibold">
|
||||||
/>
|
{s.name}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div class="w-28 h-1 bg-slate-200 dark:bg-slate-800 rounded-full overflow-hidden">
|
||||||
|
<div
|
||||||
|
class="h-full bg-brand-gradient rounded-full"
|
||||||
|
style={`width: ${(s.count / s.total) * 100}%`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span class="text-xs text-slate-400 dark:text-slate-500 whitespace-nowrap">
|
||||||
|
{s.count} / {s.total} articles
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-xs text-text-light whitespace-nowrap">
|
|
||||||
{s.count} / {s.total} articles
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</a>
|
||||||
</a>
|
))}
|
||||||
))}
|
</section>
|
||||||
</section>
|
)
|
||||||
)}
|
}
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|||||||
@ -32,63 +32,71 @@ const seriesList = Array.from(seriesMap.values()).map((s) => ({
|
|||||||
|
|
||||||
<BaseLayout title="Séries">
|
<BaseLayout title="Séries">
|
||||||
<section class="max-w-3xl mx-auto px-6 py-16">
|
<section class="max-w-3xl mx-auto px-6 py-16">
|
||||||
<h1 class="font-heading text-blue-night text-4xl font-extrabold leading-tight mb-3">
|
<h1
|
||||||
|
class="font-heading text-slate-900 dark:text-slate-50 text-4xl font-extrabold leading-tight mb-3"
|
||||||
|
>
|
||||||
Séries
|
Séries
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-text-light mb-10">
|
<p class="text-slate-500 dark:text-slate-400 mb-10">
|
||||||
Des articles qui se suivent pour explorer un sujet en profondeur.
|
Des articles qui se suivent pour explorer un sujet en profondeur.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{seriesList.length === 0 && (
|
{
|
||||||
<p class="text-text-light italic">Aucune série pour le moment.</p>
|
seriesList.length === 0 && (
|
||||||
)}
|
<p class="text-slate-500 dark:text-slate-400 italic">
|
||||||
|
Aucune série pour le moment.
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
{seriesList.map((series) => (
|
{
|
||||||
<div class="bg-white border border-border rounded-xl p-6 mb-6">
|
seriesList.map((series) => (
|
||||||
<div class="flex items-center justify-between mb-4">
|
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-xl p-6 mb-6">
|
||||||
<h2 class="font-heading text-blue-night text-xl font-semibold">
|
<div class="flex items-center justify-between mb-4">
|
||||||
{series.name}
|
<h2 class="font-heading text-slate-900 dark:text-slate-50 text-xl font-semibold">
|
||||||
</h2>
|
{series.name}
|
||||||
<span class="text-xs text-text-light">
|
</h2>
|
||||||
{series.posts.length} / {series.total} articles
|
<span class="text-xs text-slate-400 dark:text-slate-500">
|
||||||
</span>
|
{series.posts.length} / {series.total} articles
|
||||||
</div>
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="w-full h-1 bg-border rounded-full mb-5 overflow-hidden">
|
<div class="w-full h-1 bg-slate-200 dark:bg-slate-800 rounded-full mb-5 overflow-hidden">
|
||||||
<div
|
<div
|
||||||
class="h-full bg-sage rounded-full"
|
class="h-full bg-brand-gradient rounded-full"
|
||||||
style={`width: ${(series.posts.length / series.total) * 100}%`}
|
style={`width: ${(series.posts.length / series.total) * 100}%`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ol class="list-none space-y-2">
|
<ol class="list-none space-y-2">
|
||||||
{series.posts.map((post) => (
|
{series.posts.map((post) => (
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href={`/blog/${post.id}`}
|
href={`/blog/${post.id}`}
|
||||||
class="flex items-center gap-3 py-2 px-3 rounded-lg text-sm text-text-light no-underline hover:bg-sage-light/50 hover:text-blue-night transition-colors"
|
class="flex items-center gap-3 py-2 px-3 rounded-lg text-sm text-slate-500 dark:text-slate-400 no-underline hover:bg-slate-100 dark:hover:bg-slate-800 hover:text-slate-900 dark:hover:text-white transition-colors"
|
||||||
>
|
>
|
||||||
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-sage text-white flex items-center justify-center text-xs font-bold">
|
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-brand-gradient text-white flex items-center justify-center text-xs font-bold">
|
||||||
{post.data.series?.part}
|
{post.data.series?.part}
|
||||||
</span>
|
</span>
|
||||||
<span>{post.data.title}</span>
|
<span>{post.data.title}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
{/* Articles pas encore publiés */}
|
|
||||||
{Array.from(
|
|
||||||
{ length: series.total - series.posts.length },
|
|
||||||
(_, i) => (
|
|
||||||
<li class="flex items-center gap-3 py-2 px-3 text-sm text-text-light/50">
|
|
||||||
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-border text-text-light/50 flex items-center justify-center text-xs font-bold">
|
|
||||||
{series.posts.length + i + 1}
|
|
||||||
</span>
|
|
||||||
<span class="italic">À venir...</span>
|
|
||||||
</li>
|
</li>
|
||||||
)
|
))}
|
||||||
)}
|
{/* Articles pas encore publiés */}
|
||||||
</ol>
|
{Array.from(
|
||||||
</div>
|
{ length: series.total - series.posts.length },
|
||||||
))}
|
(_, i) => (
|
||||||
|
<li class="flex items-center gap-3 py-2 px-3 text-sm text-slate-400/60 dark:text-slate-500/60">
|
||||||
|
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-slate-200 dark:bg-slate-800 text-slate-400 dark:text-slate-500 flex items-center justify-center text-xs font-bold">
|
||||||
|
{series.posts.length + i + 1}
|
||||||
|
</span>
|
||||||
|
<span class="italic">À venir...</span>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
</section>
|
</section>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|||||||
@ -2,60 +2,86 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,600;0,9..144,800;1,9..144,400&family=Inter:wght@400;500;600&display=swap");
|
/* Polices Fraunces + Inter : chargées via <link> dans BaseLayout.astro */
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
|
html {
|
||||||
|
@apply scroll-smooth;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply bg-bg-warm text-text-dark font-body leading-relaxed;
|
@apply bg-canvas-light text-slate-700 font-body leading-relaxed antialiased;
|
||||||
|
@apply dark:bg-canvas-dark dark:text-slate-300;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
h4 {
|
h4 {
|
||||||
@apply font-heading text-blue-night;
|
@apply font-heading text-slate-900 dark:text-slate-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Selection picks up the brand teal */
|
||||||
|
::selection {
|
||||||
|
@apply bg-brand-cyan/25 text-slate-900 dark:text-white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ─── Prose (articles MDX) ─── */
|
@layer components {
|
||||||
|
/* Reusable gradient text helper for the wordmark / accents */
|
||||||
|
.text-gradient {
|
||||||
|
@apply bg-brand-gradient bg-clip-text text-transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────
|
||||||
|
Prose (articles MDX) — light + dark
|
||||||
|
───────────────────────────────────────────── */
|
||||||
.prose {
|
.prose {
|
||||||
@apply max-w-none text-text-dark leading-relaxed;
|
@apply max-w-none text-slate-700 dark:text-slate-300 leading-relaxed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose h2 {
|
.prose h2 {
|
||||||
@apply font-heading text-blue-night text-2xl font-semibold mt-10 mb-4;
|
@apply font-heading text-slate-900 dark:text-slate-50 text-2xl font-semibold mt-10 mb-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose h3 {
|
.prose h3 {
|
||||||
@apply font-heading text-blue-night text-xl font-semibold mt-8 mb-3;
|
@apply font-heading text-slate-900 dark:text-slate-50 text-xl font-semibold mt-8 mb-3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose p {
|
.prose p {
|
||||||
@apply mb-5;
|
@apply mb-5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prose strong {
|
||||||
|
@apply text-slate-900 dark:text-slate-100 font-semibold;
|
||||||
|
}
|
||||||
|
|
||||||
.prose a {
|
.prose a {
|
||||||
@apply text-sage underline underline-offset-2 hover:text-blue-night transition-colors;
|
@apply text-brand-deep dark:text-brand-cyan underline underline-offset-2 decoration-brand-cyan/40 hover:decoration-brand-cyan transition-colors;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose blockquote {
|
.prose blockquote {
|
||||||
@apply border-l-4 border-sage pl-4 italic text-text-light my-6;
|
@apply border-l-4 border-brand-cyan pl-4 italic text-slate-500 dark:text-slate-400 my-6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose code {
|
/* Inline code (not inside a fenced block) */
|
||||||
@apply bg-sage-light text-blue-night px-1.5 py-0.5 rounded text-sm;
|
.prose :not(pre) > code {
|
||||||
|
@apply bg-slate-100 dark:bg-slate-800 text-brand-deep dark:text-brand-teal px-1.5 py-0.5 rounded text-[0.9em] font-medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fenced code blocks: Shiki sets the Slate background via the theme.
|
||||||
|
We only handle spacing, rounding and horizontal scroll. */
|
||||||
.prose pre {
|
.prose pre {
|
||||||
@apply bg-blue-night text-white rounded-lg p-4 overflow-x-auto my-6;
|
@apply rounded-xl p-4 overflow-x-auto my-6 text-sm leading-relaxed ring-1 ring-slate-800/60;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose pre code {
|
.prose pre code {
|
||||||
@apply bg-transparent text-white p-0;
|
@apply bg-transparent p-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose img {
|
.prose img {
|
||||||
@apply rounded-lg my-6;
|
@apply rounded-xl my-6 ring-1 ring-slate-200 dark:ring-slate-800;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose ul,
|
.prose ul,
|
||||||
@ -63,6 +89,22 @@
|
|||||||
@apply my-4 pl-6;
|
@apply my-4 pl-6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prose ul {
|
||||||
|
@apply list-disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose ol {
|
||||||
|
@apply list-decimal;
|
||||||
|
}
|
||||||
|
|
||||||
.prose li {
|
.prose li {
|
||||||
@apply mb-2;
|
@apply mb-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prose li::marker {
|
||||||
|
@apply text-brand-cyan;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose hr {
|
||||||
|
@apply my-10 border-slate-200 dark:border-slate-800;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,22 +1,48 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export default {
|
export default {
|
||||||
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
|
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
|
||||||
|
darkMode: "class",
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
"blue-night": "#1B2A4A",
|
// ─── Brand "Electric Teal" ───
|
||||||
"blue-mid": "#2d4a7a",
|
brand: {
|
||||||
sage: "#6B8F71",
|
cyan: "#06B6D4", // accent primary
|
||||||
"sage-light": "#E8F0E9",
|
blue: "#3B82F6", // accent secondary
|
||||||
"bg-warm": "#FAFAF7",
|
teal: "#22D3EE", // deep marine highlight
|
||||||
"text-dark": "#2D2D2D",
|
deep: "#0891B2", // deep teal (hover / prestige)
|
||||||
"text-light": "#6B6B6B",
|
indigo: "#4F46E5",
|
||||||
border: "#E5E5E0",
|
},
|
||||||
|
// ─── Canvas (Slate) ───
|
||||||
|
canvas: {
|
||||||
|
dark: "#0F172A", // dark body / terminal backdrop
|
||||||
|
deep: "#090D16", // dark footer / deepest sheet
|
||||||
|
light: "#F8FAFC", // light body (alpine snow)
|
||||||
|
},
|
||||||
|
// ─── 7-color diagnostic & syntax spectrum ───
|
||||||
|
diag: {
|
||||||
|
rose: "#F43F5E", // exception / invalid
|
||||||
|
orange: "#FF6B00", // keyword / control
|
||||||
|
amber: "#F59E0B", // string / regex
|
||||||
|
emerald: "#10B981", // support / type / class
|
||||||
|
sky: "#0EA5E9", // variable / parameter
|
||||||
|
indigo: "#6366F1", // entity / function
|
||||||
|
violet: "#8B5CF6", // comment / metadata
|
||||||
|
},
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
heading: ["Fraunces", "Georgia", "serif"],
|
heading: ["Fraunces", "Georgia", "serif"],
|
||||||
body: ["Inter", "-apple-system", "BlinkMacSystemFont", "sans-serif"],
|
body: ["Inter", "-apple-system", "BlinkMacSystemFont", "sans-serif"],
|
||||||
},
|
},
|
||||||
|
backgroundImage: {
|
||||||
|
// 135° brand gradient (logo ligatures, CTAs, highlights)
|
||||||
|
"brand-gradient": "linear-gradient(135deg, #06B6D4 0%, #3B82F6 100%)",
|
||||||
|
"brand-gradient-soft":
|
||||||
|
"linear-gradient(135deg, rgba(6,182,212,0.12) 0%, rgba(59,130,246,0.12) 100%)",
|
||||||
|
},
|
||||||
|
boxShadow: {
|
||||||
|
glow: "0 8px 30px -8px rgba(6,182,212,0.35)",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user