diff --git a/v8/shortcode_mermaid/README.md b/v8/shortcode_mermaid/README.md
new file mode 100644
index 00000000..0959f24b
--- /dev/null
+++ b/v8/shortcode_mermaid/README.md
@@ -0,0 +1,89 @@
+# mermaid (Nikola plugin)
+
+Render Mermaid diagrams from Nikola shortcode blocks.
+
+This plugin provides a `mermaid` shortcode that wraps diagram source code in a
+`
` block and, by default, injects a guarded Mermaid.js
+loader next to the diagram. It works without editing theme templates.
+
+
+
+## Install
+
+Copy the plugin folder into your Nikola site:
+
+```text
+your_site/
+ plugins/
+ mermaid/
+ mermaid.py
+ mermaid.plugin
+ README.md
+ conf.py.sample
+ requirements-nonpy.txt
+```
+
+## Configuration
+
+No Nikola configuration is required for the default behavior.
+
+Optional configuration:
+
+```python
+MERMAID_CONFIG = {
+ "auto_load": True,
+ "cdn_url": "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js",
+ "initialize": {
+ "startOnLoad": False,
+ },
+}
+```
+
+Set `auto_load` to `False` if your theme already loads and initializes
+Mermaid.js. When `auto_load` is enabled, the loader is protected by global
+browser flags so multiple diagrams do not inject multiple Mermaid.js scripts.
+
+## Usage
+
+Use the shortcode in a post or page:
+
+```markdown
+{{% mermaid %}}
+mindmap
+ root((Data Scientist - ML))
+ Analyse de donnees
+ MLOps
+ NLP et RAG
+{{% /mermaid %}}
+```
+
+You can pass a theme name through the shortcode:
+
+```markdown
+{{% mermaid theme="dark" %}}
+graph TD
+ A[Data] --> B[Model]
+ B --> C[API]
+{{% /mermaid %}}
+```
+
+The selected theme is exposed as a `data-theme` attribute:
+
+```html
+
+...
+
+```
+
+## Online example
+
+- The following website use the Mermaid shortcode plugin : [stephmnt/datascience_portfolio](stephmnt.github.io/datascience_portfolio)
+
+## Notes
+
+- This plugin does not bundle Mermaid.js; it loads Mermaid.js from the configured
+ CDN URL when `auto_load` is enabled.
+- `requirements-nonpy.txt` declares Mermaid.js as a non-Python runtime dependency
+ for the Nikola plugin index.
+- If diagrams do not render, check the browser console, CDN access, and that the
+ shortcode is closed with `{{% /mermaid %}}`.
diff --git a/v8/shortcode_mermaid/conf.py.sample b/v8/shortcode_mermaid/conf.py.sample
new file mode 100644
index 00000000..6f1d8675
--- /dev/null
+++ b/v8/shortcode_mermaid/conf.py.sample
@@ -0,0 +1,16 @@
+# mermaid plugin sample configuration
+
+MERMAID_CONFIG = {
+ # Load Mermaid.js automatically from the shortcode output.
+ # Set to False if your theme already loads and initializes Mermaid.
+ "auto_load": True,
+
+ # Mermaid.js URL used when auto_load is True.
+ "cdn_url": "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js",
+
+ # Passed to mermaid.initialize().
+ # The plugin renders diagrams explicitly, so startOnLoad should usually stay False.
+ "initialize": {
+ "startOnLoad": False,
+ },
+}
diff --git a/v8/shortcode_mermaid/mermaid.plugin b/v8/shortcode_mermaid/mermaid.plugin
new file mode 100644
index 00000000..f144d8a5
--- /dev/null
+++ b/v8/shortcode_mermaid/mermaid.plugin
@@ -0,0 +1,13 @@
+[Core]
+Name = mermaid
+Module = mermaid
+
+[Documentation]
+Author = Stéphane Manet
+Version = 0.1.0
+Website = https://plugins.getnikola.com/#mermaid
+Description = Render Mermaid diagrams from Nikola shortcode blocks with an automatic Mermaid.js loader.
+
+[Nikola]
+PluginCategory = ShortcodePlugin
+MinVersion = 8.0.0
diff --git a/v8/shortcode_mermaid/mermaid.py b/v8/shortcode_mermaid/mermaid.py
new file mode 100644
index 00000000..e584647d
--- /dev/null
+++ b/v8/shortcode_mermaid/mermaid.py
@@ -0,0 +1,116 @@
+# -*- coding: utf-8 -*-
+#
+# MIT License
+#
+# Copyright (c) 2026 Stephane Manet
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import html
+import json
+
+from nikola.plugin_categories import ShortcodePlugin # type: ignore
+
+
+DEFAULT_CDN_URL = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"
+DEFAULT_INITIALIZE = {"startOnLoad": False}
+
+
+class MermaidShortcode(ShortcodePlugin):
+ name = "mermaid"
+
+ def handler(self, site=None, data=None, lang=None, **kwargs):
+ theme = html.escape(str(kwargs.get("theme", "default")), quote=True)
+ content = (data or "").strip()
+ settings = _settings(site)
+
+ diagram = (
+ f'
\n'
+ f'{content}\n'
+ '
'
+ )
+
+ if not settings["auto_load"]:
+ return diagram
+
+ return diagram + "\n" + _loader_script(settings)
+
+
+def _settings(site):
+ config = {}
+ if site is not None:
+ config = site.config.get("MERMAID_CONFIG") or {}
+
+ initialize = config.get("initialize", DEFAULT_INITIALIZE)
+ if not isinstance(initialize, dict):
+ initialize = DEFAULT_INITIALIZE
+
+ initialize = dict(initialize)
+ initialize.setdefault("startOnLoad", False)
+
+ return {
+ "auto_load": bool(config.get("auto_load", True)),
+ "cdn_url": str(config.get("cdn_url", DEFAULT_CDN_URL)),
+ "initialize": initialize,
+ }
+
+
+def _loader_script(settings):
+ cdn_url = _to_script_json(settings["cdn_url"])
+ initialize = _to_script_json(settings["initialize"], sort_keys=True)
+
+ return (
+ ''
+ )
+
+
+def _to_script_json(value, **kwargs):
+ return json.dumps(value, **kwargs).replace("", "<\\/")
diff --git a/v8/shortcode_mermaid/shortcode_mermaid.png b/v8/shortcode_mermaid/shortcode_mermaid.png
new file mode 100644
index 00000000..252c2950
Binary files /dev/null and b/v8/shortcode_mermaid/shortcode_mermaid.png differ