Skip to content

Commit 339c2c3

Browse files
committed
fix(markdown): editorial restyle of mermaid, code blocks, blockquotes, lists
1 parent 665ca75 commit 339c2c3

1 file changed

Lines changed: 68 additions & 73 deletions

File tree

front/src/components/features/blog/MarkdownRenderer/MarkdownRenderer.jsx

Lines changed: 68 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,11 @@ const expandMermaidLabelBounds = (svg) => {
107107
// per theme change, even when many diagrams render simultaneously.
108108
let mermaidConfigKey = null;
109109

110-
// Mermaid configuration helper.
111-
// Color palette is built on the site's blue-slate identity:
112-
// primary #2563eb / secondary #4338ca / dark bg #0f172a
113-
// Dark mode → dark navy node fills + medium-blue borders + blue arrows.
114-
// Light mode → very-light-blue node fills + soft-blue borders + brand-blue arrows.
115-
// Both modes stay monochromatic (no rainbow), just have more contrast & depth.
110+
// Mermaid configuration helper — Brand v2 palette.
111+
// Accent: cyan-700 (#0E7490) light / cyan-400 (#22D3EE) dark
112+
// Dark canvas: brand-bg (#0B1120)
113+
// Light canvas: white
114+
// Mermaid stays monochrome-ish (neutral nodes, cyan accents for arrows + emphasis).
116115
const getMermaidConfig = (isDark) => ({
117116
startOnLoad: false,
118117
theme: isDark ? 'dark' : 'default',
@@ -124,62 +123,61 @@ const getMermaidConfig = (isDark) => ({
124123
padding: 15,
125124
},
126125
themeVariables: isDark ? {
127-
// ── Dark mode: dark navy base, medium-blue accents ──
128-
background: '#0f172a', // page bg (slate-900)
129-
mainBkg: '#162033', // node fill — dark navy, not flat gray
130-
nodeBorder: '#2e4f85', // node border — visible blue, not gray
131-
primaryColor: '#1a3260', // primary-class nodes — deep blue
132-
primaryBorderColor: '#3668b8', // primary node border — more saturated
133-
primaryTextColor: '#e2e8f0', // text on primary nodes (slate-200)
134-
secondaryColor: '#101a2e', // secondary elements — darker navy
135-
tertiaryColor: '#1a2c45', // tertiary — mid navy
136-
lineColor: '#4a7fc1', // arrows — clear medium blue (much more readable)
137-
clusterBkg: '#0c1422', // subgraph fill — darker than nodes → hierarchy
138-
clusterBorder: '#243d6e', // subgraph border — subtle blue
139-
titleColor: '#e8eeff', // slightly blue-white
140-
edgeLabelBackground: '#162033', // edge labels match node bg
141-
nodeTextColor: '#dde4f0', // node text — slightly blue-tinted light
126+
// ── Dark mode: brand navy + cyan accents ──
127+
background: '#0B1120', // brand-bg
128+
mainBkg: '#101a2e', // node fill — brand-bg-soft, slightly lifted from page
129+
nodeBorder: '#3a4660', // node border — neutral, not screaming
130+
primaryColor: '#0e2a3a', // primary-class nodes — dark cyan-tinted
131+
primaryBorderColor: '#22D3EE', // primary node border — brand-accent
132+
primaryTextColor: '#F5F5F0', // text on primary nodes — warm white
133+
secondaryColor: '#0c1422', // secondary — darker than nodes for hierarchy
134+
tertiaryColor: '#13203a', // tertiary — mid step
135+
lineColor: '#22D3EE', // arrows — brand-accent (cyan-400)
136+
clusterBkg: '#0a1020', // subgraph fill — almost page bg
137+
clusterBorder: '#2a3550', // subgraph border — subtle neutral
138+
titleColor: '#67E8F9', // titles — cyan-300, brighter accent
139+
edgeLabelBackground: '#101a2e', // edge labels match node bg
140+
nodeTextColor: '#E5E7F0', // node text — slightly cool light
142141
} : {
143-
// ── Light mode: barely-blue node fills, brand-blue borders & arrows ──
142+
// ── Light mode: white canvas + cyan-700 accents ──
144143
background: '#ffffff',
145-
mainBkg: '#f0f5ff', // node fill — site's --bg-accent (near-white + blue)
146-
nodeBorder: '#93c5fd', // node border — blue-300 (soft but visible)
147-
primaryColor: '#dbeafe', // primary-class nodes — blue-100
148-
primaryBorderColor: '#60a5fa', // primary node border — blue-400
149-
primaryTextColor: '#1e3a8a', // text on primary nodes — blue-900 (high contrast)
150-
secondaryColor: '#edf2ff', // secondary elements — slightly deeper tint
151-
tertiaryColor: '#e5edff', // tertiary — another step deeper
152-
lineColor: '#2563eb', // arrows — site's --primary (strong, readable)
153-
clusterBkg: '#f8fafc', // subgraph fill — slate-50 (lighter than nodes)
154-
clusterBorder: '#bfdbfe', // subgraph border — blue-200
155-
titleColor: '#1e3a8a', // deep blue title — blue-900
156-
edgeLabelBackground: '#e8f0fe', // edge labels — very light blue
157-
nodeTextColor: '#0f172a', // node text — slate-900 (maximum contrast)
144+
mainBkg: '#fafafa', // node fill — barely-off-white, neutral
145+
nodeBorder: '#d4d4d8', // node border — gray-300, soft
146+
primaryColor: '#ECFEFF', // primary-class nodes — cyan-50, hint of accent
147+
primaryBorderColor: '#0E7490', // primary node border — cyan-700
148+
primaryTextColor: '#0E7490', // text on primary nodes — cyan-700
149+
secondaryColor: '#f4f4f5', // secondary — slightly deeper neutral
150+
tertiaryColor: '#fafafa', // tertiary — back to nearly white
151+
lineColor: '#0E7490', // arrows — cyan-700 (brand light accent)
152+
clusterBkg: '#fafafa', // subgraph fill — very light neutral
153+
clusterBorder: '#e5e7eb', // subgraph border — gray-200
154+
titleColor: '#0E7490', // titles — cyan-700
155+
edgeLabelBackground: '#ffffff', // edge labels white
156+
nodeTextColor: '#111827', // node text — gray-900 (max contrast)
158157
}
159158
});
160159

161-
// Enhanced Toggle Component
160+
// Toggle Section — editorial: border-top/bottom hairlines, no fill, cyan chevron.
162161
const ToggleSection = ({ title, children, defaultOpen = false }) => {
163162
const [isOpen, setIsOpen] = useState(defaultOpen);
164-
163+
165164
return (
166-
<div className="my-6 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden bg-gray-50 dark:bg-gray-800/50">
165+
<div className="my-[1.5em] border-y border-current/15">
167166
<button
168167
onClick={() => setIsOpen(!isOpen)}
169-
className="w-full px-4 py-3 bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-blue-900/30 dark:to-indigo-900/30 hover:from-blue-100 hover:to-indigo-100 dark:hover:from-blue-900/50 dark:hover:to-indigo-900/50 transition-all duration-200 flex items-center justify-between text-left font-semibold text-gray-900 dark:text-gray-100"
168+
className="w-full py-[0.7em] flex items-center justify-between text-left font-semibold transition-opacity hover:opacity-70"
170169
>
171-
<span className="flex items-center">
170+
<span className="flex items-center gap-2">
172171
{isOpen ? (
173-
<ChevronDown size={18} className="mr-2 text-blue-600 dark:text-blue-400" />
172+
<ChevronDown size={16} className="text-cyan-700 dark:text-brand-accent flex-shrink-0" />
174173
) : (
175-
<ChevronRight size={18} className="mr-2 text-blue-600 dark:text-blue-400" />
174+
<ChevronRight size={16} className="text-cyan-700 dark:text-brand-accent flex-shrink-0" />
176175
)}
177176
{title}
178177
</span>
179-
<div className="w-2 h-2 rounded-full bg-blue-500 dark:bg-blue-400"></div>
180178
</button>
181179
{isOpen && (
182-
<div className="p-4 border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800">
180+
<div className="pb-[1em] pt-[0.4em] border-t border-current/10">
183181
{children}
184182
</div>
185183
)}
@@ -778,36 +776,33 @@ const CodeBlock = ({ language, value, ...props }) => {
778776
);
779777
}
780778

781-
// Select theme based on dark mode
779+
// Editorial code block — hairline header (mono uppercase language label),
780+
// no rounded chrome, no header bg fill. Syntax highlighting theme keeps
781+
// its own colors (those are designed for code readability, not brand).
782782
const codeTheme = isDarkMode ? oneDark : oneLight;
783-
const headerBg = isDarkMode ? 'bg-gray-800' : 'bg-gray-200';
784-
const headerText = isDarkMode ? 'text-gray-300' : 'text-gray-700';
785-
const borderColor = isDarkMode ? 'border-gray-600' : 'border-gray-300';
786-
783+
787784
return (
788-
<div className="relative my-6 group">
789-
<div className={`flex items-center justify-between ${headerBg} ${headerText} px-3 sm:px-4 py-2 text-xs sm:text-sm rounded-t-lg border-b ${borderColor}`}>
790-
<span className="font-medium">{language || 'code'}</span>
785+
<div className="relative my-[1.5em] group">
786+
<div className="flex items-center justify-between px-[0.6em] py-[0.4em] border-b border-current/15 font-mono text-[0.7em] tracking-[0.12em] uppercase">
787+
<span className="opacity-60">{language || 'code'}</span>
791788
<button
792789
onClick={copyToClipboard}
793-
className="flex items-center gap-1 hover:text-blue-600 dark:hover:text-blue-400 transition-colors opacity-100 sm:opacity-0 sm:group-hover:opacity-100 text-xs sm:text-sm"
790+
className="flex items-center gap-1 hover:text-cyan-700 dark:hover:text-brand-accent transition-colors opacity-100 sm:opacity-0 sm:group-hover:opacity-100"
791+
aria-label={copied ? 'Copied' : 'Copy code'}
794792
>
795-
{copied ? <Check size={14} /> : <Copy size={14} />}
796-
<span className="hidden sm:inline">{copied ? 'Copied!' : 'Copy'}</span>
793+
{copied ? <Check size={11} /> : <Copy size={11} />}
794+
<span className="hidden sm:inline">{copied ? 'Copied' : 'Copy'}</span>
797795
</button>
798796
</div>
799797
<SyntaxHighlighter
800798
style={codeTheme}
801799
language={language || 'text'}
802800
customStyle={{
803801
margin: 0,
804-
borderTopLeftRadius: 0,
805-
borderTopRightRadius: 0,
806-
borderBottomLeftRadius: '0.5rem',
807-
borderBottomRightRadius: '0.5rem',
808-
fontSize: '0.875rem',
809-
lineHeight: '1.5',
810-
padding: '1rem',
802+
borderRadius: 0,
803+
fontSize: '0.85em',
804+
lineHeight: '1.55',
805+
padding: '1em',
811806
overflowX: 'auto',
812807
}}
813808
{...props}
@@ -995,11 +990,11 @@ const BlogMarkdownRenderer = memo(({ content, className = "", baseImagePath = ""
995990
// It's inline if: no language class AND no newlines AND content is short
996991
const isInline = !hasLanguageClass && !hasNewlines;
997992

998-
// Handle inline code
993+
// Inline code — subtle cyan tint, no border, font-mono inherits from RM theme
999994
if (isInline) {
1000995
return (
1001-
<code
1002-
className="px-1.5 py-0.5 bg-gray-200 dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded text-sm font-mono border border-gray-300 dark:border-gray-600"
996+
<code
997+
className="px-[0.35em] py-[0.05em] bg-cyan-700/10 dark:bg-brand-accent/10 text-cyan-800 dark:text-brand-accent text-[0.88em] font-mono"
1003998
{...props}
1004999
>
10051000
{children}
@@ -1021,27 +1016,27 @@ const BlogMarkdownRenderer = memo(({ content, className = "", baseImagePath = ""
10211016
return <>{children}</>;
10221017
},
10231018

1024-
// Enhanced lists
1019+
// Lists — color inherits from parent (RM-aware)
10251020
ul: ({ children, ...props }) => (
1026-
<ul className="mb-4 ml-6 space-y-2 list-disc text-gray-700 dark:text-gray-300" {...props}>
1021+
<ul className="mb-[1em] ml-[1.5em] space-y-[0.4em] list-disc" {...props}>
10271022
{children}
10281023
</ul>
10291024
),
10301025
ol: ({ children, ...props }) => (
1031-
<ol className="mb-4 ml-6 space-y-2 list-decimal text-gray-700 dark:text-gray-300" {...props}>
1026+
<ol className="mb-[1em] ml-[1.5em] space-y-[0.4em] list-decimal" {...props}>
10321027
{children}
10331028
</ol>
10341029
),
10351030
li: ({ children, ...props }) => (
1036-
<li className="leading-relaxed" {...props}>
1031+
<li className="leading-relaxed pl-[0.25em]" {...props}>
10371032
{children}
10381033
</li>
10391034
),
1040-
1041-
// Enhanced blockquotes
1035+
1036+
// Blockquote — editorial: thin cyan left rule, italic, inherits color
10421037
blockquote: ({ children, ...props }) => (
1043-
<blockquote
1044-
className="my-8 pl-6 border-l-4 border-blue-500 dark:border-blue-400 py-2 italic text-lg sm:text-xl text-gray-700 dark:text-gray-300 leading-relaxed"
1038+
<blockquote
1039+
className="my-[1.5em] pl-[1em] border-l-2 border-cyan-700/40 dark:border-brand-accent/40 italic"
10451040
{...props}
10461041
>
10471042
{children}

0 commit comments

Comments
 (0)