Files
docs/src/theme/Mermaid/index.tsx
T
arswarog ec5d2f14f4 feat(theme): тёмная тема на палитре Nord
## Зачем

Более комфортно для глаз

## Реализация

**Почему не хватило конфига в themeConfig.mermaid.options**

Docusaurus инициализирует Mermaid так:

```ts
mermaid.initialize({
  ...options,          // статично, оба режима
  theme: isDark ? 'base' : 'default',
});
```

`themeVariables` лежат внутри `options` и применяются независимо от `colorMode`. Попытка передать Nord-цвета через `options` дала бы Nord-оттенки и в светлом режиме.

**Почему swizzle, а не CSS-переопределения**

Mermaid рендерит SVG и встраивает финальные hex-значения непосредственно в `<style>` внутри SVG-элемента. CSS снаружи SVG не пробивает эти стили без `!important` на каждый конкретный селектор — хрупко и нестабильно при обновлениях Mermaid.Reviewed-on: #9

Co-authored-by: Arswarog <arswarog@yandex.ru>
2026-08-08 10:37:30 +03:00

83 lines
2.4 KiB
TypeScript

import ErrorBoundary from '@docusaurus/ErrorBoundary';
import { ErrorBoundaryErrorMessageFallback } from '@docusaurus/theme-common';
import { useColorMode } from '@docusaurus/theme-common';
import {
MermaidContainerClassName,
useMermaidConfig,
useMermaidRenderResult,
} from '@docusaurus/theme-mermaid/client';
import type { Props } from '@theme/Mermaid';
import type { RenderResult } from 'mermaid';
import React, { useRef, useEffect, useMemo, type ReactNode } from 'react';
import styles from './styles.module.css';
const NORD_THEME_VARIABLES = {
background: '#2e3440',
mainBkg: '#3b4252',
primaryColor: '#5e81ac',
primaryTextColor: '#eceff4',
primaryBorderColor: '#88c0d0',
secondaryColor: '#434c5e',
secondaryTextColor: '#d8dee9',
secondaryBorderColor: '#4c566a',
tertiaryColor: '#3b4252',
tertiaryTextColor: '#d8dee9',
tertiaryBorderColor: '#4c566a',
lineColor: '#88c0d0',
edgeLabelBackground: '#434c5e',
textColor: '#eceff4',
titleColor: '#88c0d0',
nodeBorder: '#4c566a',
clusterBkg: '#434c5e',
clusterBorder: '#4c566a',
};
function MermaidRenderResult({ renderResult }: { renderResult: RenderResult }): ReactNode {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const div = ref.current!;
renderResult.bindFunctions?.(div);
}, [renderResult]);
return (
<div
ref={ref}
className={`${MermaidContainerClassName} ${styles.container}`}
// eslint-disable-next-line react/no-danger -- mermaid renders trusted SVG
dangerouslySetInnerHTML={{ __html: renderResult.svg }}
/>
);
}
function MermaidRenderer({ value }: Props): ReactNode {
const { colorMode } = useColorMode();
const baseConfig = useMermaidConfig();
const config = useMemo(
() =>
colorMode === 'dark'
? { ...baseConfig, themeVariables: NORD_THEME_VARIABLES }
: baseConfig,
[colorMode, baseConfig],
);
const renderResult = useMermaidRenderResult({ text: value, config });
if (renderResult === null) {
return null;
}
return <MermaidRenderResult renderResult={renderResult} />;
}
export default function Mermaid(props: Props): ReactNode {
return (
<ErrorBoundary fallback={(params) => <ErrorBoundaryErrorMessageFallback {...params} />}>
<MermaidRenderer {...props} />
</ErrorBoundary>
);
}