thibaud frere commited on
Commit
9e27095
·
1 Parent(s): e1c7423
app/src/components/Accordion.astro CHANGED
@@ -48,7 +48,7 @@ const wrapperClass = ["accordion", className].filter(Boolean).join(" ");
48
  };
49
 
50
  const close = () => {
51
- const start = content.scrollHeight;
52
  wrapper.style.height = `${start}px`;
53
  void wrapper.offsetHeight; // reflow
54
  wrapper.style.transition = `height ${duration}ms ease`;
@@ -83,7 +83,6 @@ const wrapperClass = ["accordion", className].filter(Boolean).join(" ");
83
  }
84
 
85
  .accordion[open] {
86
- box-shadow: 0 6px 22px rgba(0,0,0,0.08);
87
  border-color: color-mix(in oklab, var(--border-color), var(--primary-color) 20%);
88
  }
89
 
@@ -92,13 +91,15 @@ const wrapperClass = ["accordion", className].filter(Boolean).join(" ");
92
  display: flex;
93
  align-items: center;
94
  justify-content: space-between;
95
- gap: var(--spacing-1);
96
- padding: var(--spacing-1);
97
  cursor: pointer;
98
  color: var(--text-color);
99
  user-select: none;
100
  }
101
 
 
 
102
  /* Remove native marker */
103
  .accordion__summary::-webkit-details-marker {
104
  display: none;
@@ -126,10 +127,23 @@ const wrapperClass = ["accordion", className].filter(Boolean).join(" ");
126
  overflow: hidden;
127
  height: 0px;
128
  will-change: height;
 
129
  }
130
 
131
  .accordion__content {
132
- padding: var(--spacing-1);
 
 
 
 
 
 
 
 
 
 
 
 
133
  }
134
 
135
  /* Focus styles for accessibility */
 
48
  };
49
 
50
  const close = () => {
51
+ const start = wrapper.offsetHeight || content.scrollHeight;
52
  wrapper.style.height = `${start}px`;
53
  void wrapper.offsetHeight; // reflow
54
  wrapper.style.transition = `height ${duration}ms ease`;
 
83
  }
84
 
85
  .accordion[open] {
 
86
  border-color: color-mix(in oklab, var(--border-color), var(--primary-color) 20%);
87
  }
88
 
 
91
  display: flex;
92
  align-items: center;
93
  justify-content: space-between;
94
+ gap: 4px;
95
+ padding: 4px;
96
  cursor: pointer;
97
  color: var(--text-color);
98
  user-select: none;
99
  }
100
 
101
+ /* Remove conditional padding to avoid jump on close */
102
+
103
  /* Remove native marker */
104
  .accordion__summary::-webkit-details-marker {
105
  display: none;
 
127
  overflow: hidden;
128
  height: 0px;
129
  will-change: height;
130
+ position: relative;
131
  }
132
 
133
  .accordion__content {
134
+ padding: 12px 4px 4px;
135
+ }
136
+
137
+ /* Separator between header and content when open (edge-to-edge) */
138
+ .accordion[open] .accordion__content-wrapper::before {
139
+ content: "";
140
+ position: absolute;
141
+ left: 0;
142
+ right: 0;
143
+ top: 6px; /* space below header */
144
+ height: 1px;
145
+ background: var(--neutral-300);
146
+ pointer-events: none;
147
  }
148
 
149
  /* Focus styles for accessibility */
app/src/content/chapters/available-blocks.mdx CHANGED
@@ -229,8 +229,18 @@ Accessible accordion based on `details/summary`. You can pass any children conte
229
  <p>This one stays collapsed until the user clicks the summary.</p>
230
  </Accordion>
231
 
 
 
 
 
 
 
 
 
 
 
232
  <small className="muted">Example</small>
233
- ```mdx
234
  import Accordion from '../components/Accordion.astro'
235
 
236
  <Accordion title="Accordion title" open>
@@ -243,7 +253,27 @@ import Accordion from '../components/Accordion.astro'
243
  <li>Item B</li>
244
  </ul>
245
  </Accordion>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
  ```
 
 
247
 
248
 
249
  ### Minimal table
@@ -283,22 +313,29 @@ The main purpose of the ```HtmlFragment``` component is to **embed** a **Plotly*
283
 
284
  They exist in the `app/src/content/fragments` folder.
285
 
 
 
286
  Here are some examples of the two **libraries** in the template:
287
 
288
- D3 version
289
  <div className="plot-card">
290
  <HtmlFragment src="d3-line.html" />
291
  </div>
 
292
 
293
  <div className="plot-card">
294
  <HtmlFragment src="d3-bar.html" />
295
  </div>
 
296
 
 
 
 
297
 
298
- Plotly version
299
  <div className="plot-card">
300
  <HtmlFragment src="line.html" />
301
  </div>
 
302
 
303
  <small className="muted">Example</small>
304
  ```mdx
@@ -315,8 +352,22 @@ You can embed external content in your article using **iframes**. For example, *
315
 
316
  <iframe className="plot-card" src="https://trackio-documentation.hf.space/?project=fake-training-750735&metrics=train_loss,train_accuracy&sidebar=hidden&lang=en" width="100%" height="660" frameborder="0"></iframe>
317
 
 
318
  <small className="muted">Example</small>
319
  ```mdx
320
  <iframe frameborder="0" scrolling="no" style="width:100%; height:292px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Fhuggingface%2Fpicotron%2Fblob%2F1004ae37b87887cde597c9060fb067faa060bafe%2Fsetup.py&style=default&type=code&showBorder=on&showLineNumbers=on"></iframe>
321
  <iframe src="https://trackio-documentation.hf.space/?project=fake-training-750735&metrics=train_loss,train_accuracy&sidebar=hidden&lang=en" width="100%" height="600" frameborder="0"></iframe>
322
  ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  <p>This one stays collapsed until the user clicks the summary.</p>
230
  </Accordion>
231
 
232
+ <Accordion title="Accordion with code example">
233
+ ```ts
234
+ function greet(name: string) {
235
+ console.log(`Hello, ${name}`);
236
+ }
237
+
238
+ greet("Astro");
239
+ ```
240
+ </Accordion>
241
+
242
  <small className="muted">Example</small>
243
+ ````mdx
244
  import Accordion from '../components/Accordion.astro'
245
 
246
  <Accordion title="Accordion title" open>
 
253
  <li>Item B</li>
254
  </ul>
255
  </Accordion>
256
+
257
+ <Accordion title="Accordion with code example">
258
+ ```ts
259
+ function greet(name: string) {
260
+ console.log(`Hello, ${name}`);
261
+ }
262
+
263
+ greet("Astro");
264
+ ```
265
+ </Accordion>
266
+
267
+ <Accordion title="Accordion with code example">
268
+ ```ts
269
+ function greet(name: string) {
270
+ console.log(`Hello, ${name}`);
271
+ }
272
+
273
+ greet("Astro");
274
  ```
275
+ </Accordion>
276
+ ````
277
 
278
 
279
  ### Minimal table
 
313
 
314
  They exist in the `app/src/content/fragments` folder.
315
 
316
+ Plotly and D3 are already supported by the template. For researchers who want to stay in **Python** while targeting **D3**, the [d3blocks](https://github.com/d3blocks/d3blocks) library lets you create interactive D3 charts with only a few lines of code. In **2025**, **D3** often provides more flexibility and a more web‑native rendering than **Plotly** for custom visualizations.
317
+
318
  Here are some examples of the two **libraries** in the template:
319
 
320
+ <small class="muted">D3 version</small>
321
  <div className="plot-card">
322
  <HtmlFragment src="d3-line.html" />
323
  </div>
324
+ <caption className="caption">D3 Line chart — simple time series example.</caption>
325
 
326
  <div className="plot-card">
327
  <HtmlFragment src="d3-bar.html" />
328
  </div>
329
+ <caption className="caption">D3 Bar chart — categorical distribution example.</caption>
330
 
331
+ ---
332
+
333
+ <small class="muted">Plotly version</small>
334
 
 
335
  <div className="plot-card">
336
  <HtmlFragment src="line.html" />
337
  </div>
338
+ <caption className="caption">Plotly Line chart — interactive time series with hover and zoom.</caption>
339
 
340
  <small className="muted">Example</small>
341
  ```mdx
 
352
 
353
  <iframe className="plot-card" src="https://trackio-documentation.hf.space/?project=fake-training-750735&metrics=train_loss,train_accuracy&sidebar=hidden&lang=en" width="100%" height="660" frameborder="0"></iframe>
354
 
355
+
356
  <small className="muted">Example</small>
357
  ```mdx
358
  <iframe frameborder="0" scrolling="no" style="width:100%; height:292px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Fhuggingface%2Fpicotron%2Fblob%2F1004ae37b87887cde597c9060fb067faa060bafe%2Fsetup.py&style=default&type=code&showBorder=on&showLineNumbers=on"></iframe>
359
  <iframe src="https://trackio-documentation.hf.space/?project=fake-training-750735&metrics=train_loss,train_accuracy&sidebar=hidden&lang=en" width="100%" height="600" frameborder="0"></iframe>
360
  ```
361
+
362
+ #### gradio
363
+
364
+ You can also embed **gradio** apps.
365
+
366
+ <gradio-app theme_mode="light" space="hebrew-llm-leaderboard/leaderboard"></gradio-app>
367
+
368
+
369
+
370
+ <small className="muted">Example</small>
371
+ ```mdx
372
+ <gradio-app theme_mode="light" space="hebrew-llm-leaderboard/leaderboard"></gradio-app>
373
+ ```
app/src/content/chapters/writing-your-content.mdx CHANGED
@@ -16,7 +16,7 @@ Your article lives in two places
16
  - `app/src/content/` — where you can find the `article.mdx`, `bibliography.bib` and html fragments.
17
  - `app/src/assets/` — images, audio, and other static assets. (handled by git lfs)
18
 
19
- The `article.mdx` file is a markdown file with html. It is the main file that contains your article.
20
 
21
  <small className="muted">Example</small>
22
  ```mdx
@@ -49,21 +49,38 @@ This is a short paragraph written in Markdown. Below is an example image:
49
  <Image src={placeholder} alt="Example image" />
50
  ```
51
 
52
- ### Markdown
53
 
54
- You can use **markdown** to write your content. Here is the complete [markdown documentation](https://www.markdownguide.org/basic-syntax/).
55
 
56
  <small className="muted">Example</small>
57
  ```mdx
58
- # This is the main title
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
- This is a short paragraph containing **bold** and *italic* text.
 
 
61
 
62
- ## This is a second level heading
63
 
64
- - This is a list item
65
- - This is a second list item
66
- - This is a third list item
67
  ```
68
 
69
 
 
16
  - `app/src/content/` — where you can find the `article.mdx`, `bibliography.bib` and html fragments.
17
  - `app/src/assets/` — images, audio, and other static assets. (handled by git lfs)
18
 
19
+ The `article.mdx` file is the main file that contains your article.
20
 
21
  <small className="muted">Example</small>
22
  ```mdx
 
49
  <Image src={placeholder} alt="Example image" />
50
  ```
51
 
52
+ ### MDX
53
 
54
+ MDX is a mix of Markdown and HTML/JSX: write regular Markdown, and embed interactive components inline when needed. We’ll describe the available components you can use later in this guide. For Markdown syntax, see the complete [Markdown documentation](https://www.markdownguide.org/basic-syntax/).
55
 
56
  <small className="muted">Example</small>
57
  ```mdx
58
+ {/* IMPORTS */}
59
+ import { Image } from 'astro:assets'
60
+ import placeholder from '../assets/images/placeholder.png'
61
+ import Aside from '../components/Aside.astro'
62
+
63
+ # Mixing Markdown and components
64
+
65
+ This paragraph is written in Markdown.
66
+
67
+ <Aside>A short callout inserted via a component.</Aside>
68
+
69
+ Below is an image imported via Astro and optimized at build time:
70
+
71
+ <Image src={placeholder} alt="Sample image with Astro optimization" />
72
+ ```
73
+
74
+
75
+ Use `---` on its own line to insert a horizontal separator between sections. This is a standard Markdown “thematic break”. Don’t confuse it with the `---` used at the very top of the file to delimit the frontmatter.
76
 
77
+ <small className="muted">Example</small>
78
+ ```mdx
79
+ Intro paragraph.
80
 
81
+ ---
82
 
83
+ Next section begins here.
 
 
84
  ```
85
 
86
 
app/src/content/fragments/palettes.html CHANGED
@@ -44,21 +44,93 @@
44
  const base = chroma(baseHex);
45
  const lc = base.lch();
46
  const baseH = base.get('hsl.h') || 0;
47
- const L = Math.max(40, Math.min(85, lc[0] || 70));
48
- const C = Math.max(40, Math.min(90, lc[1] || 80));
49
- const offsets = [180, 120, -120, 60, -60];
50
- const rest = offsets.map(off => chroma.lch(L, C, (baseH + off + 360) % 360).hex());
51
- return [base.hex(), ...rest];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  }},
53
  { key: 'sequential', title: 'Sequential', desc: 'For <strong>numeric scales</strong>; gradient from <strong>light to dark</strong>; ideal for <strong>heatmaps</strong>.', generator: (baseHex) => {
54
  const c = chroma(baseHex).saturate(0.3);
55
- return chroma.scale([c.brighten(2), c, c.darken(2)]).mode('lab').correctLightness(true).colors(6);
56
  }},
57
  { key: 'diverging', title: 'Diverging', desc: 'For <strong>centered ranges</strong> with <strong>two extremes</strong> (e.g., negatives/positives) around a <strong>baseline</strong>.', generator: (baseHex) => {
58
  const baseH = chroma(baseHex).get('hsl.h');
59
  const compH = (baseH + 180) % 360;
60
- const left = chroma.hsl(compH, 0.75, 0.55);
61
- const right = chroma.hsl(baseH, 0.75, 0.55);
62
  const center = '#ffffff';
63
  const leftRamp = chroma.scale([left, center]).mode('lch').correctLightness(true).colors(4);
64
  const rightRamp = chroma.scale([center, right]).mode('lch').correctLightness(true).colors(4);
 
44
  const base = chroma(baseHex);
45
  const lc = base.lch();
46
  const baseH = base.get('hsl.h') || 0;
47
+ const L0 = Math.max(40, Math.min(85, lc[0] || 70));
48
+ const C0 = Math.max(45, Math.min(75, lc[1] || 70));
49
+ const MIN_DELTA = 18; // distance minimale en Lab
50
+
51
+ const seen = new Set();
52
+ const results = [];
53
+
54
+ const makeSafe = (h, L, C) => {
55
+ let c = C;
56
+ let col = chroma.lch(L, c, h);
57
+ let guard = 0;
58
+ while (col.clipped && typeof col.clipped === 'function' && col.clipped() && c > 30 && guard < 8) {
59
+ c -= 5;
60
+ col = chroma.lch(L, c, h);
61
+ guard++;
62
+ }
63
+ return col;
64
+ };
65
+
66
+ const isFarEnough = (hex) => results.every(prev => chroma.distance(hex, prev, 'lab') >= MIN_DELTA);
67
+ const pushHex = (col) => {
68
+ const hex = col.hex();
69
+ if (!seen.has(hex.toLowerCase())) { results.push(hex); seen.add(hex.toLowerCase()); }
70
+ };
71
+
72
+ // Base en premier
73
+ pushHex(base);
74
+
75
+ // Cinq couleurs supplémentaires, espacées de 60°
76
+ const angles = [60, 120, 180, 240, 300];
77
+ const hueOffsets = [0, 20, -20, 40, -40, 60, -60, 80, -80];
78
+ const lVariants = [L0, Math.max(40, L0 - 6), Math.min(85, L0 + 6)];
79
+
80
+ angles.forEach(step => {
81
+ let accepted = false;
82
+ for (let li = 0; li < lVariants.length && !accepted; li++) {
83
+ for (let oi = 0; oi < hueOffsets.length && !accepted; oi++) {
84
+ let h = (baseH + step + hueOffsets[oi] + 360) % 360;
85
+ let col = makeSafe(h, lVariants[li], C0);
86
+ const hex = col.hex();
87
+ if (!seen.has(hex.toLowerCase()) && isFarEnough(hex)) {
88
+ pushHex(col);
89
+ accepted = true;
90
+ }
91
+ }
92
+ }
93
+ if (!accepted) {
94
+ // Réduction de C si nécessaire
95
+ let cTry = C0 - 10;
96
+ let h = (baseH + step + 360) % 360;
97
+ let trials = 0;
98
+ while (!accepted && cTry >= 30 && trials < 6) {
99
+ const col = makeSafe(h, L0, cTry);
100
+ const hex = col.hex();
101
+ if (!seen.has(hex.toLowerCase()) && isFarEnough(hex)) {
102
+ pushHex(col);
103
+ accepted = true;
104
+ break;
105
+ }
106
+ cTry -= 5;
107
+ trials++;
108
+ }
109
+ // Dernier recours: choisir la teinte la plus éloignée possible même si < seuil
110
+ if (!accepted) {
111
+ let bestHex = null; let bestMin = -1;
112
+ hueOffsets.forEach(off => {
113
+ const hh = (baseH + step + off + 360) % 360;
114
+ const cand = makeSafe(hh, L0, C0).hex();
115
+ const minD = results.reduce((m, prev) => Math.min(m, chroma.distance(cand, prev, 'lab')), Infinity);
116
+ if (minD > bestMin && !seen.has(cand.toLowerCase())) { bestMin = minD; bestHex = cand; }
117
+ });
118
+ if (bestHex) { seen.add(bestHex.toLowerCase()); results.push(bestHex); }
119
+ }
120
+ }
121
+ });
122
+
123
+ return results.slice(0, 6);
124
  }},
125
  { key: 'sequential', title: 'Sequential', desc: 'For <strong>numeric scales</strong>; gradient from <strong>light to dark</strong>; ideal for <strong>heatmaps</strong>.', generator: (baseHex) => {
126
  const c = chroma(baseHex).saturate(0.3);
127
+ return chroma.scale([c.darken(2), c, c.brighten(2)]).mode('lab').correctLightness(true).colors(6);
128
  }},
129
  { key: 'diverging', title: 'Diverging', desc: 'For <strong>centered ranges</strong> with <strong>two extremes</strong> (e.g., negatives/positives) around a <strong>baseline</strong>.', generator: (baseHex) => {
130
  const baseH = chroma(baseHex).get('hsl.h');
131
  const compH = (baseH + 180) % 360;
132
+ const left = chroma.hsl(baseH, 0.75, 0.55);
133
+ const right = chroma.hsl(compH, 0.75, 0.55);
134
  const center = '#ffffff';
135
  const leftRamp = chroma.scale([left, center]).mode('lch').correctLightness(true).colors(4);
136
  const rightRamp = chroma.scale([center, right]).mode('lch').correctLightness(true).colors(4);
app/src/pages/index.astro CHANGED
@@ -66,9 +66,10 @@ const bibtex = `@misc{${bibKey},\n title={${titleFlat}},\n author={${authorsBi
66
  })();
67
  </script>
68
 
69
-
70
  <script src="https://cdn.plot.ly/plotly-3.0.0.min.js" charset="utf-8"></script>
71
  <script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
 
72
  </head>
73
  <body>
74
  <ThemeToggle />
 
66
  })();
67
  </script>
68
 
69
+ <!-- TO MANAGE PROPERLY -->
70
  <script src="https://cdn.plot.ly/plotly-3.0.0.min.js" charset="utf-8"></script>
71
  <script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
72
+ <script type="module" src="https://gradio.s3-us-west-2.amazonaws.com/4.4.0/gradio.js"> </script>
73
  </head>
74
  <body>
75
  <ThemeToggle />
app/src/styles/components/_code.css CHANGED
@@ -63,3 +63,6 @@ html[data-theme='light'] .astro-code {
63
  }
64
 
65
 
 
 
 
 
63
  }
64
 
65
 
66
+
67
+ /* Overrides inside Accordion: remove padding and border on code containers */
68
+ .accordion .astro-code { padding: 0; border: none; }