diff --git a/README.md b/README.md index 02ed593..59b9524 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,16 @@ Three core modules form the **ingest → drive → emit** spine: `source` brings events in, `state` decides what happens, and `sink` fans the resulting effects out. Each is a thin seam you can adopt on its own, and none imports another. + ```mermaid +%%{init: {'theme':'base','themeVariables':{'background':'transparent','primaryColor':'#8a929c','primaryBorderColor':'#d9620a','primaryTextColor':'#16191d','lineColor':'#b06a28','defaultLinkColor':'#b06a28','titleColor':'#b5500c','mainBkg':'#8a929c','nodeBorder':'#d9620a','nodeTextColor':'#16191d','labelColor':'#16191d','edgeLabelBackground':'#c0c7cf','altBackground':'#aab1ba','clusterBkg':'#aab1ba','clusterBorder':'#d9620a'}}}%% flowchart LR streams[(external streams)] -->|source| engine[state engine] engine -->|sink| destinations[(destinations)] @@ -59,6 +68,7 @@ folds an event into a new instance and emits effects as plain data, leaving persistence and dispatch to the host. ```mermaid +%%{init: {'theme':'base','themeVariables':{'background':'transparent','primaryColor':'#8a929c','primaryBorderColor':'#d9620a','primaryTextColor':'#16191d','lineColor':'#b06a28','defaultLinkColor':'#b06a28','titleColor':'#b5500c','mainBkg':'#8a929c','nodeBorder':'#d9620a','nodeTextColor':'#16191d','labelColor':'#16191d','edgeLabelBackground':'#c0c7cf','altBackground':'#aab1ba','clusterBkg':'#aab1ba','clusterBorder':'#d9620a'}}}%% stateDiagram-v2 [*] --> Idle Idle --> Working: Start [guard] @@ -74,6 +84,7 @@ Consumes external streams (Kafka, JetStream, Redis, CDC, and more) and drives a machine, with the ack tied to a durable transition so redelivery is safe. ```mermaid +%%{init: {'theme':'base','themeVariables':{'background':'transparent','primaryColor':'#8a929c','primaryBorderColor':'#d9620a','primaryTextColor':'#16191d','lineColor':'#b06a28','defaultLinkColor':'#b06a28','titleColor':'#b5500c','mainBkg':'#8a929c','nodeBorder':'#d9620a','nodeTextColor':'#16191d','labelColor':'#16191d','edgeLabelBackground':'#c0c7cf','altBackground':'#aab1ba','clusterBkg':'#aab1ba','clusterBorder':'#d9620a'}}}%% flowchart LR stream[(stream)] --> decode[decode / codec] --> route["route to (key, event)"] --> fire["Fire on instance"] --> commit[durable commit] --> ack[ack] ``` @@ -84,6 +95,7 @@ Fans emitted effects out to many destinations through a `Manifold`, fire-and-forget; one outlet's failure never stops the rest. ```mermaid +%%{init: {'theme':'base','themeVariables':{'background':'transparent','primaryColor':'#8a929c','primaryBorderColor':'#d9620a','primaryTextColor':'#16191d','lineColor':'#b06a28','defaultLinkColor':'#b06a28','titleColor':'#b5500c','mainBkg':'#8a929c','nodeBorder':'#d9620a','nodeTextColor':'#16191d','labelColor':'#16191d','edgeLabelBackground':'#c0c7cf','altBackground':'#aab1ba','clusterBkg':'#aab1ba','clusterBorder':'#d9620a'}}}%% flowchart LR effect[emitted effect] --> manifold[Manifold] manifold --> a[destination A] diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index 79fa6ac..12f8bb6 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -2,6 +2,7 @@ import { defineConfig } from 'astro/config'; import starlight from '@astrojs/starlight'; import mermaid from 'astro-mermaid'; +import { crucibleMermaidThemeVariables } from './src/mermaid-theme.mjs'; // GitHub project page: https://stablekernel.github.io/crucible // `site` + `base` must match the Pages URL so generated links and assets resolve. @@ -13,11 +14,27 @@ export default defineConfig({ // Chosen over rehype-mermaid because it needs no headless browser (Playwright) // at build time, keeping the CI `npm run build` step fast and dependency-light. // It registers a remark plugin that transforms mermaid code blocks into a - // hydrated
 element. `theme: 'dark'` matches the
-    // dark-default Crucible brand; mermaid auto-syncs when the user toggles theme.
+    // hydrated 
 element.
+    //
+    // Theming is centralized in ./src/mermaid-theme.mjs and applied to every
+    // diagram on the site (all stateDiagram-v2 + flowchart blocks, and the
+    // diagrams generated by tools/docsgen). We build on mermaid's `base` theme
+    // because it is the only theme that honors `themeVariables`, and pass the
+    // crucible brand palette through `mermaidConfig` (astro-mermaid forwards it
+    // verbatim to mermaid.initialize()).
+    //
+    // autoTheme is OFF deliberately. When on, the integration swaps in stock
+    // 'dark'/'default' mermaid themes per page mode, which discards most of our
+    // base themeVariables on toggle. astro-mermaid exposes a single mermaidConfig
+    // (no per-mode themeVariables), so to keep one source of truth we render the
+    // dark brand palette in both page modes. Dark charcoal nodes with white text
+    // stay legible on a light page the same way a fenced code block does.
     mermaid({
-      theme: 'dark',
-      autoTheme: true,
+      theme: 'base',
+      autoTheme: false,
+      mermaidConfig: {
+        themeVariables: crucibleMermaidThemeVariables,
+      },
     }),
     starlight({
       title: 'Crucible',
diff --git a/docs/src/mermaid-theme.mjs b/docs/src/mermaid-theme.mjs
new file mode 100644
index 0000000..18b8bdc
--- /dev/null
+++ b/docs/src/mermaid-theme.mjs
@@ -0,0 +1,65 @@
+// Crucible "foundry" mermaid theme — the single source of truth for diagram
+// colors across the docs site (every stateDiagram-v2 + flowchart, plus the
+// diagrams generated by tools/docsgen → ToMermaid).
+//
+// These hexes mirror the brand tokens in src/styles/crucible.css and the
+// %%{init}%% directives in the repo-root README.md. Keep all three in sync.
+//
+// Identity: molten ember/copper accents over a deep steel/charcoal base.
+// The brand is dark-default, so we build on mermaid's `base` theme — the only
+// theme that honors `themeVariables` (see https://mermaid.js.org/config/theming.html)
+// — with `darkMode: true` so derived shades resolve for a dark canvas. Every
+// variable name below is a documented mermaid theme variable; nothing invented.
+export const crucibleMermaidThemeVariables = {
+  darkMode: true,
+  background: '#16191d', // deep steel base canvas
+
+  // Primary nodes: charcoal surface, molten-ember border, white label.
+  primaryColor: '#23272c',
+  primaryBorderColor: '#d9620a', // ember-orange
+  primaryTextColor: '#f4f6f8',
+
+  // Edges / links: copper, with ember-glow reserved for emphasis (titles).
+  lineColor: '#c47a3d', // copper
+  defaultLinkColor: '#c47a3d',
+
+  // Secondary nodes: lighter charcoal panel, copper border.
+  secondaryColor: '#353b42',
+  secondaryBorderColor: '#c47a3d',
+  secondaryTextColor: '#f4f6f8',
+
+  // Tertiary / deep-composite + subgraph fills: nav-dark with a steel hairline.
+  tertiaryColor: '#1b1f24',
+  tertiaryBorderColor: '#2b3036',
+  tertiaryTextColor: '#e3e7ec',
+
+  // Flowchart specifics.
+  mainBkg: '#23272c',
+  nodeBorder: '#d9620a',
+  nodeTextColor: '#f4f6f8',
+  clusterBkg: '#1b1f24', // subgraph background
+  clusterBorder: '#c47a3d',
+  titleColor: '#f6a85b', // ember-glow emphasis on subgraph/diagram titles
+  edgeLabelBackground: '#23272c',
+
+  // stateDiagram-v2 specifics.
+  labelColor: '#f4f6f8',
+  altBackground: '#353b42', // deep composite-state background
+
+  // General text + notes.
+  textColor: '#f4f6f8',
+  noteBkgColor: '#353b42',
+  noteTextColor: '#f4f6f8',
+};
+
+// astro-mermaid passes `mermaidConfig` straight through to mermaid.initialize().
+// We pin `theme: 'base'` here too because base is the only themeVariables-aware
+// theme; the integration spreads this config and (when autoTheme is on) would
+// otherwise swap in stock 'dark'/'default'. We keep autoTheme OFF in the astro
+// config so the dark brand palette renders consistently in both page modes —
+// dark charcoal nodes with white text stay legible on a light page background,
+// the same way a fenced code block does.
+export const crucibleMermaidConfig = {
+  theme: 'base',
+  themeVariables: crucibleMermaidThemeVariables,
+};