Cette page illustre le render hook layouts/_markup/render-codeblock.html : chaque bloc de code fenced Markdown peut afficher un en-tête avec un titre ou un nom de fichier, l’icône de son langage et un bouton « copier le code ». Les options se passent dans l’info string du bloc, sans shortcode.
Titre / nom de fichier
L’attribut title="..." affiche un en-tête avec un titre ou un nom de fichier :
<p>Hello World</p>Icône du langage (mapping auto)
L’attribut icon=true affiche l’icône du langage du bloc, résolue par le partial layouts/_partials/func/GetCodeIcon.html (slugs Simple Icons, alias inclus) :
console.log('Hello');Bouton copier
L’attribut copy=true ajoute un bouton « copier le code », avec un retour visuel de 2 s à la copie :
echo "Hello World"Tout combiné
body {
color: rebeccapurple;
}Sans option (rendu Hugo par défaut)
Un bloc sans option passe par transform.HighlightCodeBlock : rendu strictement identique à celui de Hugo, aucune régression.
<p>Hello World</p>Icône explicite (Simple Icons)
L’attribut icon="..." force une icône donnée (slug Simple Icons) — utile pour un langage non mappé ou pour le texte brut :
content/
layouts/Langage non mappé (pas d’icône)
Un langage absent du mapping n’affiche pas d’icône (zig ici) :
const std = @import("std");Opt-out explicite
copy=false (resp. icon=false) désactive l’option même si elle est activée globalement dans la configuration du site :
console.log('pas de bouton copier');Récapitulatif
| Syntaxe | Effet |
|---|---|
{title="index.html"} | En-tête avec titre / nom de fichier |
{icon=true} | Icône du langage (mapping auto) |
{icon="python"} | Icône explicite (slug Simple Icons) |
{icon=false} | Désactive l’icône |
{copy=true} | Bouton « copier le code » |
{copy=false} | Désactive le bouton copier |
Notes
- Défauts globaux :
params.code.copyetparams.code.icon(config/_default/params.yaml) activent les options pour tout le site ; l’info string du bloc gagne toujours. - Syntaxe : un attribut sans valeur (
{icon},{copy}) est invalide pour le parseur d’attributs Markdown (Goldmark) — toujours écrireicon=true/copy=true. - Langages non reconnus par Chroma (drupal, flutter, mongodb…) : rendus en texte brut, mais l’icône du mapping s’affiche ; une icône explicite (
{icon="drupal"}) reste la solution la plus propre. - Accessibilité : vrai
<button>avecaria-labelbasculé vers « Code copié » pendant 2 s, fallbackexecCommandpour les contextes non sécurisés. - Icônes du bouton : Lucide
copy/check, émises via le partialicondu modulehugolify-theme-icons; sans ce module, le bouton reste fonctionnel avec son libellé texte.
Icônes — un exemple par langage du mapping
Un bloc pour chaque langage mappé dans layouts/_partials/func/GetCodeIcon.html, dans l’ordre du mapping, alias inclus (js et javascript partagent la même icône). Les langages non reconnus par Chroma sont rendus en texte brut — l’icône du mapping s’affiche quand même.
<button [disabled]="isLoading">Valider</button>---
layout: ../layouts/Base.astro
---
<h1>Bonjour</h1>echo "Hello World"<button class="btn btn-primary">Valider</button>.title { color: rebeccapurple; }void main() { print('Bonjour'); }FROM alpine:3
RUN apk add curlFROM node:20-alpine
COPY . .
RUN npm cifunction demo_form_alter(&$form, $form_state) {
$form['#validate'][] = 'demo_validate';
}def hello, do: IO.puts("Bonjour")main =
text "Bonjour"hello() ->
io:format("Bonjour~n").const Text('Bonjour')git commit -m "feat: blocs de code"image: node:20
script:
- npm testfmt.Println("Bonjour")func main() { fmt.Println("Bonjour") }query {
user(id: 1) {
name
}
}type Query {
hello: String
}main = putStrLn "Bonjour"<p>Bonjour</p><p>Bonjour le monde</p>{{ range .Pages }}{{ .Title }}{{ end }}System.out.println("Bonjour");console.log("Bonjour");const somme = (a, b) => a + b;<Bouton label="Bonjour" onClick={valider} /><Bouton label="Bonjour" onClick={valider} />apiVersion: v1
kind: Podfun main() = println("Bonjour")kubectl get pods -n prodRoute::get('/bonjour', fn () => 'Bonjour');@primaire: rebeccapurple;
.title { color: @primaire; }print("Bonjour")**Bonjour le monde**## Bonjour
Un titre de section.db.utilisateurs.find({ actif: true })SELECT * FROM utilisateurs WHERE actif = 1;export default function Page() {
return <h1>Bonjour</h1>;
}export const metadata = { title: "Bonjour" };location / {
proxy_pass http://app:3000;
}print "Bonjour\n";<?php echo "Bonjour";SELECT count(*) FROM utilisateurs;SELECT NOW();Get-ChildItem -Recurse -Filter *.logWrite-Host "Bonjour"print("Bonjour")print("Bonjour le monde")x <- c(1, 2, 3)
mean(x)puts "Bonjour"<button onClick={valider}>Valider</button>SET salut "Bonjour"println!("Bonjour");fn main() {
println!("Bonjour le monde");
}$primaire: rebeccapurple.title {
color: $primaire;
}ls -laecho "Bonjour"contract Bonjour {
string public salut = "Bonjour";
}SELECT * FROM utilisateurs LIMIT 5;<svg xmlns="http://www.w3.org/2000/svg"><circle r="10" /></svg><h1>{titre}</h1>print("Bonjour")#[Route('/bonjour', name: 'bonjour')]
public function bonjour(): Response {}<div class="flex items-center gap-2">Bonjour</div>@tailwind utilities;resource "aws_instance" "web" {
ami = "ami-123456"
}resource "aws_s3_bucket" "cdn" {
bucket = "cdn"
}const id: number = 1;function direBonjour(nom: string): string {
return "Bonjour " + nom;
}import { defineConfig } from "vite";
export default defineConfig({});<template>
<p>{{ msg }}</p>
</template>add_action("init", "mon_init");<utilisateur>
<nom>Thomas</nom>
</utilisateur>echo $SHELL