Spaces:
Running
Running
thibaud frere
commited on
Commit
·
fc7711a
1
Parent(s):
101af31
update
Browse files- .gitattributes +1 -0
- ASTRO_MIGRATION.md +0 -272
- README.md +1 -1
- app/astro.config.mjs +9 -0
- app/package-lock.json +0 -0
- app/package.json +0 -0
- app/src/assets/images/{placeholder.png → placeholder.jpg} +2 -2
- app/src/components/Aside.astro +0 -8
- app/src/components/Footer.astro +95 -1
- app/src/pages/article.mdx +172 -24
- app/src/pages/index.astro +63 -8
- app/src/styles/components/_base.scss +153 -0
- app/src/styles/components/_layout.scss +33 -0
- app/src/styles/components/_variables.scss +40 -0
- app/src/styles/global.scss +20 -171
.gitattributes
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
app/assets/images/*.png filter=lfs diff=lfs merge=lfs -text
|
| 2 |
app/src/assets/images/*.png filter=lfs diff=lfs merge=lfs -text
|
| 3 |
*.png filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 4 |
*.wav filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 1 |
app/assets/images/*.png filter=lfs diff=lfs merge=lfs -text
|
| 2 |
app/src/assets/images/*.png filter=lfs diff=lfs merge=lfs -text
|
| 3 |
*.png filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.jpg filter=lfs diff=lfs merge=lfs -text
|
| 5 |
*.wav filter=lfs diff=lfs merge=lfs -text
|
ASTRO_MIGRATION.md
DELETED
|
@@ -1,272 +0,0 @@
|
|
| 1 |
-
## Migration vers Astro (Markdown/MDX) – Spécification et guide
|
| 2 |
-
|
| 3 |
-
### Objectif
|
| 4 |
-
- Remplacer le template Distill par une pile moderne basée sur Markdown/MDX avec Astro, tout en conservant: front‑matter, maths, code, citations/bibliographie, table des matières, figures, sections de contenu, et composants interactifs (plots/tables).
|
| 5 |
-
- Offrir une optimisation d’images de premier ordre (AVIF/WebP, `srcset`, lazy‑loading) et un déploiement statique via Nginx.
|
| 6 |
-
|
| 7 |
-
### Stack cible
|
| 8 |
-
- Astro 4+ (SSG, sortie statique `dist/`)
|
| 9 |
-
- Markdown/MDX (contenu principal)
|
| 10 |
-
- Intégrations/Plugins:
|
| 11 |
-
- `@astrojs/mdx` (MDX)
|
| 12 |
-
- `remark-math` + `rehype-katex` (maths)
|
| 13 |
-
- `rehype-slug` + `rehype-autolink-headings` (ancres H2/H3/H4)
|
| 14 |
-
- `remark-toc` (table des matières auto)
|
| 15 |
-
- `rehype-citation` (citations/bibliographie depuis BibTeX/CSL)
|
| 16 |
-
- `@astrojs/image` avec `SquooshImageService` (optimisation d’images sans dépendances natives)
|
| 17 |
-
- Surlignage code: Shiki par défaut d’Astro (fences ```lang)
|
| 18 |
-
|
| 19 |
-
### Mapping Distill → Astro
|
| 20 |
-
- `d-front-matter` → YAML front‑matter en tête de fichier `.md/.mdx` (titre, auteurs, dates, cover/hero, tags…)
|
| 21 |
-
- `d-title` → en‑tête de page dans un layout Astro (`src/layouts/BlogLayout.astro`) utilisant les métadonnées
|
| 22 |
-
- `d-byline` → bloc auteurs/affiliations dans le layout
|
| 23 |
-
- `d-article` → contenu Markdown principal
|
| 24 |
-
- `d-figure` → `<Image />` Astro (optimisé) + `<figure>/<figcaption>` sémantique
|
| 25 |
-
- `d-toc` → `remark-toc` + `rehype-slug/autolink` (TOC ancré)
|
| 26 |
-
- `d-footnote`/`d-footnote-list` → notes de bas de page Markdown standard
|
| 27 |
-
- `d-math` → Math Markdown via `remark-math`/`rehype-katex` (`$...$`, `$$...$$`)
|
| 28 |
-
- `d-code` (Prism) → fences Markdown + Shiki
|
| 29 |
-
- `d-cite`/`d-bibliography` → `rehype-citation` (génération des références + liens), section `## References` en bas
|
| 30 |
-
|
| 31 |
-
### Arborescence proposée
|
| 32 |
-
```
|
| 33 |
-
astro-site/
|
| 34 |
-
src/
|
| 35 |
-
assets/ # images sources (ex: banner.png)
|
| 36 |
-
content/
|
| 37 |
-
bibliography.bib
|
| 38 |
-
posts/
|
| 39 |
-
finetasks.mdx # migration de index.html → Markdown/MDX
|
| 40 |
-
components/
|
| 41 |
-
TOC.astro # (optionnel) wrapper visuel autour du TOC
|
| 42 |
-
PlotlyChart.jsx # composant interactif (client:visible)
|
| 43 |
-
layouts/
|
| 44 |
-
BlogLayout.astro
|
| 45 |
-
public/ # assets statiques bruts si besoin
|
| 46 |
-
astro.config.mjs
|
| 47 |
-
package.json
|
| 48 |
-
```
|
| 49 |
-
|
| 50 |
-
### Configuration Astro (plugins)
|
| 51 |
-
```js
|
| 52 |
-
// astro.config.mjs
|
| 53 |
-
import { defineConfig } from 'astro/config';
|
| 54 |
-
import mdx from '@astrojs/mdx';
|
| 55 |
-
import image from '@astrojs/image';
|
| 56 |
-
import { SquooshImageService } from '@astrojs/image/squoosh';
|
| 57 |
-
|
| 58 |
-
// Remark/Rehype
|
| 59 |
-
import remarkMath from 'remark-math';
|
| 60 |
-
import remarkToc from 'remark-toc';
|
| 61 |
-
import rehypeKatex from 'rehype-katex';
|
| 62 |
-
import rehypeSlug from 'rehype-slug';
|
| 63 |
-
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
|
| 64 |
-
import rehypeCitation from 'rehype-citation';
|
| 65 |
-
|
| 66 |
-
export default defineConfig({
|
| 67 |
-
integrations: [
|
| 68 |
-
mdx(),
|
| 69 |
-
image({ serviceEntryPoint: SquooshImageService() })
|
| 70 |
-
],
|
| 71 |
-
markdown: {
|
| 72 |
-
remarkPlugins: [
|
| 73 |
-
[remarkToc, { heading: 'Table of contents', maxDepth: 3 }],
|
| 74 |
-
remarkMath,
|
| 75 |
-
],
|
| 76 |
-
rehypePlugins: [
|
| 77 |
-
rehypeSlug,
|
| 78 |
-
[rehypeAutolinkHeadings, { behavior: 'wrap' }],
|
| 79 |
-
rehypeKatex,
|
| 80 |
-
// Génère les citations et la biblio (insertion en bas si "## References" existe)
|
| 81 |
-
[rehypeCitation, {
|
| 82 |
-
bibliography: 'src/content/bibliography.bib',
|
| 83 |
-
linkCitations: true,
|
| 84 |
-
// CSL optionnel: csl: 'src/content/ieee.csl'
|
| 85 |
-
}],
|
| 86 |
-
],
|
| 87 |
-
shikiConfig: { theme: 'github-dark' },
|
| 88 |
-
},
|
| 89 |
-
build: {
|
| 90 |
-
assets: 'assets',
|
| 91 |
-
},
|
| 92 |
-
output: 'static'
|
| 93 |
-
});
|
| 94 |
-
```
|
| 95 |
-
|
| 96 |
-
Ajouter les styles KaTeX (dans `src/layouts/BlogLayout.astro` ou global CSS):
|
| 97 |
-
```html
|
| 98 |
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css" crossorigin="anonymous" />
|
| 99 |
-
```
|
| 100 |
-
|
| 101 |
-
### Layout principal
|
| 102 |
-
```astro
|
| 103 |
-
---
|
| 104 |
-
// src/layouts/BlogLayout.astro
|
| 105 |
-
const { title, description, authors = [], published, hero } = Astro.props;
|
| 106 |
-
---
|
| 107 |
-
<html lang="en">
|
| 108 |
-
<head>
|
| 109 |
-
<meta charset="utf-8" />
|
| 110 |
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 111 |
-
<title>{title}</title>
|
| 112 |
-
<meta name="description" content={description} />
|
| 113 |
-
</head>
|
| 114 |
-
<body>
|
| 115 |
-
<header class="page-hero">
|
| 116 |
-
{hero && <img src={hero} alt={title} loading="eager" fetchpriority="high" />}
|
| 117 |
-
<h1>{title}</h1>
|
| 118 |
-
{published && <p class="byline">{published}</p>}
|
| 119 |
-
{authors.length > 0 && (
|
| 120 |
-
<p class="authors">{authors.map(a => a.name || a).join(', ')}</p>
|
| 121 |
-
)}
|
| 122 |
-
</header>
|
| 123 |
-
<main>
|
| 124 |
-
<slot />
|
| 125 |
-
</main>
|
| 126 |
-
</body>
|
| 127 |
-
<style>
|
| 128 |
-
.page-hero img{width:100%;height:auto;}
|
| 129 |
-
main{max-width:980px;margin:0 auto;padding:24px}
|
| 130 |
-
</style>
|
| 131 |
-
</html>
|
| 132 |
-
```
|
| 133 |
-
|
| 134 |
-
### Exemple de page MDX (migration de `index.html`)
|
| 135 |
-
```mdx
|
| 136 |
-
---
|
| 137 |
-
layout: ../layouts/BlogLayout.astro
|
| 138 |
-
title: "Scaling FineWeb to 1000+ languages: Step 1: finding signal in 100s of evaluation tasks"
|
| 139 |
-
description: "Multilingual evaluation & FineTasks"
|
| 140 |
-
published: "Oct 23, 2024"
|
| 141 |
-
authors:
|
| 142 |
-
- name: "Hynek Kydlíček"
|
| 143 |
-
- name: "Guilherme Penedo"
|
| 144 |
-
- name: "Clémentine Fourier"
|
| 145 |
-
- name: "Nathan Habib"
|
| 146 |
-
- name: "Thomas Wolf"
|
| 147 |
-
hero: "../assets/images/banner.png"
|
| 148 |
-
---
|
| 149 |
-
|
| 150 |
-
## Table of contents
|
| 151 |
-
|
| 152 |
-
<!-- Le TOC sera injecté ici par remark-toc -->
|
| 153 |
-
|
| 154 |
-

|
| 155 |
-
|
| 156 |
-
Du texte… équations $e^{i\pi}+1=0$ et un bloc:
|
| 157 |
-
|
| 158 |
-
$$
|
| 159 |
-
\int_a^b f(x)\,dx
|
| 160 |
-
$$
|
| 161 |
-
|
| 162 |
-
Citation: [@kydlicek2024finetasksmultilingualtasks].
|
| 163 |
-
|
| 164 |
-
## Results
|
| 165 |
-
|
| 166 |
-
<PlotlyChart client:visible dataUrl="/data/..." />
|
| 167 |
-
|
| 168 |
-
## References
|
| 169 |
-
```
|
| 170 |
-
|
| 171 |
-
### Composants interactifs (exemples)
|
| 172 |
-
- Plotly (client‑side, hydraté quand visible):
|
| 173 |
-
```jsx
|
| 174 |
-
// src/components/PlotlyChart.jsx
|
| 175 |
-
import { useEffect, useRef } from 'react';
|
| 176 |
-
import Plotly from 'plotly.js-basic-dist-min';
|
| 177 |
-
|
| 178 |
-
export default function PlotlyChart({ data, layout, config }) {
|
| 179 |
-
const ref = useRef(null);
|
| 180 |
-
useEffect(() => {
|
| 181 |
-
if (!ref.current) return;
|
| 182 |
-
Plotly.newPlot(ref.current, data || [], layout || {}, config || {responsive:true});
|
| 183 |
-
return () => { Plotly.purge(ref.current); };
|
| 184 |
-
}, [data, layout, config]);
|
| 185 |
-
return <div ref={ref} style={{width:'100%'}} />;
|
| 186 |
-
}
|
| 187 |
-
```
|
| 188 |
-
|
| 189 |
-
- DataTables: préférer générer des tables Markdown; pour un tableau interactif, créer un composant MDX dédié et l’hydrater `client:visible`.
|
| 190 |
-
|
| 191 |
-
### Optimisation d’images (Astro)
|
| 192 |
-
- Utiliser `@astrojs/image` (Squoosh) ou `astro:assets` pour images locales.
|
| 193 |
-
- Exemple responsive (MDX):
|
| 194 |
-
```astro
|
| 195 |
-
---
|
| 196 |
-
import { Image } from '@astrojs/image/components';
|
| 197 |
-
import banner from '../assets/images/banner.png';
|
| 198 |
-
---
|
| 199 |
-
<Image src={banner} alt="FineTasks" widths={[480,768,1080,1440]} formats={["avif","webp","png"]} sizes="(max-width: 768px) 100vw, 980px" loading="lazy" decoding="async" />
|
| 200 |
-
```
|
| 201 |
-
- Bonnes pratiques: dimensions explicites `width/height` (automatiques avec `Image`), `loading="lazy"` hors hero, `fetchpriority="high"` sur le hero.
|
| 202 |
-
|
| 203 |
-
### Citations et bibliographie
|
| 204 |
-
- Placer `src/content/bibliography.bib` (copie de `app/src/bibliography.bib`).
|
| 205 |
-
- Citer en Markdown: `[@clé]` ou `[-@clé]`. Ajouter `## References` à la fin; `rehype-citation` génère la bibliographie.
|
| 206 |
-
|
| 207 |
-
### Commandes/installation
|
| 208 |
-
```bash
|
| 209 |
-
npm create astro@latest astro-site -- --template minimal
|
| 210 |
-
cd astro-site
|
| 211 |
-
npm i -D @astrojs/mdx @astrojs/image remark-math rehype-katex rehype-slug rehype-autolink-headings remark-toc rehype-citation
|
| 212 |
-
npm run dev
|
| 213 |
-
# build
|
| 214 |
-
npm run build
|
| 215 |
-
```
|
| 216 |
-
|
| 217 |
-
### Dockerfile (multi‑stage)
|
| 218 |
-
```Dockerfile
|
| 219 |
-
FROM node:20 AS build
|
| 220 |
-
WORKDIR /site
|
| 221 |
-
COPY astro-site/package*.json ./
|
| 222 |
-
RUN npm ci
|
| 223 |
-
COPY astro-site/ .
|
| 224 |
-
RUN npm run build
|
| 225 |
-
|
| 226 |
-
FROM nginx:alpine
|
| 227 |
-
COPY --from=build /site/dist /usr/share/nginx/html
|
| 228 |
-
COPY nginx.conf /etc/nginx/nginx.conf
|
| 229 |
-
EXPOSE 8080
|
| 230 |
-
CMD ["nginx","-g","daemon off;"]
|
| 231 |
-
```
|
| 232 |
-
|
| 233 |
-
### Nginx (statique, cache + SPA fallback)
|
| 234 |
-
```nginx
|
| 235 |
-
worker_processes auto;
|
| 236 |
-
events { worker_connections 1024; }
|
| 237 |
-
http {
|
| 238 |
-
include /etc/nginx/mime.types;
|
| 239 |
-
server {
|
| 240 |
-
listen 8080;
|
| 241 |
-
server_name localhost;
|
| 242 |
-
root /usr/share/nginx/html;
|
| 243 |
-
index index.html;
|
| 244 |
-
|
| 245 |
-
location /assets/ {
|
| 246 |
-
expires 30d;
|
| 247 |
-
add_header Cache-Control "public, max-age=2592000, immutable";
|
| 248 |
-
}
|
| 249 |
-
location / {
|
| 250 |
-
try_files $uri $uri/ /index.html;
|
| 251 |
-
}
|
| 252 |
-
location = /health { return 200 "ok"; add_header Content-Type text/plain; }
|
| 253 |
-
}
|
| 254 |
-
}
|
| 255 |
-
```
|
| 256 |
-
|
| 257 |
-
### Étapes de migration depuis ce dépôt
|
| 258 |
-
1) Créer `astro-site/` et installer la stack (ci‑dessus).
|
| 259 |
-
2) Copier `app/src/bibliography.bib` → `astro-site/src/content/bibliography.bib`.
|
| 260 |
-
3) Copier les images `app/assets/images/*` → `astro-site/src/assets/images/*`.
|
| 261 |
-
4) Convertir `app/src/index.html` → `astro-site/src/content/posts/finetasks.mdx` (reprendre sections H2/H3/H4, figures, équations, et remplacer les blocs Distill par Markdown/MDX).
|
| 262 |
-
5) Recréer les applets (plots/tables) via composants MDX (`PlotlyChart.jsx`, etc.).
|
| 263 |
-
6) Ajuster le layout (`BlogLayout.astro`) pour le titre, auteurs, date, hero.
|
| 264 |
-
7) Ajouter TOC, maths, citations (plugins configurés).
|
| 265 |
-
8) Vérifier le build (`npm run build`) et brancher le Docker/Nginx proposés.
|
| 266 |
-
|
| 267 |
-
### Notes
|
| 268 |
-
- Pour éviter des dépendances natives (Sharp) en build Docker, on force le service image Squoosh (WASM).
|
| 269 |
-
- Si besoin de SEO/sitemap/RSS, ajouter `@astrojs/sitemap`/`@astrojs/rss`.
|
| 270 |
-
- Les composants interactifs doivent être idempotents et hydratés avec `client:visible`/`client:idle`.
|
| 271 |
-
|
| 272 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
---
|
| 2 |
-
title: '
|
| 3 |
emoji: 📝
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: indigo
|
|
|
|
| 1 |
---
|
| 2 |
+
title: 'The science article template'
|
| 3 |
emoji: 📝
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: indigo
|
app/astro.config.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import remarkFootnotes from 'remark-footnotes';
|
|
| 7 |
import rehypeSlug from 'rehype-slug';
|
| 8 |
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
|
| 9 |
import rehypeCitation from 'rehype-citation';
|
|
|
|
| 10 |
|
| 11 |
export default defineConfig({
|
| 12 |
output: 'static',
|
|
@@ -22,6 +23,14 @@ export default defineConfig({
|
|
| 22 |
rehypeSlug,
|
| 23 |
[rehypeAutolinkHeadings, { behavior: 'wrap' }],
|
| 24 |
rehypeKatex,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
[rehypeCitation, {
|
| 26 |
bibliography: 'src/content/bibliography.bib',
|
| 27 |
linkCitations: true
|
|
|
|
| 7 |
import rehypeSlug from 'rehype-slug';
|
| 8 |
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
|
| 9 |
import rehypeCitation from 'rehype-citation';
|
| 10 |
+
import rehypePrettyCode from 'rehype-pretty-code';
|
| 11 |
|
| 12 |
export default defineConfig({
|
| 13 |
output: 'static',
|
|
|
|
| 23 |
rehypeSlug,
|
| 24 |
[rehypeAutolinkHeadings, { behavior: 'wrap' }],
|
| 25 |
rehypeKatex,
|
| 26 |
+
[rehypePrettyCode, {
|
| 27 |
+
theme: {
|
| 28 |
+
light: 'github-light',
|
| 29 |
+
dark: 'github-dark'
|
| 30 |
+
},
|
| 31 |
+
keepBackground: false,
|
| 32 |
+
defaultLang: 'text'
|
| 33 |
+
}],
|
| 34 |
[rehypeCitation, {
|
| 35 |
bibliography: 'src/content/bibliography.bib',
|
| 36 |
linkCitations: true
|
app/package-lock.json
CHANGED
|
Binary files a/app/package-lock.json and b/app/package-lock.json differ
|
|
|
app/package.json
CHANGED
|
Binary files a/app/package.json and b/app/package.json differ
|
|
|
app/src/assets/images/{placeholder.png → placeholder.jpg}
RENAMED
|
File without changes
|
app/src/components/Aside.astro
CHANGED
|
@@ -8,11 +8,3 @@
|
|
| 8 |
<slot name="aside" />
|
| 9 |
</aside>
|
| 10 |
</div>
|
| 11 |
-
|
| 12 |
-
<style>
|
| 13 |
-
.margin-aside { display: grid; grid-template-columns: minmax(0, 1fr) 240px; gap: 16px; align-items: start; margin: 12px 0; }
|
| 14 |
-
.margin-aside__aside { background: var(--surface-bg); border: 1px solid var(--border-color); border-radius: 8px; padding: 10px; font-size: 0.9rem; color: var(--text-color); }
|
| 15 |
-
@media (max-width: 1100px) { .margin-aside { grid-template-columns: 1fr; } }
|
| 16 |
-
</style>
|
| 17 |
-
|
| 18 |
-
|
|
|
|
| 8 |
<slot name="aside" />
|
| 9 |
</aside>
|
| 10 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/src/components/Footer.astro
CHANGED
|
@@ -26,7 +26,101 @@ const { citationText, bibtex } = Astro.props as Props;
|
|
| 26 |
.footer-inner { max-width: 680px; margin: 0 auto; padding: 24px 16px; }
|
| 27 |
.citation-block h3 { margin: 0 0 8px; }
|
| 28 |
.citation-block h4 { margin: 16px 0 8px; font-size: 14px; text-transform: uppercase; color: var(--muted-color); }
|
| 29 |
-
.citation-text, .citation-bibtex { width: 100%; min-height: 44px; border: 1px solid var(--border-color); border-radius: 6px; background: var(--surface-bg); padding: 8px; resize: none; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 13px; color: var(--text-color); }
|
|
|
|
|
|
|
|
|
|
| 30 |
</style>
|
| 31 |
|
| 32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
.footer-inner { max-width: 680px; margin: 0 auto; padding: 24px 16px; }
|
| 27 |
.citation-block h3 { margin: 0 0 8px; }
|
| 28 |
.citation-block h4 { margin: 16px 0 8px; font-size: 14px; text-transform: uppercase; color: var(--muted-color); }
|
| 29 |
+
.citation-text, .citation-bibtex { width: 100%; min-height: 44px; border: 1px solid var(--border-color); border-radius: 6px; background: var(--surface-bg); padding: 8px; resize: none; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 13px; color: var(--text-color); white-space: pre-wrap; overflow-y: hidden; line-height: 1.4; }
|
| 30 |
+
.references-block h3 { margin: 24px 0 8px; }
|
| 31 |
+
.references-block .footnotes { margin-top: 8px; }
|
| 32 |
+
.references-block .bibliography { margin-top: 8px; }
|
| 33 |
</style>
|
| 34 |
|
| 35 |
|
| 36 |
+
<script is:inline>
|
| 37 |
+
(() => {
|
| 38 |
+
const getFooter = () => document.currentScript?.closest('footer') || document.querySelector('footer.distill-footer');
|
| 39 |
+
const footer = getFooter();
|
| 40 |
+
if (!footer) return;
|
| 41 |
+
const target = footer.querySelector('.references-block');
|
| 42 |
+
if (!target) return;
|
| 43 |
+
|
| 44 |
+
const contentRoot = document.querySelector('section.content-grid main') || document.querySelector('main') || document.body;
|
| 45 |
+
|
| 46 |
+
const findFirstOutsideFooter = (selectors) => {
|
| 47 |
+
for (const sel of selectors) {
|
| 48 |
+
const el = contentRoot.querySelector(sel);
|
| 49 |
+
if (el && !footer.contains(el)) return el;
|
| 50 |
+
}
|
| 51 |
+
return null;
|
| 52 |
+
};
|
| 53 |
+
|
| 54 |
+
const ensureHeading = (text) => {
|
| 55 |
+
const exists = Array.from(target.children).some((c) => c.tagName === 'H3' && c.textContent.trim().toLowerCase() === text.toLowerCase());
|
| 56 |
+
if (!exists) {
|
| 57 |
+
const h = document.createElement('h3');
|
| 58 |
+
h.textContent = text;
|
| 59 |
+
target.appendChild(h);
|
| 60 |
+
}
|
| 61 |
+
};
|
| 62 |
+
|
| 63 |
+
const moveIntoFooter = (element, headingText) => {
|
| 64 |
+
if (!element) return false;
|
| 65 |
+
if (element.classList.contains('footnotes')) {
|
| 66 |
+
const hr = element.querySelector('hr');
|
| 67 |
+
if (hr) hr.remove();
|
| 68 |
+
}
|
| 69 |
+
// Supprimer un éventuel titre déjà inclus dans le bloc (pour éviter doublons)
|
| 70 |
+
const firstHeading = element.querySelector(':scope > h1, :scope > h2, :scope > h3');
|
| 71 |
+
if (firstHeading) {
|
| 72 |
+
const txt = (firstHeading.textContent || '').trim().toLowerCase();
|
| 73 |
+
const targetTxt = headingText.trim().toLowerCase();
|
| 74 |
+
if (txt === targetTxt || txt.includes('reference') || txt.includes('bibliograph')) {
|
| 75 |
+
firstHeading.remove();
|
| 76 |
+
}
|
| 77 |
+
}
|
| 78 |
+
ensureHeading(headingText);
|
| 79 |
+
target.appendChild(element);
|
| 80 |
+
return true;
|
| 81 |
+
};
|
| 82 |
+
|
| 83 |
+
const autoResizeTextareas = () => {
|
| 84 |
+
const areas = footer.querySelectorAll('.citation-text, .citation-bibtex');
|
| 85 |
+
areas.forEach((ta) => {
|
| 86 |
+
ta.style.height = 'auto';
|
| 87 |
+
const min = 44;
|
| 88 |
+
const next = Math.max(ta.scrollHeight, min);
|
| 89 |
+
ta.style.height = next + 'px';
|
| 90 |
+
});
|
| 91 |
+
};
|
| 92 |
+
|
| 93 |
+
const run = () => {
|
| 94 |
+
const referencesEl = findFirstOutsideFooter(['#references', '.references', '.bibliography']);
|
| 95 |
+
const footnotesEl = findFirstOutsideFooter(['.footnotes']);
|
| 96 |
+
const movedRefs = moveIntoFooter(referencesEl, 'References');
|
| 97 |
+
const movedNotes = moveIntoFooter(footnotesEl, 'Footnotes');
|
| 98 |
+
autoResizeTextareas();
|
| 99 |
+
return movedRefs || movedNotes;
|
| 100 |
+
};
|
| 101 |
+
|
| 102 |
+
// Try now; if not found yet, try again on DOM ready
|
| 103 |
+
const done = run();
|
| 104 |
+
if (!done) {
|
| 105 |
+
const onReady = () => run();
|
| 106 |
+
if (document.readyState === 'loading') {
|
| 107 |
+
document.addEventListener('DOMContentLoaded', onReady, { once: true });
|
| 108 |
+
} else {
|
| 109 |
+
setTimeout(onReady, 0);
|
| 110 |
+
}
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
// Resize on window changes (e.g., fonts, layout)
|
| 114 |
+
window.addEventListener('resize', () => {
|
| 115 |
+
// throttle via rAF
|
| 116 |
+
let raf = null;
|
| 117 |
+
if (raf) cancelAnimationFrame(raf);
|
| 118 |
+
raf = requestAnimationFrame(() => {
|
| 119 |
+
autoResizeTextareas();
|
| 120 |
+
raf = null;
|
| 121 |
+
});
|
| 122 |
+
}, { passive: true });
|
| 123 |
+
})();
|
| 124 |
+
</script>
|
| 125 |
+
|
| 126 |
+
|
app/src/pages/article.mdx
CHANGED
|
@@ -2,9 +2,9 @@
|
|
| 2 |
title: "The science template:\nCraft Beautiful Blogs"
|
| 3 |
description: "A modern, MDX-first research article template with math, citations, and interactive figures."
|
| 4 |
authors:
|
| 5 |
-
- "
|
| 6 |
-
- "
|
| 7 |
-
- "
|
| 8 |
affiliation: "Hugging Face"
|
| 9 |
published: "Feb 19, 2025"
|
| 10 |
tags:
|
|
@@ -15,17 +15,26 @@ ogImage: "https://example.com/your-og-image.png"
|
|
| 15 |
|
| 16 |
import HtmlFragment from "../components/HtmlFragment.astro";
|
| 17 |
import { Image } from 'astro:assets';
|
| 18 |
-
import placeholder from "../assets/images/placeholder.
|
| 19 |
import audioDemo from "../assets/audio/audio-example.wav";
|
| 20 |
import Aside from "../components/Aside.astro";
|
| 21 |
import visualPoster from "../assets/images/visual-vocabulary-poster.png";
|
| 22 |
|
| 23 |
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
## Features
|
| 31 |
|
|
@@ -34,13 +43,14 @@ In this guide, you’ll learn how to install the template, write content (math,
|
|
| 34 |
<span className="tag">KaTeX math</span>
|
| 35 |
<span className="tag">Citations & footnotes</span>
|
| 36 |
<span className="tag">Automatic build</span>
|
| 37 |
-
<span className="tag">
|
| 38 |
<span className="tag">Dark theme</span>
|
| 39 |
<span className="tag">HTML fragments</span>
|
| 40 |
<span className="tag">Plotly ready</span>
|
| 41 |
<span className="tag">SEO Friendly</span>
|
| 42 |
<span className="tag">Lightweight bundle(\<500ko)</span>
|
| 43 |
-
<span className="tag">
|
|
|
|
| 44 |
</div>
|
| 45 |
|
| 46 |
## Getting Started
|
|
@@ -66,6 +76,52 @@ Large assets: track binaries (e.g., `.png`, `.wav`) with Git LFS to keep the rep
|
|
| 66 |
|
| 67 |
Author content in MDX. Below are minimal examples for the core elements.
|
| 68 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
### Math
|
| 70 |
|
| 71 |
Inline example: $x^2 + y^2 = z^2$.
|
|
@@ -87,29 +143,114 @@ $$
|
|
| 87 |
|
| 88 |
### Images
|
| 89 |
|
| 90 |
-
Responsive
|
|
|
|
|
|
|
| 91 |
|
| 92 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
|
| 94 |
```mdx
|
| 95 |
import { Image } from 'astro:assets'
|
| 96 |
-
import myImage from '../assets/images/placeholder.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
|
| 98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
```
|
| 100 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
### Citations and notes
|
| 102 |
|
| 103 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
|
| 105 |
-
[
|
|
|
|
|
|
|
| 106 |
|
| 107 |
```mdx
|
| 108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
|
| 110 |
-
[^
|
| 111 |
```
|
| 112 |
|
|
|
|
| 113 |
### Interactive fragments
|
| 114 |
|
| 115 |
Plotly example (line):
|
|
@@ -170,12 +311,19 @@ import audioDemo from '../assets/audio/audio-example.wav'
|
|
| 170 |
</audio>
|
| 171 |
```
|
| 172 |
|
| 173 |
-
|
|
|
|
|
|
|
| 174 |
|
| 175 |
-
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
|
| 180 |
## Best Practices
|
| 181 |
|
|
|
|
| 2 |
title: "The science template:\nCraft Beautiful Blogs"
|
| 3 |
description: "A modern, MDX-first research article template with math, citations, and interactive figures."
|
| 4 |
authors:
|
| 5 |
+
- "John Doe"
|
| 6 |
+
- "Alice Martin"
|
| 7 |
+
- "Robert Brown"
|
| 8 |
affiliation: "Hugging Face"
|
| 9 |
published: "Feb 19, 2025"
|
| 10 |
tags:
|
|
|
|
| 15 |
|
| 16 |
import HtmlFragment from "../components/HtmlFragment.astro";
|
| 17 |
import { Image } from 'astro:assets';
|
| 18 |
+
import placeholder from "../assets/images/placeholder.jpg";
|
| 19 |
import audioDemo from "../assets/audio/audio-example.wav";
|
| 20 |
import Aside from "../components/Aside.astro";
|
| 21 |
import visualPoster from "../assets/images/visual-vocabulary-poster.png";
|
| 22 |
|
| 23 |
|
| 24 |
+
<Aside>
|
| 25 |
+
Welcome to this single-page research article template built with Astro and MDX.
|
| 26 |
+
It’s designed to help you write clear, modern, and interactive technical articles with minimal setup.
|
| 27 |
+
Whether you cover machine learning, data science, physics, or software topics, this template keeps the authoring flow simple while offering robust features out of the box.
|
| 28 |
+
<Fragment slot="aside">
|
| 29 |
+
Reading time: 10–15 minutes.
|
| 30 |
+
</Fragment>
|
| 31 |
+
In this guide, you’ll learn how to install the template,
|
| 32 |
+
write content (math, citations, images, code, asides, interactive fragments),
|
| 33 |
+
customize styles and behavior, and follow a few best practices for publishing.
|
| 34 |
+
<Fragment slot="aside">
|
| 35 |
+
If you have questions or remarks open a discussion on the <a href="https://huggingface.co/spaces/tfrere/science-blog-template/discussions?status=open&type=discussion">Community tab</a>!
|
| 36 |
+
</Fragment>
|
| 37 |
+
</Aside>
|
| 38 |
|
| 39 |
## Features
|
| 40 |
|
|
|
|
| 43 |
<span className="tag">KaTeX math</span>
|
| 44 |
<span className="tag">Citations & footnotes</span>
|
| 45 |
<span className="tag">Automatic build</span>
|
| 46 |
+
<span className="tag">Table of content</span>
|
| 47 |
<span className="tag">Dark theme</span>
|
| 48 |
<span className="tag">HTML fragments</span>
|
| 49 |
<span className="tag">Plotly ready</span>
|
| 50 |
<span className="tag">SEO Friendly</span>
|
| 51 |
<span className="tag">Lightweight bundle(\<500ko)</span>
|
| 52 |
+
<span className="tag">Aside notes</span>
|
| 53 |
+
<span className="tag">Medium like zoomable images</span>
|
| 54 |
</div>
|
| 55 |
|
| 56 |
## Getting Started
|
|
|
|
| 76 |
|
| 77 |
Author content in MDX. Below are minimal examples for the core elements.
|
| 78 |
|
| 79 |
+
### MDX: frontmatter and imports (minimal example)
|
| 80 |
+
|
| 81 |
+
The initial skeleton of this article looks like this:
|
| 82 |
+
|
| 83 |
+
```mdx
|
| 84 |
+
---
|
| 85 |
+
title: "The science template:\nCraft Beautiful Blogs"
|
| 86 |
+
description: "A modern, MDX-first research article template with math, citations, and interactive figures."
|
| 87 |
+
authors:
|
| 88 |
+
- "John Doe"
|
| 89 |
+
- "Alice Martin"
|
| 90 |
+
- "Robert Brown"
|
| 91 |
+
affiliation: "Hugging Face"
|
| 92 |
+
published: "Feb 19, 2025"
|
| 93 |
+
tags:
|
| 94 |
+
- research
|
| 95 |
+
- template
|
| 96 |
+
ogImage: "https://example.com/your-og-image.png"
|
| 97 |
+
---
|
| 98 |
+
|
| 99 |
+
import HtmlFragment from "../components/HtmlFragment.astro";
|
| 100 |
+
import { Image } from 'astro:assets';
|
| 101 |
+
import placeholder from "../assets/images/placeholder.png";
|
| 102 |
+
import audioDemo from "../assets/audio/audio-example.wav";
|
| 103 |
+
import Aside from "../components/Aside.astro";
|
| 104 |
+
import visualPoster from "../assets/images/visual-vocabulary-poster.png";
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
<Aside>
|
| 108 |
+
Welcome to this single-page research article template built with Astro and MDX.
|
| 109 |
+
It’s designed to help you write clear, modern, and interactive technical articles with minimal setup.
|
| 110 |
+
Whether you cover machine learning, data science, physics, or software topics, this template keeps the authoring flow simple while offering robust features out of the box.
|
| 111 |
+
<Fragment slot="aside">
|
| 112 |
+
Reading time: 10–15 minutes.
|
| 113 |
+
</Fragment>
|
| 114 |
+
In this guide, you’ll learn how to install the template,
|
| 115 |
+
write content (math, citations, images, code, asides, interactive fragments),
|
| 116 |
+
customize styles and behavior, and follow a few best practices for publishing.
|
| 117 |
+
<Fragment slot="aside">
|
| 118 |
+
If you have questions or remarks open a discussion on the <a href="https://huggingface.co/spaces/lvwerra/distill-blog-template/discussions?status=open&type=discussion">Community tab</a>!
|
| 119 |
+
</Fragment>
|
| 120 |
+
</Aside>
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
```
|
| 124 |
+
|
| 125 |
### Math
|
| 126 |
|
| 127 |
Inline example: $x^2 + y^2 = z^2$.
|
|
|
|
| 143 |
|
| 144 |
### Images
|
| 145 |
|
| 146 |
+
Responsive images automatically generate an optimized `srcset` and `sizes` so the browser downloads the most appropriate file for the current viewport and DPR. You can also request multiple output formats (e.g., **AVIF**, **WebP**, fallback **PNG/JPEG**) and control lazy loading/decoding for better performance.
|
| 147 |
+
|
| 148 |
+
**Optional:** Zoomable (Medium-like lightbox): add `data-zoomable` to opt-in. Only images with this attribute will open full-screen on click.
|
| 149 |
|
| 150 |
+
<figure>
|
| 151 |
+
<Image
|
| 152 |
+
src={placeholder}
|
| 153 |
+
data-zoomable
|
| 154 |
+
alt="Example with caption and credit"
|
| 155 |
+
/>
|
| 156 |
+
<figcaption>
|
| 157 |
+
Optimized image with a descriptive caption.
|
| 158 |
+
<span className="image-credit">Crédit: Photo by <a href="https://example.com">Author</a></span>
|
| 159 |
+
</figcaption>
|
| 160 |
+
</figure>
|
| 161 |
|
| 162 |
```mdx
|
| 163 |
import { Image } from 'astro:assets'
|
| 164 |
+
import myImage from '../assets/images/placeholder.jpg'
|
| 165 |
+
|
| 166 |
+
<Image src={myImage} alt="Responsive, optimized example image" />
|
| 167 |
+
|
| 168 |
+
<figure>
|
| 169 |
+
<Image src={myImage} data-zoomable alt="Example with caption and credit" loading="lazy" />
|
| 170 |
+
<figcaption>
|
| 171 |
+
Optimized image with a descriptive caption.
|
| 172 |
+
<span className="image-credit">Crédit: Photo by <a href="https://example.com">Author</a></span>
|
| 173 |
+
</figcaption>
|
| 174 |
+
</figure>
|
| 175 |
+
```
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
### Code blocks
|
| 179 |
+
|
| 180 |
+
Use fenced code blocks with a language for syntax highlighting.
|
| 181 |
|
| 182 |
+
Python block example:
|
| 183 |
+
|
| 184 |
+
```python
|
| 185 |
+
def greet(name: str) -> None:
|
| 186 |
+
print(f"Hello, {name}!")
|
| 187 |
+
|
| 188 |
+
greet("Astro")
|
| 189 |
```
|
| 190 |
|
| 191 |
+
How to write it in MDX:
|
| 192 |
+
|
| 193 |
+
````mdx
|
| 194 |
+
```python
|
| 195 |
+
def greet(name: str) -> None:
|
| 196 |
+
print(f"Hello, {name}!")
|
| 197 |
+
|
| 198 |
+
greet("Astro")
|
| 199 |
+
```
|
| 200 |
+
````
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
### GitHub code embeds
|
| 204 |
+
|
| 205 |
+
Finally, if you want to include code from GitHub you can use emgithub.com and, for example, create a collapsible widget like this:
|
| 206 |
+
|
| 207 |
+
```html
|
| 208 |
+
<details style="background: #f6f8fa; border: 1px solid #d0d7de; border-radius: 6px; margin: 1em 0;">
|
| 209 |
+
<summary style="padding: 12px; cursor: pointer; user-select: none; background: #f3f4f6; border-bottom: 1px solid #d0d7de;">
|
| 210 |
+
👉 Naive DP implementation with overlap in Picotron (Click to expand)
|
| 211 |
+
</summary>
|
| 212 |
+
<div class="code-embed-container" style="margin: 0; border-radius: 0; overflow-x: scroll; width: max-content; min-width: 100%; font-size: 8px;"></div>
|
| 213 |
+
<script
|
| 214 |
+
src="https://emgithub.com/embed-v2.js?target=https%3A%2F%2Fgithub.com%2Fhuggingface%2Fpicotron%2Fblob%2F0035cce0e04afd6192763b11efe50010d8ad0f71%2Fpicotron%2Fdata_parallel%2Fdata_parallel.py%23L10-L60&style=github&type=code&showBorder=off&showLineNumbers=on&showFileMeta=on&showCopy=on&showFullPath=on">
|
| 215 |
+
</script>
|
| 216 |
+
</details>
|
| 217 |
+
```
|
| 218 |
+
|
| 219 |
+
Which will display as follows:
|
| 220 |
+
|
| 221 |
+
<details style="background: #f6f8fa; border: 1px solid #d0d7de; border-radius: 6px; margin: 1em 0;">
|
| 222 |
+
<summary style="padding: 12px; cursor: pointer; user-select: none; background: #f3f4f6; border-bottom: 1px solid #d0d7de;">
|
| 223 |
+
👉 Naive DP implementation with overlap in Picotron (Click to expand)
|
| 224 |
+
</summary>
|
| 225 |
+
<div class="code-embed-container" style="margin: 0; border-radius: 0; overflow-x: scroll; width: max-content; min-width: 100%; font-size: 8px;"></div>
|
| 226 |
+
<script
|
| 227 |
+
src="https://emgithub.com/embed-v2.js?target=https%3A%2F%2Fgithub.com%2Fhuggingface%2Fpicotron%2Fblob%2F0035cce0e04afd6192763b11efe50010d8ad0f71%2Fpicotron%2Fdata_parallel%2Fdata_parallel.py%23L10-L60&style=github&type=code&showBorder=off&showLineNumbers=on&showFileMeta=on&showCopy=on&showFullPath=on">
|
| 228 |
+
</script>
|
| 229 |
+
</details>
|
| 230 |
+
|
| 231 |
### Citations and notes
|
| 232 |
|
| 233 |
+
Here are a few variations using the same bibliography:
|
| 234 |
+
|
| 235 |
+
1) In-text citation with brackets: [@example2023].
|
| 236 |
+
|
| 237 |
+
2) Narrative citation: As shown by @vaswani2017attention, transformers enable efficient sequence modeling.
|
| 238 |
|
| 239 |
+
3) Multiple citations and a footnote together: see [@vaswani2017attention; @example2023] for related work. Also note this footnote[^f1].
|
| 240 |
+
|
| 241 |
+
[^f1]: Footnote attached to the sentence above.
|
| 242 |
|
| 243 |
```mdx
|
| 244 |
+
1) In-text citation with brackets: [@example2023].
|
| 245 |
+
|
| 246 |
+
2) Narrative citation: As shown by @vaswani2017attention, transformers enable efficient sequence modeling.
|
| 247 |
+
|
| 248 |
+
3) Multiple citations and a footnote together: see [@vaswani2017attention; @example2023] for related work. Also note this footnote[^f1].
|
| 249 |
|
| 250 |
+
[^f1]: Footnote attached to the sentence above.
|
| 251 |
```
|
| 252 |
|
| 253 |
+
|
| 254 |
### Interactive fragments
|
| 255 |
|
| 256 |
Plotly example (line):
|
|
|
|
| 311 |
</audio>
|
| 312 |
```
|
| 313 |
|
| 314 |
+
### Tracking training metrics with TrackIO
|
| 315 |
+
|
| 316 |
+
TrackIO is a lightweight dashboard to monitor ML experiments. The embed below opens a demo project and shows a couple of metrics. You can customize the query parameters (`project`, `metrics`, `sidebar`) to fit your setup.
|
| 317 |
|
| 318 |
+
<div style="margin-top: 8px;">
|
| 319 |
+
<iframe src="https://trackio-documentation.hf.space/?project=fake-training-750735&metrics=train_loss,train_accuracy&sidebar=hidden" width="100%" height="600" frameborder="0"></iframe>
|
| 320 |
+
</div>
|
| 321 |
+
|
| 322 |
+
To embed TrackIO in your own page, copy the following HTML and adjust the `src` parameters:
|
| 323 |
+
|
| 324 |
+
```html
|
| 325 |
+
<iframe src="https://org_name-space_name.hf.space/?project=fake-training&metrics=train_loss,train_accuracy&sidebar=hidden" width="100%" height="600" frameBorder="0"></iframe>
|
| 326 |
+
```
|
| 327 |
|
| 328 |
## Best Practices
|
| 329 |
|
app/src/pages/index.astro
CHANGED
|
@@ -22,6 +22,31 @@ const fmOg = articleFM?.ogImage as string | undefined;
|
|
| 22 |
const imageAbs = fmOg && fmOg.startsWith('http')
|
| 23 |
? fmOg
|
| 24 |
: (Astro.site ? new URL((fmOg ?? ogDefault.src), Astro.site).toString() : (fmOg ?? ogDefault.src));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
---
|
| 26 |
<html lang="en">
|
| 27 |
<head>
|
|
@@ -60,14 +85,7 @@ const imageAbs = fmOg && fmOg.startsWith('http')
|
|
| 60 |
</main>
|
| 61 |
</section>
|
| 62 |
|
| 63 |
-
<Footer citationText=
|
| 64 |
-
title={The Distill Blog Template},
|
| 65 |
-
author={Some Authors et al},
|
| 66 |
-
year={2025},
|
| 67 |
-
}`}
|
| 68 |
-
>
|
| 69 |
-
<div class="references"><h3>References</h3></div>
|
| 70 |
-
</Footer>
|
| 71 |
|
| 72 |
<!-- Medium-like image zoom (lightbox) -->
|
| 73 |
<script src="https://cdn.jsdelivr.net/npm/medium-zoom@1.1.0/dist/medium-zoom.min.js"></script>
|
|
@@ -212,6 +230,43 @@ const imageAbs = fmOg && fmOg.startsWith('http')
|
|
| 212 |
document.addEventListener('DOMContentLoaded', buildTOC, { once: true });
|
| 213 |
} else { buildTOC(); }
|
| 214 |
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 215 |
</body>
|
| 216 |
</html>
|
| 217 |
|
|
|
|
| 22 |
const imageAbs = fmOg && fmOg.startsWith('http')
|
| 23 |
? fmOg
|
| 24 |
: (Astro.site ? new URL((fmOg ?? ogDefault.src), Astro.site).toString() : (fmOg ?? ogDefault.src));
|
| 25 |
+
|
| 26 |
+
// ---- Build citation text & BibTeX from frontmatter ----
|
| 27 |
+
const rawTitle = articleFM?.title ?? 'Untitled article';
|
| 28 |
+
const titleFlat = String(rawTitle)
|
| 29 |
+
.replace(/\\n/g, ' ')
|
| 30 |
+
.replace(/\n/g, ' ')
|
| 31 |
+
.replace(/\s+/g, ' ')
|
| 32 |
+
.trim();
|
| 33 |
+
const extractYear = (val: string | undefined): number | undefined => {
|
| 34 |
+
if (!val) return undefined;
|
| 35 |
+
const d = new Date(val);
|
| 36 |
+
if (!Number.isNaN(d.getTime())) return d.getFullYear();
|
| 37 |
+
const m = String(val).match(/(19|20)\d{2}/);
|
| 38 |
+
return m ? Number(m[0]) : undefined;
|
| 39 |
+
};
|
| 40 |
+
|
| 41 |
+
const year = extractYear(published);
|
| 42 |
+
const citationAuthorsText = authors.join(', ');
|
| 43 |
+
const citationText = `${citationAuthorsText}${year ? ` (${year})` : ''}. "${titleFlat}".`;
|
| 44 |
+
|
| 45 |
+
const authorsBib = authors.join(' and ');
|
| 46 |
+
const keyAuthor = (authors[0] || 'article').split(/\s+/).slice(-1)[0].toLowerCase();
|
| 47 |
+
const keyTitle = titleFlat.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_|_$/g, '').slice(0, 24);
|
| 48 |
+
const bibKey = `${keyAuthor}${year ?? ''}_${keyTitle}`;
|
| 49 |
+
const bibtex = `@misc{${bibKey},\n title={${titleFlat}},\n author={${authorsBib}},\n ${year ? `year={${year}}` : ''}\n}`;
|
| 50 |
---
|
| 51 |
<html lang="en">
|
| 52 |
<head>
|
|
|
|
| 85 |
</main>
|
| 86 |
</section>
|
| 87 |
|
| 88 |
+
<Footer citationText={citationText} bibtex={bibtex} />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
<!-- Medium-like image zoom (lightbox) -->
|
| 91 |
<script src="https://cdn.jsdelivr.net/npm/medium-zoom@1.1.0/dist/medium-zoom.min.js"></script>
|
|
|
|
| 230 |
document.addEventListener('DOMContentLoaded', buildTOC, { once: true });
|
| 231 |
} else { buildTOC(); }
|
| 232 |
</script>
|
| 233 |
+
|
| 234 |
+
<script>
|
| 235 |
+
// Inject visible language badges for code blocks when data-language is missing
|
| 236 |
+
const addCodeLangChips = () => {
|
| 237 |
+
const blocks = document.querySelectorAll('section.content-grid pre > code');
|
| 238 |
+
blocks.forEach(code => {
|
| 239 |
+
const pre = code.parentElement;
|
| 240 |
+
if (!pre || pre.querySelector('.code-lang-chip')) return;
|
| 241 |
+
// Try several places to detect language
|
| 242 |
+
const getLang = () => {
|
| 243 |
+
const direct = code.getAttribute('data-language') || code.dataset?.language;
|
| 244 |
+
if (direct) return direct;
|
| 245 |
+
const codeClass = (code.className || '').match(/language-([a-z0-9+\-]+)/i);
|
| 246 |
+
if (codeClass) return codeClass[1];
|
| 247 |
+
const preData = pre.getAttribute('data-language') || pre.dataset?.language;
|
| 248 |
+
if (preData) return preData;
|
| 249 |
+
const wrapper = pre.closest('.astro-code');
|
| 250 |
+
if (wrapper) {
|
| 251 |
+
const wrapData = wrapper.getAttribute('data-language') || wrapper.dataset?.language;
|
| 252 |
+
if (wrapData) return wrapData;
|
| 253 |
+
const wrapClass = (wrapper.className || '').match(/language-([a-z0-9+\-]+)/i);
|
| 254 |
+
if (wrapClass) return wrapClass[1];
|
| 255 |
+
}
|
| 256 |
+
return 'text';
|
| 257 |
+
};
|
| 258 |
+
const lang = getLang().toUpperCase();
|
| 259 |
+
const chip = document.createElement('span');
|
| 260 |
+
chip.className = 'code-lang-chip';
|
| 261 |
+
chip.textContent = lang;
|
| 262 |
+
pre.classList.add('has-lang-chip');
|
| 263 |
+
pre.appendChild(chip);
|
| 264 |
+
});
|
| 265 |
+
};
|
| 266 |
+
if (document.readyState === 'loading') {
|
| 267 |
+
document.addEventListener('DOMContentLoaded', addCodeLangChips, { once: true });
|
| 268 |
+
} else { addCodeLangChips(); }
|
| 269 |
+
</script>
|
| 270 |
</body>
|
| 271 |
</html>
|
| 272 |
|
app/src/styles/components/_base.scss
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// ============================================================================
|
| 2 |
+
// Base / Reset
|
| 3 |
+
// ============================================================================
|
| 4 |
+
html { box-sizing: border-box; }
|
| 5 |
+
*, *::before, *::after { box-sizing: inherit; }
|
| 6 |
+
body { margin: 0; font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, Helvetica Neue, Arial, Apple Color Emoji, Segoe UI Emoji; color: var(--text-color); }
|
| 7 |
+
|
| 8 |
+
/* Avoid constraining <main> inside grid; scope container sizing elsewhere if needed */
|
| 9 |
+
/* main { max-width: 980px; margin: 24px auto; padding: 16px; } */
|
| 10 |
+
|
| 11 |
+
// ============================================================================
|
| 12 |
+
// Typography (inspired by Distill)
|
| 13 |
+
// ============================================================================
|
| 14 |
+
html { font-size: 14px; line-height: 1.6; }
|
| 15 |
+
@media (min-width: 768px) { html { font-size: 16px; } }
|
| 16 |
+
@media (min-width: 1024px) { html { font-size: 17px; } }
|
| 17 |
+
|
| 18 |
+
.content-grid main { color: var(--text-color); }
|
| 19 |
+
.content-grid main p { margin: 0 0 var(--spacing-3); }
|
| 20 |
+
|
| 21 |
+
.content-grid main h2 {
|
| 22 |
+
font-weight: 600;
|
| 23 |
+
font-size: clamp(22px, 2.6vw, 32px);
|
| 24 |
+
line-height: 1.2;
|
| 25 |
+
margin: var(--spacing-5) 0 var(--spacing-3);
|
| 26 |
+
padding-bottom: var(--spacing-2);
|
| 27 |
+
border-bottom: 1px solid var(--border-color);
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
.content-grid main h3 {
|
| 31 |
+
font-weight: 700;
|
| 32 |
+
font-size: clamp(18px, 2.1vw, 22px);
|
| 33 |
+
line-height: 1.25;
|
| 34 |
+
margin: var(--spacing-4) 0 var(--spacing-2);
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
.content-grid main h4 {
|
| 38 |
+
font-weight: 600;
|
| 39 |
+
text-transform: uppercase;
|
| 40 |
+
font-size: 14px;
|
| 41 |
+
line-height: 1.2;
|
| 42 |
+
margin: var(--spacing-3) 0 var(--spacing-2);
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
.content-grid main a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--link-underline); }
|
| 46 |
+
.content-grid main a:hover { border-bottom: 1px solid var(--link-underline-hover); }
|
| 47 |
+
|
| 48 |
+
/* Ne pas souligner les liens de titres dans l'article (pas le TOC) */
|
| 49 |
+
.content-grid main h2 a,
|
| 50 |
+
.content-grid main h3 a,
|
| 51 |
+
.content-grid main h4 a { border-bottom: none; text-decoration: none; }
|
| 52 |
+
.content-grid main h2 a:hover,
|
| 53 |
+
.content-grid main h3 a:hover,
|
| 54 |
+
.content-grid main h4 a:hover { border-bottom: none; text-decoration: none; }
|
| 55 |
+
|
| 56 |
+
.content-grid main ul,
|
| 57 |
+
.content-grid main ol { padding-left: 24px; margin: 0 0 var(--spacing-3); }
|
| 58 |
+
.content-grid main li { margin-bottom: var(--spacing-2); }
|
| 59 |
+
.content-grid main li:last-child { margin-bottom: 0; }
|
| 60 |
+
|
| 61 |
+
.content-grid main blockquote {
|
| 62 |
+
border-left: 2px solid var(--border-color);
|
| 63 |
+
padding-left: var(--spacing-4);
|
| 64 |
+
font-style: italic;
|
| 65 |
+
color: var(--muted-color);
|
| 66 |
+
margin: var(--spacing-4) 0;
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
.content-grid main pre { background: var(--code-bg); border: 1px solid var(--border-color); border-radius: 6px; padding: var(--spacing-3); font-size: 14px; overflow: auto; }
|
| 70 |
+
.content-grid main code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
|
| 71 |
+
|
| 72 |
+
/* Pretty-code language label (visible chip at top-right) */
|
| 73 |
+
.content-grid main pre:has(code[data-language]),
|
| 74 |
+
.content-grid main pre:has(code[class*="language-"]) {
|
| 75 |
+
position: relative;
|
| 76 |
+
padding-top: 28px; /* space for the label */
|
| 77 |
+
}
|
| 78 |
+
.content-grid main pre > code[data-language]::after,
|
| 79 |
+
.content-grid main pre > code[class*="language-"]::after {
|
| 80 |
+
content: attr(data-language);
|
| 81 |
+
position: absolute;
|
| 82 |
+
top: 4px;
|
| 83 |
+
right: 6px;
|
| 84 |
+
font-size: 11px;
|
| 85 |
+
line-height: 1;
|
| 86 |
+
text-transform: uppercase;
|
| 87 |
+
color: var(--muted-color);
|
| 88 |
+
background: transparent;
|
| 89 |
+
border: none;
|
| 90 |
+
border-radius: 4px;
|
| 91 |
+
padding: 2px 4px;
|
| 92 |
+
pointer-events: none;
|
| 93 |
+
z-index: 1;
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
/* JS fallback chip */
|
| 97 |
+
.content-grid main pre.has-lang-chip { position: relative; padding-top: 22px; }
|
| 98 |
+
.content-grid main pre .code-lang-chip {
|
| 99 |
+
position: absolute;
|
| 100 |
+
top: 0px; right: 0px;
|
| 101 |
+
font-size: 10px; line-height: 1;
|
| 102 |
+
color: rgba(255,255,255,.5);
|
| 103 |
+
background: rgba(255,255,255,.1);
|
| 104 |
+
border: none;
|
| 105 |
+
border-radius: 0px; padding: 6px 6px 4px 4px; pointer-events: none; z-index: 1;
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
.content-grid main table { border-collapse: collapse; width: 100%; margin: 0 0 var(--spacing-4); }
|
| 109 |
+
.content-grid main th, .content-grid main td { border-bottom: 1px solid var(--border-color); padding: 6px 8px; text-align: left; font-size: 15px; }
|
| 110 |
+
.content-grid main thead th { border-bottom: 1px solid var(--border-color); }
|
| 111 |
+
|
| 112 |
+
.content-grid main hr { border: none; border-bottom: 1px solid var(--border-color); margin: var(--spacing-5) 0; }
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
// ============================================================================
|
| 118 |
+
// Media / Figures
|
| 119 |
+
// ============================================================================
|
| 120 |
+
:where(picture, img) {
|
| 121 |
+
max-width: 100%;
|
| 122 |
+
width: 100%;
|
| 123 |
+
height: auto;
|
| 124 |
+
display: block;
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
figure { margin: 16px 0; }
|
| 128 |
+
figcaption { color: var(--muted-color); font-size: 12px; }
|
| 129 |
+
|
| 130 |
+
// Inline feature tags
|
| 131 |
+
.tag-list { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 16px; }
|
| 132 |
+
.tag {
|
| 133 |
+
display: inline-flex;
|
| 134 |
+
align-items: center;
|
| 135 |
+
gap: 6px;
|
| 136 |
+
padding: 4px 8px;
|
| 137 |
+
font-size: 12px;
|
| 138 |
+
line-height: 1;
|
| 139 |
+
border-radius: 999px;
|
| 140 |
+
background: var(--surface-bg);
|
| 141 |
+
border: 1px solid var(--border-color);
|
| 142 |
+
color: var(--text-color);
|
| 143 |
+
}
|
| 144 |
+
[data-theme="dark"] .tag { background: #1a1f27; border-color: rgba(255,255,255,.15); }
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
// ============================================================================
|
| 148 |
+
// Figures, captions & image credits
|
| 149 |
+
// ============================================================================
|
| 150 |
+
figure { margin: 12px 0; }
|
| 151 |
+
figcaption { text-align: center; font-size: 0.9rem; color: var(--muted-color); margin-top: 6px; }
|
| 152 |
+
.image-credit { display: block; margin-top: 4px; font-size: 12px; color: var(--muted-color); }
|
| 153 |
+
.image-credit a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
|
app/src/styles/components/_layout.scss
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// ============================================================================
|
| 2 |
+
// Layout – 3-column grid (TOC / Article / Aside)
|
| 3 |
+
// ============================================================================
|
| 4 |
+
.content-grid { max-width: 1280px; margin: 0 auto; padding: 0 16px; margin-top: 40px; display: grid; grid-template-columns: 220px minmax(0, 680px) 260px; gap: 32px; align-items: start; }
|
| 5 |
+
.content-grid > main { max-width: 100%; margin: 0; padding: 0; }
|
| 6 |
+
|
| 7 |
+
// TOC (left column)
|
| 8 |
+
.toc { position: sticky; top: 24px; }
|
| 9 |
+
.toc nav { border-left: 1px solid var(--border-color); padding-left: 16px; font-size: 13px; }
|
| 10 |
+
.toc .title { font-weight: 600; font-size: 14px; margin-bottom: 8px; }
|
| 11 |
+
|
| 12 |
+
// Hide in-article TOC (duplicated by sticky aside)
|
| 13 |
+
main > nav:first-of-type { display: none; }
|
| 14 |
+
|
| 15 |
+
// TOC look & feel
|
| 16 |
+
.toc nav ul { margin: 0 0 6px; padding-left: 1em; }
|
| 17 |
+
.toc nav li { list-style: none; margin: .25em 0; }
|
| 18 |
+
.toc nav a { color: var(--text-color); text-decoration: none; border-bottom: none; }
|
| 19 |
+
.toc nav > ul > li > a { font-weight: 700; }
|
| 20 |
+
.toc nav a:hover { text-decoration: underline solid var(--muted-color); }
|
| 21 |
+
.toc nav a.active { text-decoration: underline; }
|
| 22 |
+
|
| 23 |
+
// Right aside (notes)
|
| 24 |
+
.right-aside { position: sticky; top: 24px; }
|
| 25 |
+
.right-aside .aside-card { background: var(--surface-bg); border: 1px solid var(--border-color); border-radius: 8px; padding: 10px; margin-bottom: 10px; font-size: 0.9rem; color: var(--text-color); }
|
| 26 |
+
|
| 27 |
+
// Responsive – collapse to single column
|
| 28 |
+
@media (max-width: 1100px) {
|
| 29 |
+
.content-grid { grid-template-columns: 1fr; }
|
| 30 |
+
.toc { position: static; }
|
| 31 |
+
.right-aside { display: none; }
|
| 32 |
+
main > nav:first-of-type { display: block; }
|
| 33 |
+
}
|
app/src/styles/components/_variables.scss
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// ============================================================================
|
| 2 |
+
// Theme Variables (inspired by Distill)
|
| 3 |
+
// ============================================================================
|
| 4 |
+
:root {
|
| 5 |
+
--distill-gray: rgb(107, 114, 128);
|
| 6 |
+
--distill-gray-light: rgb(185, 185, 185);
|
| 7 |
+
--distill-gray-lighter: rgb(228, 228, 228);
|
| 8 |
+
--distill-gray-lightest: rgb(245, 245, 245);
|
| 9 |
+
--distill-blue: #007BFF;
|
| 10 |
+
|
| 11 |
+
--text-color: rgba(0,0,0,.85);
|
| 12 |
+
--muted-color: rgba(0,0,0,.6);
|
| 13 |
+
--border-color: rgba(0,0,0,.1);
|
| 14 |
+
|
| 15 |
+
/* Light surfaces & links */
|
| 16 |
+
--surface-bg: #fafafa;
|
| 17 |
+
--code-bg: #f6f8fa;
|
| 18 |
+
--link-underline: rgba(0,0,0,.4);
|
| 19 |
+
--link-underline-hover: rgba(0,0,0,.8);
|
| 20 |
+
|
| 21 |
+
--spacing-1: 8px;
|
| 22 |
+
--spacing-2: 12px;
|
| 23 |
+
--spacing-3: 16px;
|
| 24 |
+
--spacing-4: 24px;
|
| 25 |
+
--spacing-5: 32px;
|
| 26 |
+
}
|
| 27 |
+
// Theme tokens for dark mode
|
| 28 |
+
[data-theme="dark"] {
|
| 29 |
+
--text-color: rgba(255,255,255,.9);
|
| 30 |
+
--muted-color: rgba(255,255,255,.7);
|
| 31 |
+
--border-color: rgba(255,255,255,.15);
|
| 32 |
+
--surface-bg: #12151b;
|
| 33 |
+
--code-bg: #12151b;
|
| 34 |
+
--link-underline: rgba(255,255,255,.5);
|
| 35 |
+
--link-underline-hover: rgba(255,255,255,.9);
|
| 36 |
+
color-scheme: dark;
|
| 37 |
+
background: #0f1115;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
|
app/src/styles/global.scss
CHANGED
|
@@ -1,41 +1,6 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
:root {
|
| 5 |
-
--distill-gray: rgb(107, 114, 128);
|
| 6 |
-
--distill-gray-light: rgb(185, 185, 185);
|
| 7 |
-
--distill-gray-lighter: rgb(228, 228, 228);
|
| 8 |
-
--distill-gray-lightest: rgb(245, 245, 245);
|
| 9 |
-
--distill-blue: #007BFF;
|
| 10 |
-
|
| 11 |
-
--text-color: rgba(0,0,0,.85);
|
| 12 |
-
--muted-color: rgba(0,0,0,.6);
|
| 13 |
-
--border-color: rgba(0,0,0,.1);
|
| 14 |
-
|
| 15 |
-
/* Light surfaces & links */
|
| 16 |
-
--surface-bg: #fafafa;
|
| 17 |
-
--code-bg: #f6f8fa;
|
| 18 |
-
--link-underline: rgba(0,0,0,.4);
|
| 19 |
-
--link-underline-hover: rgba(0,0,0,.8);
|
| 20 |
-
|
| 21 |
-
--spacing-1: 8px;
|
| 22 |
-
--spacing-2: 12px;
|
| 23 |
-
--spacing-3: 16px;
|
| 24 |
-
--spacing-4: 24px;
|
| 25 |
-
--spacing-5: 32px;
|
| 26 |
-
}
|
| 27 |
-
// Theme tokens for dark mode
|
| 28 |
-
[data-theme="dark"] {
|
| 29 |
-
--text-color: rgba(255,255,255,.9);
|
| 30 |
-
--muted-color: rgba(255,255,255,.7);
|
| 31 |
-
--border-color: rgba(255,255,255,.15);
|
| 32 |
-
--surface-bg: #12151b;
|
| 33 |
-
--code-bg: #12151b;
|
| 34 |
-
--link-underline: rgba(255,255,255,.5);
|
| 35 |
-
--link-underline-hover: rgba(255,255,255,.9);
|
| 36 |
-
color-scheme: dark;
|
| 37 |
-
background: #0f1115;
|
| 38 |
-
}
|
| 39 |
/* Dark-mode form tweak */
|
| 40 |
[data-theme="dark"] .plotly_input_container > select { background-color: #1a1f27; border-color: var(--border-color); color: var(--text-color); }
|
| 41 |
|
|
@@ -47,116 +12,34 @@
|
|
| 47 |
[data-theme="dark"] .citation-text,
|
| 48 |
[data-theme="dark"] .citation-bibtex { background: #12151b; border-color: rgba(255,255,255,.15); color: var(--text-color); }
|
| 49 |
|
| 50 |
-
// ============================================================================
|
| 51 |
-
// Base / Reset
|
| 52 |
-
// ============================================================================
|
| 53 |
-
html { box-sizing: border-box; }
|
| 54 |
-
*, *::before, *::after { box-sizing: inherit; }
|
| 55 |
-
body { margin: 0; font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, Helvetica Neue, Arial, Apple Color Emoji, Segoe UI Emoji; color: var(--text-color); }
|
| 56 |
-
|
| 57 |
-
/* Avoid constraining <main> inside grid; scope container sizing elsewhere if needed */
|
| 58 |
-
/* main { max-width: 980px; margin: 24px auto; padding: 16px; } */
|
| 59 |
-
|
| 60 |
-
// ============================================================================
|
| 61 |
-
// Typography (inspired by Distill)
|
| 62 |
-
// ============================================================================
|
| 63 |
-
html { font-size: 14px; line-height: 1.6; }
|
| 64 |
-
@media (min-width: 768px) { html { font-size: 16px; } }
|
| 65 |
-
@media (min-width: 1024px) { html { font-size: 17px; } }
|
| 66 |
-
|
| 67 |
-
.content-grid main { color: var(--text-color); }
|
| 68 |
-
.content-grid main p { margin: 0 0 var(--spacing-3); }
|
| 69 |
-
|
| 70 |
-
.content-grid main h2 {
|
| 71 |
-
font-weight: 600;
|
| 72 |
-
font-size: clamp(22px, 2.6vw, 32px);
|
| 73 |
-
line-height: 1.2;
|
| 74 |
-
margin: var(--spacing-5) 0 var(--spacing-3);
|
| 75 |
-
padding-bottom: var(--spacing-2);
|
| 76 |
-
border-bottom: 1px solid var(--border-color);
|
| 77 |
-
}
|
| 78 |
-
|
| 79 |
-
.content-grid main h3 {
|
| 80 |
-
font-weight: 700;
|
| 81 |
-
font-size: clamp(18px, 2.1vw, 22px);
|
| 82 |
-
line-height: 1.25;
|
| 83 |
-
margin: var(--spacing-4) 0 var(--spacing-2);
|
| 84 |
-
}
|
| 85 |
-
|
| 86 |
-
.content-grid main h4 {
|
| 87 |
-
font-weight: 600;
|
| 88 |
-
text-transform: uppercase;
|
| 89 |
-
font-size: 14px;
|
| 90 |
-
line-height: 1.2;
|
| 91 |
-
margin: var(--spacing-3) 0 var(--spacing-2);
|
| 92 |
-
}
|
| 93 |
-
|
| 94 |
-
.content-grid main a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--link-underline); }
|
| 95 |
-
.content-grid main a:hover { border-bottom: 1px solid var(--link-underline-hover); }
|
| 96 |
-
|
| 97 |
-
/* Ne pas souligner les liens de titres dans l'article (pas le TOC) */
|
| 98 |
-
.content-grid main h2 a,
|
| 99 |
-
.content-grid main h3 a,
|
| 100 |
-
.content-grid main h4 a { border-bottom: none; text-decoration: none; }
|
| 101 |
-
.content-grid main h2 a:hover,
|
| 102 |
-
.content-grid main h3 a:hover,
|
| 103 |
-
.content-grid main h4 a:hover { border-bottom: none; text-decoration: none; }
|
| 104 |
|
| 105 |
-
.
|
| 106 |
-
.content-grid main ol { padding-left: 24px; margin: 0 0 var(--spacing-3); }
|
| 107 |
-
.content-grid main li { margin-bottom: var(--spacing-2); }
|
| 108 |
-
.content-grid main li:last-child { margin-bottom: 0; }
|
| 109 |
|
| 110 |
-
.
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
color: var(--muted-color);
|
| 115 |
-
margin: var(--spacing-4) 0;
|
| 116 |
}
|
| 117 |
-
|
| 118 |
-
.
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
.content-grid main thead th { border-bottom: 1px solid var(--border-color); }
|
| 124 |
-
|
| 125 |
-
.content-grid main hr { border: none; border-bottom: 1px solid var(--border-color); margin: var(--spacing-5) 0; }
|
| 126 |
-
|
| 127 |
-
// ============================================================================
|
| 128 |
-
// Media / Figures
|
| 129 |
-
// ============================================================================
|
| 130 |
-
:where(picture, img) {
|
| 131 |
-
max-width: 100%;
|
| 132 |
-
width: 100%;
|
| 133 |
-
height: auto;
|
| 134 |
-
display: block;
|
| 135 |
}
|
| 136 |
|
| 137 |
-
figure { margin: 16px 0; }
|
| 138 |
-
figcaption { color: var(--muted-color); font-size: 12px; }
|
| 139 |
-
|
| 140 |
-
// Inline feature tags
|
| 141 |
-
.tag-list { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 16px; }
|
| 142 |
-
.tag {
|
| 143 |
-
display: inline-flex;
|
| 144 |
-
align-items: center;
|
| 145 |
-
gap: 6px;
|
| 146 |
-
padding: 4px 8px;
|
| 147 |
-
font-size: 12px;
|
| 148 |
-
line-height: 1;
|
| 149 |
-
border-radius: 999px;
|
| 150 |
-
background: var(--surface-bg);
|
| 151 |
-
border: 1px solid var(--border-color);
|
| 152 |
-
color: var(--text-color);
|
| 153 |
-
}
|
| 154 |
-
[data-theme="dark"] .tag { background: #1a1f27; border-color: rgba(255,255,255,.15); }
|
| 155 |
|
| 156 |
/* Opt-in zoomable images */
|
| 157 |
img[data-zoomable] { cursor: zoom-in; }
|
| 158 |
.medium-zoom--opened img[data-zoomable] { cursor: zoom-out; }
|
| 159 |
|
|
|
|
| 160 |
// ============================================================================
|
| 161 |
// Hero (full-bleed)
|
| 162 |
// ============================================================================
|
|
@@ -202,40 +85,6 @@ img[data-zoomable] { cursor: zoom-in; }
|
|
| 202 |
}
|
| 203 |
.meta-container-cell p { margin: 0; }
|
| 204 |
|
| 205 |
-
// ============================================================================
|
| 206 |
-
// Layout – 3-column grid (TOC / Article / Aside)
|
| 207 |
-
// ============================================================================
|
| 208 |
-
.content-grid { max-width: 1280px; margin: 0 auto; padding: 0 16px; margin-top: 40px; display: grid; grid-template-columns: 220px minmax(0, 680px) 260px; gap: 32px; align-items: start; }
|
| 209 |
-
.content-grid > main { max-width: 100%; margin: 0; padding: 0; }
|
| 210 |
-
|
| 211 |
-
// TOC (left column)
|
| 212 |
-
.toc { position: sticky; top: 24px; }
|
| 213 |
-
.toc nav { border-left: 1px solid var(--border-color); padding-left: 16px; font-size: 13px; }
|
| 214 |
-
.toc .title { font-weight: 600; font-size: 14px; margin-bottom: 8px; }
|
| 215 |
-
|
| 216 |
-
// Hide in-article TOC (duplicated by sticky aside)
|
| 217 |
-
main > nav:first-of-type { display: none; }
|
| 218 |
-
|
| 219 |
-
// TOC look & feel
|
| 220 |
-
.toc nav ul { margin: 0 0 6px; padding-left: 1em; }
|
| 221 |
-
.toc nav li { list-style: none; margin: .25em 0; }
|
| 222 |
-
.toc nav a { color: var(--text-color); text-decoration: none; border-bottom: none; }
|
| 223 |
-
.toc nav > ul > li > a { font-weight: 700; }
|
| 224 |
-
.toc nav a:hover { text-decoration: underline solid var(--muted-color); }
|
| 225 |
-
.toc nav a.active { text-decoration: underline; }
|
| 226 |
-
|
| 227 |
-
// Right aside (notes)
|
| 228 |
-
.right-aside { position: sticky; top: 24px; }
|
| 229 |
-
.right-aside .aside-card { background: var(--surface-bg); border: 1px solid var(--border-color); border-radius: 8px; padding: 10px; margin-bottom: 10px; font-size: 0.9rem; color: var(--text-color); }
|
| 230 |
-
|
| 231 |
-
// Responsive – collapse to single column
|
| 232 |
-
@media (max-width: 1100px) {
|
| 233 |
-
.content-grid { grid-template-columns: 1fr; }
|
| 234 |
-
.toc { position: static; }
|
| 235 |
-
.right-aside { display: none; }
|
| 236 |
-
main > nav:first-of-type { display: block; }
|
| 237 |
-
}
|
| 238 |
-
|
| 239 |
// ============================================================================
|
| 240 |
// Plotly – fragments & controls
|
| 241 |
// ============================================================================
|
|
|
|
| 1 |
+
@import "./components/variables";
|
| 2 |
+
@import "./components/base";
|
| 3 |
+
@import "./components/layout";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
/* Dark-mode form tweak */
|
| 5 |
[data-theme="dark"] .plotly_input_container > select { background-color: #1a1f27; border-color: var(--border-color); color: var(--text-color); }
|
| 6 |
|
|
|
|
| 12 |
[data-theme="dark"] .citation-text,
|
| 13 |
[data-theme="dark"] .citation-bibtex { background: #12151b; border-color: rgba(255,255,255,.15); color: var(--text-color); }
|
| 14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
+
.margin-aside { position: relative; margin: 12px 0; }
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
+
.margin-aside__main { /* main text stays full width of the main column */ }
|
| 19 |
+
.margin-aside__aside {
|
| 20 |
+
position: absolute;
|
| 21 |
+
top: 0;
|
| 22 |
+
right: -260px; /* push into the right grid column (width 260 + gap 32) */
|
| 23 |
+
width: 260px;
|
| 24 |
+
border-radius: 8px;
|
| 25 |
+
padding: 10px;
|
| 26 |
+
font-size: 0.9rem;
|
| 27 |
color: var(--muted-color);
|
|
|
|
| 28 |
}
|
| 29 |
+
@media (max-width: 1100px) {
|
| 30 |
+
.margin-aside__aside {
|
| 31 |
+
position: static;
|
| 32 |
+
width: auto;
|
| 33 |
+
margin-top: 8px;
|
| 34 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
}
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
/* Opt-in zoomable images */
|
| 39 |
img[data-zoomable] { cursor: zoom-in; }
|
| 40 |
.medium-zoom--opened img[data-zoomable] { cursor: zoom-out; }
|
| 41 |
|
| 42 |
+
|
| 43 |
// ============================================================================
|
| 44 |
// Hero (full-bleed)
|
| 45 |
// ============================================================================
|
|
|
|
| 85 |
}
|
| 86 |
.meta-container-cell p { margin: 0; }
|
| 87 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
// ============================================================================
|
| 89 |
// Plotly – fragments & controls
|
| 90 |
// ============================================================================
|