Skip to content

Commit 4da9e93

Browse files
authored
Merge pull request #378 from a-ignatov-parc/fix-prefix-css
Fix CSS prefixer
2 parents 5d0aaf3 + 0415860 commit 4da9e93

File tree

5 files changed

+77
-72
lines changed

5 files changed

+77
-72
lines changed

.changeset/dirty-owls-check.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'docusaurus-theme-redoc': patch
3+
---
4+
5+
Fixes incorrect CSS prefixing that breaks layout on mobile devices and resolves reintroduced issues with incorrect background color for code samples

packages/docusaurus-theme-redoc/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
"clsx": "^1.2.1",
3737
"lodash": "^4.17.21",
3838
"mobx": "^6.12.4",
39+
"postcss": "^8.4.45",
40+
"postcss-prefix-selector": "^1.16.1",
3941
"redoc": "2.1.5",
4042
"styled-components": "^6.1.11"
4143
},
@@ -45,6 +47,7 @@
4547
"@docusaurus/theme-common": "^3.4.0",
4648
"@docusaurus/types": "^3.4.0",
4749
"@types/lodash": "^4.14.200",
50+
"@types/postcss-prefix-selector": "^1",
4851
"@types/react": "^18.3.3",
4952
"@types/react-dom": "^18.3.0",
5053
"@types/react-is": "^18.3.0",

packages/docusaurus-theme-redoc/src/theme/Redoc/ServerStyles.tsx

Lines changed: 25 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -5,49 +5,27 @@ import { AppStore, Redoc, RedocRawOptions } from 'redoc';
55
// eslint-disable-next-line import/no-extraneous-dependencies
66
import { renderToString } from 'react-dom/server';
77
import { ServerStyleSheet } from 'styled-components';
8+
import postcss from 'postcss';
9+
import prefixer from 'postcss-prefix-selector';
810

9-
/**
10-
* @see https://stackoverflow.com/a/54077142
11-
*/
12-
const prefixCssSelectors = function (rules: string, className: string) {
13-
const classLen = className.length;
14-
let char, nextChar, isAt, isIn;
15-
16-
// makes sure the className will not concatenate the selector
17-
className += ' ';
18-
19-
// removes comments
20-
rules = rules.replace(/\/\*(?:(?!\*\/)[\s\S])*\*\/|[\r\n\t]+/g, '');
21-
22-
// makes sure nextChar will not target a space
23-
rules = rules.replace(/}(\s*)@/g, '}@');
24-
rules = rules.replace(/}(\s*)}/g, '}}');
25-
26-
for (let i = 0; i < rules.length - 2; i++) {
27-
char = rules[i];
28-
nextChar = rules[i + 1];
11+
const prefixCssSelectors = function (css: string, className: string): string {
12+
const processor = postcss().use(
13+
prefixer({
14+
prefix: className,
15+
}),
16+
);
2917

30-
if (char === '@' && nextChar !== 'f') isAt = true;
31-
if (!isAt && char === '{') isIn = true;
32-
if (isIn && char === '}') isIn = false;
18+
return processor.process(css).css;
19+
};
3320

34-
if (
35-
!isIn &&
36-
nextChar !== '@' &&
37-
nextChar !== '}' &&
38-
(char === '}' || char === ',' || ((char === '{' || char === ';') && isAt))
39-
) {
40-
rules = rules.slice(0, i + 1) + className + rules.slice(i + 1);
41-
i += classLen;
42-
isAt = false;
43-
}
44-
}
21+
const renderCss = function (store: AppStore): string {
22+
const styleSheet = new ServerStyleSheet();
4523

46-
// prefix the first select if it is not `@media` and if it is not yet prefixed
47-
if (rules.indexOf(className) !== 0 && rules.indexOf('@') !== 0)
48-
rules = className + rules;
24+
renderToString(
25+
styleSheet.collectStyles(React.createElement(Redoc, { store })),
26+
);
4927

50-
return rules;
28+
return String(styleSheet.instance);
5129
};
5230

5331
const LIGHT_MODE_PREFIX = "html:not([data-theme='dark'])";
@@ -63,41 +41,26 @@ export function ServerStyles({
6341
darkThemeOptions: RedocRawOptions;
6442
}) {
6543
const fullUrl = useBaseUrl(specProps.url, { absolute: true });
66-
const css = {
67-
light: '',
68-
dark: '',
69-
};
70-
const lightSheet = new ServerStyleSheet();
71-
const lightStore = new AppStore(specProps.spec, fullUrl, lightThemeOptions);
72-
renderToString(
73-
lightSheet.collectStyles(React.createElement(Redoc, { store: lightStore })),
74-
);
75-
const lightStyleTag = lightSheet.getStyleTags();
76-
let lightCss = lightStyleTag.slice(lightStyleTag.indexOf('>') + 1);
77-
lightCss = lightCss.slice(0, lightCss.indexOf('<style'));
78-
css.light = prefixCssSelectors(lightCss, LIGHT_MODE_PREFIX);
7944

80-
const darkSheet = new ServerStyleSheet();
81-
const darkStore = new AppStore(specProps.spec, fullUrl, darkThemeOptions);
82-
renderToString(
83-
darkSheet.collectStyles(React.createElement(Redoc, { store: darkStore })),
45+
const lightCss = prefixCssSelectors(
46+
renderCss(new AppStore(specProps.spec, fullUrl, lightThemeOptions)),
47+
LIGHT_MODE_PREFIX,
8448
);
85-
const darkStyleTag = darkSheet.getStyleTags();
86-
let darkCss = darkStyleTag.slice(darkStyleTag.indexOf('>') + 1);
87-
darkCss = darkCss.slice(0, darkCss.indexOf('<style'));
88-
css.dark = prefixCssSelectors(darkCss, DARK_MODE_PREFIX).slice(
89-
DARK_MODE_PREFIX.length + 1,
49+
50+
const darkCss = prefixCssSelectors(
51+
renderCss(new AppStore(specProps.spec, fullUrl, darkThemeOptions)),
52+
DARK_MODE_PREFIX,
9053
);
9154

9255
return (
9356
<div className="redocusaurus-styles">
9457
<style
9558
key="light-mode-styles"
96-
dangerouslySetInnerHTML={{ __html: css.light }}
59+
dangerouslySetInnerHTML={{ __html: lightCss }}
9760
/>
9861
<style
9962
key="dark-mode-styles"
100-
dangerouslySetInnerHTML={{ __html: css.dark }}
63+
dangerouslySetInnerHTML={{ __html: darkCss }}
10164
/>
10265
</div>
10366
);

packages/docusaurus-theme-redoc/src/theme/Redoc/styles.css

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,16 @@ html[data-theme='dark']
127127
}
128128

129129
/* Fixes https://github.com/rohit-gohri/redocusaurus/issues/306 */
130-
html[data-theme='dark']
131-
.redocusaurus
130+
html[data-theme='dark']
131+
.redocusaurus
132132
div[id^='tag']
133133
button:has(span):has(.operation-type) {
134134
background-color: var(--ifm-color-gray-800);
135135
}
136136

137-
html[data-theme='dark']
138-
.redocusaurus
139-
div[id^='tag']
137+
html[data-theme='dark']
138+
.redocusaurus
139+
div[id^='tag']
140140
button + div {
141141
background-color: var(--ifm-color-emphasis-0);
142142
}
@@ -146,20 +146,22 @@ html[data-theme='dark']
146146
margin-bottom: 10px;
147147
}
148148

149-
/*
149+
/*
150150
* Code Samples
151151
* @see https://github.com/rohit-gohri/redocusaurus/issues/217
152152
*/
153153
html:not([data-theme='dark'])
154154
.redocusaurus
155-
div[id^='react-tabs']
156-
> div:nth-child(1)
157-
> pre:nth-child(2) {
155+
[role='tabpanel']
156+
pre {
158157
background-color: transparent;
159158
}
160159

161160
/** https://github.com/rohit-gohri/redocusaurus/issues/45 */
162-
html:not([data-theme='dark']) .redocusaurus div[id^='react-tabs'] code {
161+
html:not([data-theme='dark'])
162+
.redocusaurus
163+
[role='tabpanel']
164+
code {
163165
color: var(--ifm-color-emphasis-0);
164166
}
165167

yarn.lock

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3725,6 +3725,15 @@ __metadata:
37253725
languageName: node
37263726
linkType: hard
37273727

3728+
"@types/postcss-prefix-selector@npm:^1":
3729+
version: 1.16.3
3730+
resolution: "@types/postcss-prefix-selector@npm:1.16.3"
3731+
dependencies:
3732+
postcss: "npm:^8.4.27"
3733+
checksum: c0ece88bf7f5adfdcbf1f44436ce3e78d5fe0431fd84dce4040e53c03af2ca71fd3f1a36d127e1402c085c5507d5403fe461577670252c311556c102d7dc948d
3734+
languageName: node
3735+
linkType: hard
3736+
37283737
"@types/prismjs@npm:^1.26.0":
37293738
version: 1.26.2
37303739
resolution: "@types/prismjs@npm:1.26.2"
@@ -6357,6 +6366,7 @@ __metadata:
63576366
"@docusaurus/types": "npm:^3.4.0"
63586367
"@redocly/openapi-core": "npm:1.16.0"
63596368
"@types/lodash": "npm:^4.14.200"
6369+
"@types/postcss-prefix-selector": "npm:^1"
63606370
"@types/react": "npm:^18.3.3"
63616371
"@types/react-dom": "npm:^18.3.0"
63626372
"@types/react-is": "npm:^18.3.0"
@@ -6368,6 +6378,8 @@ __metadata:
63686378
lodash: "npm:^4.17.21"
63696379
mobx: "npm:^6.12.4"
63706380
nodemon: "npm:^3.1.0"
6381+
postcss: "npm:^8.4.45"
6382+
postcss-prefix-selector: "npm:^1.16.1"
63716383
react: "npm:^18.3.1"
63726384
react-dom: "npm:^18.3.1"
63736385
react-is: "npm:^18.3.1"
@@ -12402,6 +12414,15 @@ __metadata:
1240212414
languageName: node
1240312415
linkType: hard
1240412416

12417+
"postcss-prefix-selector@npm:^1.16.1":
12418+
version: 1.16.1
12419+
resolution: "postcss-prefix-selector@npm:1.16.1"
12420+
peerDependencies:
12421+
postcss: ">4 <9"
12422+
checksum: e6a138f2c2f4be38013e778d0242f7fced1468759b508b3f85349ec1a5cd0bd6e7194998749508edb7a987fff9d4f3ee89b7d4f34e5df34dbf65130bf3bc680c
12423+
languageName: node
12424+
linkType: hard
12425+
1240512426
"postcss-reduce-idents@npm:^6.0.3":
1240612427
version: 6.0.3
1240712428
resolution: "postcss-reduce-idents@npm:6.0.3"
@@ -12507,6 +12528,17 @@ __metadata:
1250712528
languageName: node
1250812529
linkType: hard
1250912530

12531+
"postcss@npm:^8.4.27, postcss@npm:^8.4.45":
12532+
version: 8.4.45
12533+
resolution: "postcss@npm:8.4.45"
12534+
dependencies:
12535+
nanoid: "npm:^3.3.7"
12536+
picocolors: "npm:^1.0.1"
12537+
source-map-js: "npm:^1.2.0"
12538+
checksum: 7eaf7346d04929ee979548ece5e34d253eae6f175346e298b2c4621ad6f4ee00adfe7abe72688640e910c0361ae50537c5dda3e35fd1066491282c342b3ee5c8
12539+
languageName: node
12540+
linkType: hard
12541+
1251012542
"preferred-pm@npm:^3.0.0":
1251112543
version: 3.1.2
1251212544
resolution: "preferred-pm@npm:3.1.2"

0 commit comments

Comments
 (0)