Skip to content

Commit 952f770

Browse files
committed
fix(terracotta): opaque navbar + theme-aware Mermaid diagrams
- Navbar: always bone-paper background + subtle shadow on scroll - MermaidDiagram: read fezcodexTheme and pick palette / font / container - Presets for terracotta (bone + terra), luxe (off-white), brutalist (legacy dark) - Explicit theme / className props still override presets
1 parent e2113ff commit 952f770

2 files changed

Lines changed: 128 additions & 19 deletions

File tree

src/components/MermaidDiagram.jsx

Lines changed: 124 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,148 @@
1-
import React, { useEffect, useState } from 'react';
1+
import React, { useEffect, useRef, useState } from 'react';
22
import mermaid from 'mermaid';
3+
import { useVisualSettings } from '../context/VisualSettingsContext';
34

4-
const MermaidDiagram = ({ chart, theme = 'dark', className }) => {
5+
/*
6+
* Theme-aware mermaid renderer.
7+
*
8+
* Reads fezcodexTheme from VisualSettingsContext (falling back safely if the
9+
* provider isn't in the tree) and picks a palette + container style that fits:
10+
*
11+
* terracotta — bone paper, terra ink, Fraunces-friendly warm ink text
12+
* luxe — off-white card, charcoal line, muted accent
13+
* brutalist — dark card, emerald accent (default legacy look)
14+
*
15+
* Callers can still override theme / className explicitly.
16+
*/
17+
18+
const THEME_PRESETS = {
19+
terracotta: {
20+
mermaidTheme: 'base',
21+
themeVariables: {
22+
background: '#F3ECE0',
23+
primaryColor: '#E8DECE',
24+
primaryBorderColor: '#1A1613',
25+
primaryTextColor: '#1A1613',
26+
secondaryColor: '#F3ECE0',
27+
secondaryBorderColor: '#1A161360',
28+
secondaryTextColor: '#1A1613',
29+
tertiaryColor: '#C96442',
30+
tertiaryBorderColor: '#9E4A2F',
31+
tertiaryTextColor: '#F3ECE0',
32+
lineColor: '#1A1613',
33+
textColor: '#1A1613',
34+
mainBkg: '#E8DECE',
35+
nodeBorder: '#1A1613',
36+
clusterBkg: '#F3ECE0',
37+
clusterBorder: '#1A161340',
38+
edgeLabelBackground: '#F3ECE0',
39+
titleColor: '#1A1613',
40+
noteBkgColor: '#E8DECE',
41+
noteTextColor: '#1A1613',
42+
noteBorderColor: '#C96442',
43+
fontSize: '15px',
44+
},
45+
fontFamily: "'IBM Plex Mono', 'JetBrains Mono', monospace",
46+
container:
47+
'mermaid-container my-8 border border-[#1A161330] bg-[#F3ECE0] p-6 overflow-x-auto relative',
48+
labelClass: 'Diagram · figure',
49+
labelColor: '#C96442',
50+
},
51+
luxe: {
52+
mermaidTheme: 'base',
53+
themeVariables: {
54+
background: '#FAFAF8',
55+
primaryColor: '#FFFFFF',
56+
primaryBorderColor: '#1A1A1A',
57+
primaryTextColor: '#1A1A1A',
58+
secondaryColor: '#FAFAF8',
59+
secondaryBorderColor: '#1A1A1A40',
60+
secondaryTextColor: '#1A1A1A',
61+
tertiaryColor: '#8D4004',
62+
tertiaryBorderColor: '#8D4004',
63+
tertiaryTextColor: '#FFFFFF',
64+
lineColor: '#1A1A1A',
65+
textColor: '#1A1A1A',
66+
mainBkg: '#FFFFFF',
67+
nodeBorder: '#1A1A1A',
68+
clusterBkg: '#FAFAF8',
69+
clusterBorder: '#1A1A1A20',
70+
edgeLabelBackground: '#FAFAF8',
71+
titleColor: '#1A1A1A',
72+
noteBkgColor: '#FAFAF8',
73+
noteTextColor: '#1A1A1A',
74+
noteBorderColor: '#8D4004',
75+
fontSize: '15px',
76+
},
77+
fontFamily: "'Outfit', 'Inter', system-ui, sans-serif",
78+
container:
79+
'mermaid-container my-8 border border-[#1A1A1A10] bg-[#FAFAF8] p-6 rounded-sm shadow-sm overflow-x-auto',
80+
labelClass: null,
81+
labelColor: null,
82+
},
83+
brutalist: {
84+
mermaidTheme: 'dark',
85+
themeVariables: {
86+
fontSize: '16px',
87+
},
88+
fontFamily: "'JetBrains Mono', monospace",
89+
container:
90+
'mermaid-container my-8 bg-gray-900/30 p-6 rounded-lg overflow-x-auto',
91+
labelClass: null,
92+
labelColor: null,
93+
},
94+
};
95+
96+
const MermaidDiagram = ({ chart, theme, className }) => {
597
const [svg, setSvg] = useState('');
698
const [error, setError] = useState(null);
99+
const containerRef = useRef(null);
100+
101+
const visualSettings = useVisualSettings();
102+
const activeThemeKey = (() => {
103+
if (theme === 'dark' || theme === 'default' || theme === 'base' || theme === 'neutral' || theme === 'forest') {
104+
return null;
105+
}
106+
return visualSettings?.fezcodexTheme || 'brutalist';
107+
})();
108+
const preset =
109+
(activeThemeKey && THEME_PRESETS[activeThemeKey]) || THEME_PRESETS.brutalist;
110+
111+
const mermaidTheme = theme || preset.mermaidTheme;
7112

8113
useEffect(() => {
9114
mermaid.initialize({
10115
startOnLoad: false,
11-
theme: theme,
116+
theme: mermaidTheme,
12117
securityLevel: 'loose',
13-
fontFamily: "'JetBrains Mono', monospace",
118+
fontFamily: preset.fontFamily,
14119
useMaxWidth: false,
15120
htmlLabels: true,
16121
flowchart: {
17122
padding: 15,
18123
useMaxWidth: false,
19124
htmlLabels: true,
20125
},
21-
themeVariables: {
22-
fontSize: '16px',
23-
},
126+
themeVariables: preset.themeVariables,
24127
});
25-
}, [theme]);
128+
}, [mermaidTheme, preset]);
26129

27130
useEffect(() => {
28131
const renderChart = async () => {
29132
if (!chart) return;
30133
try {
31134
const id = `mermaid-${Math.random().toString(36).substr(2, 9)}`;
32-
// mermaid.render returns an object { svg } in newer versions
33-
const { svg } = await mermaid.render(id, chart);
34-
setSvg(svg);
135+
const { svg: rendered } = await mermaid.render(id, chart);
136+
setSvg(rendered);
35137
setError(null);
36138
} catch (err) {
37139
console.error('Mermaid render error:', err);
38-
// Mermaid might leave error text in the DOM, so we can also show a friendly message
39140
setError('Failed to render diagram. Check syntax.');
40141
}
41142
};
42143

43144
renderChart();
44-
}, [chart]);
145+
}, [chart, mermaidTheme, preset]);
45146

46147
if (error) {
47148
return (
@@ -52,10 +153,18 @@ const MermaidDiagram = ({ chart, theme = 'dark', className }) => {
52153
);
53154
}
54155

55-
const containerClass = className || "mermaid-container my-8 bg-gray-900/30 p-6 rounded-lg overflow-x-auto";
156+
const containerClass = className || preset.container;
56157

57158
return (
58-
<div className={containerClass}>
159+
<div ref={containerRef} className={containerClass}>
160+
{preset.labelClass && (
161+
<div
162+
className="absolute top-0 left-4 -translate-y-1/2 bg-[#F3ECE0] px-2 font-ibm-plex-mono text-[9px] tracking-[0.3em] uppercase"
163+
style={{ color: preset.labelColor }}
164+
>
165+
{preset.labelClass}
166+
</div>
167+
)}
59168
<style>
60169
{`
61170
.mermaid-container svg {

src/components/TerracottaNavbar.jsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ const TerracottaNavbar = ({
2828

2929
return (
3030
<header
31-
className={`sticky top-0 z-40 transition-all duration-300 ${
31+
className={`sticky top-0 z-40 bg-[#F3ECE0]/95 backdrop-blur-md border-b transition-shadow duration-300 ${
3232
scrolled
33-
? 'bg-[#F3ECE0]/92 backdrop-blur-md border-[#1A161320]'
34-
: 'bg-transparent border-transparent'
35-
} border-b`}
33+
? 'border-[#1A161320] shadow-[0_4px_14px_-10px_rgba(26,22,19,0.25)]'
34+
: 'border-[#1A161310] shadow-none'
35+
}`}
3636
>
3737
<div className="mx-auto max-w-[1800px] px-5 md:px-12">
3838
<div className="grid grid-cols-[auto_1fr_auto] items-center gap-6 py-3.5">

0 commit comments

Comments
 (0)