Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions content/articles/introduction/meta.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"title": "Introduction",
"id": "introduction",
"authors": [],
"authors": ["111222333444555666"],
"date": "2024-01-01",
"chapeau": { "fr": "", "en": "" },
"chapeau": { "fr": "Découvrez Moddy, le bot Discord tout-en-un.", "en": "Discover Moddy, the all-in-one Discord bot." },
"banner": null,
"seo_level": 1,
"labels": ["about"],
Expand Down
2 changes: 1 addition & 1 deletion content/articles/quick-start/meta.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"title": "Quick Start",
"id": "quick-start",
"authors": [],
"authors": ["777888999000111222"],
"date": "2024-01-01",
"chapeau": {
"fr": "Tout ce qu'il faut savoir pour démarrer avec Moddy.",
Expand Down
10 changes: 10 additions & 0 deletions content/authors/111222333444555666.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"id": "111222333444555666",
"username": "Claire Dupont",
"avatar": null,
"avatar_decoration": null,
"post": "Designer",
"bio": "Designer UI/UX passionnée par les interfaces Discord et la documentation produit.",
"banner_color": "#e8a020",
"links": []
}
10 changes: 10 additions & 0 deletions content/authors/777888999000111222.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"id": "777888999000111222",
"username": "Marc Leclerc",
"avatar": null,
"avatar_decoration": null,
"post": "Développeur",
"bio": "Développeur backend chez Moddy, spécialisé dans les bots Discord et les APIs.",
"banner_color": "#2e7d32",
"links": []
}
23 changes: 23 additions & 0 deletions site/eleventy.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,29 @@ module.exports = function (eleventyConfig) {
return md.render(content);
});

// Generate a unique CSS gradient from a string ID (article or author)
eleventyConfig.addFilter('articleGradient', (id) => {
let h = 0;
const str = String(id);
for (let i = 0; i < str.length; i++) {
h = (((h << 5) - h) + str.charCodeAt(i)) | 0;
}
h = Math.abs(h);
const hue1 = h % 360;
const hue2 = (hue1 + 40 + ((h >> 8) % 80)) % 360;
const angle = 110 + (h % 80);
return `linear-gradient(${angle}deg, hsl(${hue1},65%,52%), hsl(${hue2},75%,40%))`;
});

// Format a date string for a given locale ('fr' or 'en')
eleventyConfig.addFilter('formatDate', (date, locale) => {
if (!date) return '';
const d = new Date(String(date) + 'T00:00:00');
return d.toLocaleDateString(locale === 'en' ? 'en-US' : 'fr-FR', {
year: 'numeric', month: 'long', day: 'numeric',
});
});

// Add a TOC plugin (implementation is wip for now)
eleventyConfig.addPlugin(pluginTOC, {
tags: ['h2', 'h3', 'h4'],
Expand Down
53 changes: 52 additions & 1 deletion site/site/articles/article.njk
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,56 @@ hasToc: true
{% endblock %}

{% block content %}
{{ renderedContent | mdMarkdown | safe }}
<article class="article-page">

{# ── Banner ── #}
<div class="article-banner"{% if not article.banner %} style="background: {{ article.id | articleGradient }}"{% endif %}>
{% if article.banner %}
<img class="article-banner-img" src="{{ article.banner }}" alt="" />
{% endif %}
</div>

<div class="article-content-inner">

{# ── Title ── #}
<h1 class="article-title">{{ article.title }}</h1>

{# ── Date | Résumé rapide ── #}
<div class="article-dateline">
<span data-lang-content="fr">{{ article.date | formatDate('fr') }}{% if article.chapeau.fr %} <span class="article-dateline-sep">|</span> {{ article.chapeau.fr }}{% endif %}</span>
<span data-lang-content="en">{{ article.date | formatDate('en') }}{% if article.chapeau.en %} <span class="article-dateline-sep">|</span> {{ article.chapeau.en }}{% endif %}</span>
</div>

{# ── Authors ── #}
{% if article.authors and article.authors.length %}
<div class="article-authors">
{% for authorId in article.authors %}
{% for a in authors %}
{% if a.id == authorId %}
<a class="article-author-block" href="/authors/{{ a.id }}/">
<div class="article-author-avatar">
{% if a.avatar %}
<img src="{{ a.avatar }}" alt="{{ a.username }}" />
{% else %}
<div class="article-author-avatar--placeholder" style="background: {{ a.id | articleGradient }}">{{ a.username[0] | upper }}</div>
{% endif %}
</div>
<div class="article-author-info">
<span class="article-author-name">{{ a.username }}</span>
{% if a.post %}<span class="article-author-post">{{ a.post }}</span>{% endif %}
</div>
</a>
{% endif %}
{% endfor %}
{% endfor %}
</div>
{% endif %}

{# ── Body ── #}
<div class="article-body">
{{ renderedContent | mdMarkdown | safe }}
</div>

</div>
</article>
{% endblock %}
151 changes: 90 additions & 61 deletions site/site/css/article.css
Original file line number Diff line number Diff line change
Expand Up @@ -61,122 +61,151 @@ span[data-lang-content] {
/* ─── Article page ────────────────────────────────────────────────────────── */

.article-page {
max-width: 800px;
padding: 24px;
width: 100%;
}

/* Banner — paysage large sur desktop, portrait sur mobile */
.article-banner {
width: 100%;
margin-bottom: 24px;
aspect-ratio: 1300 / 445;
border-radius: 12px;
overflow: hidden;
}

.article-banner img {
.article-banner-img {
width: 100%;
height: auto;
height: 100%;
object-fit: cover;
object-position: center;
display: block;
}

.article-header {
margin-bottom: 32px;
@media (max-width: 700px) {
.article-banner {
aspect-ratio: 3 / 4;
border-radius: 8px;
}
}

/* Inner content — marges latérales, espacement vertical entre blocs */
.article-content-inner {
padding: 30px 24px 48px;
max-width: 1200px;
}

.article-title {
font-size: 2rem;
font-size: 2.2rem;
font-weight: 700;
margin: 0 0 16px;
line-height: 1.2;
margin: 0 0 30px;
color: var(--md-sys-color-on-surface, #1c1b1f);
}

.article-meta {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 16px;
}

.article-labels {
display: flex;
flex-wrap: wrap;
gap: 8px;
}

.article-label {
display: inline-block;
padding: 2px 10px;
border-radius: 999px;
font-size: 0.75rem;
font-weight: 600;
text-decoration: none;
background: color-mix(in srgb, var(--label-color, #4caf50) 15%, transparent);
color: var(--label-color, #4caf50);
border: 1px solid color-mix(in srgb, var(--label-color, #4caf50) 40%, transparent);
transition: background 0.15s;
/* Date | Résumé rapide */
.article-dateline {
font-size: 0.875rem;
color: var(--md-sys-color-on-surface-variant, #49454f);
margin: 0 0 30px;
line-height: 1.5;
}

.article-label:hover {
background: color-mix(in srgb, var(--label-color, #4caf50) 25%, transparent);
.article-dateline-sep {
opacity: 0.45;
margin: 0 5px;
}

.article-byline {
/* Authors */
.article-authors {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
font-size: 0.875rem;
color: var(--md-sys-color-on-surface-variant, #49454f);
flex-direction: column;
gap: 14px;
margin-bottom: 32px;
}

.article-author {
.article-author-block {
display: flex;
align-items: center;
gap: 6px;
gap: 18px;
text-decoration: none;
color: inherit;
font-weight: 500;
}

.article-author:hover {
.article-author-block:hover .article-author-name {
color: var(--md-sys-color-primary, #6750a4);
}

.author-avatar-small {
width: 24px;
height: 24px;
.article-author-avatar {
width: 42px;
height: 42px;
border-radius: 50%;
overflow: hidden;
flex-shrink: 0;
}

.article-author-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}

.author-avatar-placeholder {
width: 24px;
height: 24px;
border-radius: 50%;
background: var(--md-sys-color-primary-container, #eaddff);
color: var(--md-sys-color-on-primary-container, #21005d);
.article-author-avatar--placeholder {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
font-weight: 700;
font-size: 1.1rem;
color: white;
text-transform: uppercase;
}

.article-date {
color: var(--md-sys-color-on-surface-variant, #49454f);
.article-author-info {
display: flex;
flex-direction: column;
gap: 2px;
}

.article-chapeau {
font-size: 1.1rem;
.article-author-name {
font-weight: 700;
font-size: 0.9rem;
transition: color 0.15s;
}

.article-author-post {
font-size: 0.78rem;
color: var(--md-sys-color-on-surface-variant, #49454f);
margin: 12px 0 0;
font-style: italic;
}

.article-body {
line-height: 1.7;
}

/* Labels (conservés pour les autres pages) */
.article-labels {
display: flex;
flex-wrap: wrap;
gap: 8px;
}

.article-label {
display: inline-block;
padding: 2px 10px;
border-radius: 999px;
font-size: 0.75rem;
font-weight: 600;
text-decoration: none;
background: color-mix(in srgb, var(--label-color, #4caf50) 15%, transparent);
color: var(--label-color, #4caf50);
border: 1px solid color-mix(in srgb, var(--label-color, #4caf50) 40%, transparent);
transition: background 0.15s;
}

.article-label:hover {
background: color-mix(in srgb, var(--label-color, #4caf50) 25%, transparent);
}

/* ─── Collection page ────────────────────────────────────────────────────── */

.collection-page,
Expand Down