refactor: control base styling of code blocks via CSS vars (#7172)

Co-authored-by: sebastienlorber <lorber.sebastien@gmail.com>
This commit is contained in:
Alexey Pyltsyn 2022-04-14 19:16:39 +03:00 committed by GitHub
parent fe064a87a6
commit ad1526aade
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 45 additions and 17 deletions

View file

@ -481,6 +481,7 @@ describe('themeConfig', () => {
const prismConfig = { const prismConfig = {
prism: { prism: {
additionalLanguages: ['kotlin', 'java'], additionalLanguages: ['kotlin', 'java'],
theme: darkTheme,
}, },
}; };
expect(testValidateThemeConfig(prismConfig)).toEqual({ expect(testValidateThemeConfig(prismConfig)).toEqual({

View file

@ -7,7 +7,9 @@
.copyButton { .copyButton {
display: flex; display: flex;
background: inherit; /* TODO: move to base button styling */
background: var(--prism-background-color);
color: var(--prism-color);
border: 1px solid var(--ifm-color-emphasis-300); border: 1px solid var(--ifm-color-emphasis-300);
border-radius: var(--ifm-global-radius); border-radius: var(--ifm-global-radius);
padding: 0.4rem; padding: 0.4rem;

View file

@ -16,6 +16,7 @@ import {
containsLineNumbers, containsLineNumbers,
ThemeClassNames, ThemeClassNames,
usePrismTheme, usePrismTheme,
getPrismCssVariables,
} from '@docusaurus/theme-common'; } from '@docusaurus/theme-common';
import CopyButton from '@theme/CodeBlock/CopyButton'; import CopyButton from '@theme/CodeBlock/CopyButton';
import type {Props} from '@theme/CodeBlock'; import type {Props} from '@theme/CodeBlock';
@ -50,6 +51,8 @@ export default function CodeBlock({
const codeBlockTitle = parseCodeBlockTitle(metastring) || title; const codeBlockTitle = parseCodeBlockTitle(metastring) || title;
const prismTheme = usePrismTheme(); const prismTheme = usePrismTheme();
const prismCssVariables = getPrismCssVariables(prismTheme);
// <pre> tags in markdown map to CodeBlocks and they may contain JSX children. // <pre> tags in markdown map to CodeBlocks and they may contain JSX children.
// When the children is not a simple string, we just return a styled block // When the children is not a simple string, we just return a styled block
// without actually highlighting. // without actually highlighting.
@ -61,7 +64,7 @@ export default function CodeBlock({
theme={prismTheme} theme={prismTheme}
code="" code=""
language={'text' as Language}> language={'text' as Language}>
{({className, style}) => ( {({className}) => (
<pre <pre
/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */ /* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */
tabIndex={0} tabIndex={0}
@ -73,7 +76,7 @@ export default function CodeBlock({
blockClassName, blockClassName,
ThemeClassNames.common.codeBlock, ThemeClassNames.common.codeBlock,
)} )}
style={style}> style={prismCssVariables}>
<code className={styles.codeBlockLines}>{children}</code> <code className={styles.codeBlockLines}>{children}</code>
</pre> </pre>
)} )}
@ -99,7 +102,7 @@ export default function CodeBlock({
theme={prismTheme} theme={prismTheme}
code={code} code={code}
language={(language ?? 'text') as Language}> language={(language ?? 'text') as Language}>
{({className, style, tokens, getLineProps, getTokenProps}) => ( {({className, tokens, getLineProps, getTokenProps}) => (
<div <div
className={clsx( className={clsx(
styles.codeBlockContainer, styles.codeBlockContainer,
@ -109,13 +112,12 @@ export default function CodeBlock({
language && !blockClassName.includes(`language-${language}`), language && !blockClassName.includes(`language-${language}`),
}, },
ThemeClassNames.common.codeBlock, ThemeClassNames.common.codeBlock,
)}>
{codeBlockTitle && (
<div style={style} className={styles.codeBlockTitle}>
{codeBlockTitle}
</div>
)} )}
<div className={styles.codeBlockContent} style={style}> style={prismCssVariables}>
{codeBlockTitle && (
<div className={styles.codeBlockTitle}>{codeBlockTitle}</div>
)}
<div className={styles.codeBlockContent}>
<pre <pre
/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */ /* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */
tabIndex={0} tabIndex={0}

View file

@ -6,6 +6,8 @@
*/ */
.codeBlockContainer { .codeBlockContainer {
background: var(--prism-background-color);
color: var(--prism-color);
margin-bottom: var(--ifm-leading); margin-bottom: var(--ifm-leading);
box-shadow: var(--ifm-global-shadow-lw); box-shadow: var(--ifm-global-shadow-lw);
border-radius: var(--ifm-code-border-radius); border-radius: var(--ifm-code-border-radius);
@ -28,7 +30,7 @@
} }
.codeBlock { .codeBlock {
--ifm-pre-background: inherit; --ifm-pre-background: var(--prism-background-color);
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
@ -44,7 +46,6 @@
.codeBlockLines { .codeBlockLines {
font: inherit; font: inherit;
background: var(--ifm-pre-background);
/* rtl:ignore */ /* rtl:ignore */
float: left; float: left;
min-width: 100%; min-width: 100%;

View file

@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import defaultPrismTheme from 'prism-react-renderer/themes/palenight';
import {Joi, URISchema} from '@docusaurus/utils-validation'; import {Joi, URISchema} from '@docusaurus/utils-validation';
import type { import type {
ThemeConfig, ThemeConfig,
@ -32,6 +33,7 @@ export const DEFAULT_CONFIG = {
metadata: [], metadata: [],
prism: { prism: {
additionalLanguages: [], additionalLanguages: [],
theme: defaultPrismTheme,
}, },
navbar: { navbar: {
hideOnScroll: false, hideOnScroll: false,
@ -335,7 +337,7 @@ export const ThemeConfigSchema = Joi.object({
theme: Joi.object({ theme: Joi.object({
plain: Joi.alternatives().try(Joi.array(), Joi.object()).required(), plain: Joi.alternatives().try(Joi.array(), Joi.object()).required(),
styles: Joi.alternatives().try(Joi.array(), Joi.object()).required(), styles: Joi.alternatives().try(Joi.array(), Joi.object()).required(),
}), }).default(DEFAULT_CONFIG.prism.theme),
darkTheme: Joi.object({ darkTheme: Joi.object({
plain: Joi.alternatives().try(Joi.array(), Joi.object()).required(), plain: Joi.alternatives().try(Joi.array(), Joi.object()).required(),
styles: Joi.alternatives().try(Joi.array(), Joi.object()).required(), styles: Joi.alternatives().try(Joi.array(), Joi.object()).required(),

View file

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import defaultTheme from 'prism-react-renderer/themes/palenight'; import type {PrismTheme} from 'prism-react-renderer';
import {useColorMode} from '../contexts/colorMode'; import {useColorMode} from '../contexts/colorMode';
import {useThemeConfig} from '../utils/useThemeConfig'; import {useThemeConfig} from '../utils/useThemeConfig';
@ -13,10 +13,10 @@ import {useThemeConfig} from '../utils/useThemeConfig';
* Returns a color-mode-dependent Prism theme: whatever the user specified in * Returns a color-mode-dependent Prism theme: whatever the user specified in
* the config. Falls back to `palenight`. * the config. Falls back to `palenight`.
*/ */
export function usePrismTheme(): typeof defaultTheme { export function usePrismTheme(): PrismTheme {
const {prism} = useThemeConfig(); const {prism} = useThemeConfig();
const {colorMode} = useColorMode(); const {colorMode} = useColorMode();
const lightModeTheme = prism.theme || defaultTheme; const lightModeTheme = prism.theme;
const darkModeTheme = prism.darkTheme || lightModeTheme; const darkModeTheme = prism.darkTheme || lightModeTheme;
const prismTheme = colorMode === 'dark' ? darkModeTheme : lightModeTheme; const prismTheme = colorMode === 'dark' ? darkModeTheme : lightModeTheme;

View file

@ -35,6 +35,7 @@ export {
parseLanguage, parseLanguage,
parseLines, parseLines,
containsLineNumbers, containsLineNumbers,
getPrismCssVariables,
} from './utils/codeBlockUtils'; } from './utils/codeBlockUtils';
export { export {

View file

@ -6,6 +6,8 @@
*/ */
import rangeParser from 'parse-numeric-range'; import rangeParser from 'parse-numeric-range';
import type {PrismTheme} from 'prism-react-renderer';
import type {CSSProperties} from 'react';
const codeBlockTitleRegex = /title=(?<quote>["'])(?<title>.*?)\1/; const codeBlockTitleRegex = /title=(?<quote>["'])(?<title>.*?)\1/;
const highlightLinesRangeRegex = /\{(?<range>[\d,-]+)\}/; const highlightLinesRangeRegex = /\{(?<range>[\d,-]+)\}/;
@ -173,3 +175,20 @@ export function parseLines(
code = lines.join('\n'); code = lines.join('\n');
return {highlightLines, code}; return {highlightLines, code};
} }
export function getPrismCssVariables(prismTheme: PrismTheme): CSSProperties {
const mapping: {[name: keyof PrismTheme['plain']]: string} = {
color: '--prism-color',
backgroundColor: '--prism-background-color',
};
const properties: CSSProperties = {};
Object.entries(prismTheme.plain).forEach(([key, value]) => {
const varName = mapping[key];
if (varName && typeof value === 'string') {
// @ts-expect-error: why css variables not in inline style type?
properties[varName] = value;
}
});
return properties;
}

View file

@ -53,7 +53,7 @@ export type AnnouncementBarConfig = {
}; };
export type PrismConfig = { export type PrismConfig = {
theme?: PrismTheme; theme: PrismTheme;
darkTheme?: PrismTheme; darkTheme?: PrismTheme;
defaultLanguage?: string; defaultLanguage?: string;
additionalLanguages: string[]; additionalLanguages: string[];