Skip to content

Commit 928ecdc

Browse files
humitosagjohnson
andcommitted
Add styles based on the documentation tool (#473)
Use our heuristic to detect the documentation tool/theme and add specific `--readthedocs-*` CSS variables based on that for known tools/themes. Reference: readthedocs/readthedocs.org#11849 (comment) --------- Co-authored-by: Anthony <[email protected]>
1 parent f8c7c30 commit 928ecdc

File tree

6 files changed

+161
-14
lines changed

6 files changed

+161
-14
lines changed

src/constants.js

+7
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,11 @@ export const ASCIIDOCTOR = "asciidoctor";
88
export const JEKYLL = "jekyll";
99
export const DOCSIFY = "docsify";
1010
export const ANTORA = "antora";
11+
export const MDBOOK = "mdbook";
1112
export const FALLBACK_DOCTOOL = "fallback";
13+
14+
// Known documentation tools themes
15+
export const SPHINX_ALABASTER = "alabaster";
16+
export const SPHINX_FURO = "furo";
17+
export const SPHINX_READTHEDOCS = "readthedocs";
18+
export const SPHINX_IMMATERIAL = "immaterial";

src/doctools.css

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Specific styles based on documentation tools and themes
3+
*
4+
* Usage of `@layer` at-rule pushes this rules down a step in
5+
* precedence/priority. This allows a user `:root` rule to override these
6+
* values.
7+
**/
8+
@layer defaults {
9+
:root[data-readthedocs-tool="mkdocs-material"] {
10+
--readthedocs-font-size: 0.58rem;
11+
--readthedocs-flyout-font-size: 0.58rem;
12+
}
13+
14+
:root[data-readthedocs-tool="antora"] {
15+
--readthedocs-font-size: 0.7rem;
16+
--readthedocs-flyout-font-size: 0.7rem;
17+
}
18+
19+
:root[data-readthedocs-tool="mdbook"] {
20+
--readthedocs-font-size: 1.3rem;
21+
--readthedocs-flyout-font-size: 1.3rem;
22+
}
23+
24+
:root[data-readthedocs-tool="sphinx"][data-readthedocs-tool-theme="furo"] {
25+
--readthedocs-font-size: 0.725rem;
26+
--readthedocs-flyout-font-size: 0.725rem;
27+
}
28+
29+
:root[data-readthedocs-tool="sphinx"][data-readthedocs-tool-theme="immaterial"] {
30+
--readthedocs-font-size: 0.58rem;
31+
--readthedocs-flyout-font-size: 0.58rem;
32+
}
33+
}

src/flyout.css

+12-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
/* New */
1+
/* Flyout styles */
2+
3+
:host {
4+
/* These variables are used more than once, use a local variable so we can set
5+
* a default in this addon */
6+
--addons-flyout-font-size: var(
7+
--readthedocs-flyout-font-size,
8+
var(--readthedocs-font-size, 0.8rem)
9+
);
10+
--addons-flyout-line-height: var(--readthedocs-flyout-line-height, 1.25em);
11+
}
212

313
.container {
414
position: fixed;
@@ -7,6 +17,7 @@
717
height: auto;
818
max-height: calc(100% - 100px);
919
overflow-y: auto;
20+
line-height: var(--addons-flyout-line-height);
1021
}
1122

1223
.container.bottom-right {
@@ -29,13 +40,6 @@
2940
top: 50px;
3041
}
3142

32-
:host {
33-
--addons-flyout-font-size: var(
34-
--readthedocs-flyout-font-size,
35-
var(--readthedocs-font-size, 0.8rem)
36-
);
37-
}
38-
3943
:host > div {
4044
font-family: var(
4145
--readthedocs-flyout-font-family,

src/flyout.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { default as objectPath } from "object-path";
1313

1414
import styleSheet from "./flyout.css";
1515
import { AddonBase, addUtmParameters, getLinkWithFilename } from "./utils";
16+
import { SPHINX, MKDOCS_MATERIAL } from "./constants";
1617
import {
1718
EVENT_READTHEDOCS_SEARCH_SHOW,
1819
EVENT_READTHEDOCS_FLYOUT_HIDE,
@@ -38,6 +39,8 @@ export class FlyoutElement extends LitElement {
3839
this.opened = false;
3940
this.floating = true;
4041
this.position = "bottom-right";
42+
this.classes = { floating: this.floating, container: true };
43+
this.classes[this.position] = true;
4144
this.readthedocsLogo = READTHEDOCS_LOGO;
4245
}
4346

@@ -319,11 +322,8 @@ export class FlyoutElement extends LitElement {
319322
return nothing;
320323
}
321324

322-
const classes = { floating: this.floating, container: true };
323-
classes[this.position] = true;
324-
325325
return html`
326-
<div class=${classMap(classes)}>
326+
<div class=${classMap(this.classes)}>
327327
${this.renderHeader()}
328328
<main class=${classMap({ closed: !this.opened })}>
329329
${this.renderLanguages()} ${this.renderVersions()}

src/index.js

+31
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { CSSResult } from "lit";
2+
13
import { getReadTheDocsConfig } from "./readthedocs-config";
24
import * as notification from "./notification";
35
import * as analytics from "./analytics";
@@ -11,13 +13,16 @@ import * as filetreediff from "./filetreediff";
1113
import * as customscript from "./customscript";
1214
import { default as objectPath } from "object-path";
1315
import {
16+
docTool,
1417
domReady,
1518
isEmbedded,
1619
IS_PRODUCTION,
1720
setupLogging,
1821
getMetadataValue,
1922
} from "./utils";
2023

24+
import doctoolsStyleSheet from "./doctools.css";
25+
2126
export function setup() {
2227
const addons = [
2328
flyout.FlyoutAddon,
@@ -50,6 +55,32 @@ export function setup() {
5055
}
5156
}
5257

58+
// Apply fixes to variables for individual documentation tools
59+
const elementHtml = document.querySelector("html");
60+
if (elementHtml) {
61+
// Inject styles at the parent DOM to set variables at :root
62+
let styleSheet = doctoolsStyleSheet;
63+
if (doctoolsStyleSheet instanceof CSSResult) {
64+
styleSheet = doctoolsStyleSheet.styleSheet;
65+
}
66+
document.adoptedStyleSheets = [styleSheet];
67+
68+
// If we detect a documentation tool, set attributes on :root to allow
69+
// for CSS selectors to utilize these values.
70+
if (docTool.documentationTool) {
71+
elementHtml.setAttribute(
72+
"data-readthedocs-tool",
73+
docTool.documentationTool,
74+
);
75+
}
76+
if (docTool.documentationTheme) {
77+
elementHtml.setAttribute(
78+
"data-readthedocs-tool-theme",
79+
docTool.documentationTheme,
80+
);
81+
}
82+
}
83+
5384
return getReadTheDocsConfig(sendUrlParam);
5485
})
5586
.then((config) => {

src/utils.js

+74-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ import { ajv } from "./data-validation";
22
import { default as objectPath } from "object-path";
33
import {
44
SPHINX,
5+
SPHINX_FURO,
6+
SPHINX_ALABASTER,
7+
SPHINX_READTHEDOCS,
8+
SPHINX_IMMATERIAL,
9+
MDBOOK,
510
MKDOCS,
611
MKDOCS_MATERIAL,
712
DOCUSAURUS,
@@ -297,6 +302,7 @@ export class DocumentationTool {
297302

298303
constructor() {
299304
this.documentationTool = this.getDocumentationTool();
305+
this.documentationTheme = this.getDocumentationTheme();
300306
console.debug(`Documentation tool detected: ${this.documentationTool}`);
301307
}
302308

@@ -411,10 +417,63 @@ export class DocumentationTool {
411417
return DOCSIFY;
412418
}
413419

420+
if (this.isMdBook()) {
421+
return MDBOOK;
422+
}
423+
424+
if (this.isAntora()) {
425+
return ANTORA;
426+
}
427+
414428
console.debug("We were not able to detect the documentation tool.");
415429
return null;
416430
}
417431

432+
getDocumentationTheme() {
433+
const documentationTool =
434+
this.documentationTool || this.getDocumentationTool();
435+
436+
if (documentationTool === SPHINX) {
437+
if (this.isSphinxAlabasterLikeTheme()) {
438+
return SPHINX_ALABASTER;
439+
} else if (this.isSphinxReadTheDocsLikeTheme()) {
440+
return SPHINX_READTHEDOCS;
441+
} else if (this.isSphinxFuroLikeTheme()) {
442+
return SPHINX_FURO;
443+
} else if (this.isSphinxImmaterialLikeTheme()) {
444+
return SPHINX_IMMATERIAL;
445+
}
446+
}
447+
448+
// TODO: add the other known themes
449+
return null;
450+
}
451+
452+
isAntora() {
453+
if (
454+
document.querySelectorAll('meta[name="generator"][content^="Antora"]')
455+
.length
456+
) {
457+
return true;
458+
}
459+
return false;
460+
}
461+
462+
isMdBook() {
463+
// <head>
464+
// <!-- Book generated using mdBook -->
465+
// <meta charset="UTF-8">
466+
// ...
467+
if (
468+
document?.head?.firstChild?.nextSibling?.textContent.includes(
469+
"Book generated using mdBook",
470+
)
471+
) {
472+
return true;
473+
}
474+
return false;
475+
}
476+
418477
isDocsify() {
419478
if (document.querySelectorAll("head > link[href*=docsify]").length) {
420479
return true;
@@ -427,7 +486,8 @@ export class DocumentationTool {
427486
this.isSphinxAlabasterLikeTheme() ||
428487
this.isSphinxReadTheDocsLikeTheme() ||
429488
this.isSphinxFuroLikeTheme() ||
430-
this.isSphinxBookThemeLikeTheme()
489+
this.isSphinxBookThemeLikeTheme() ||
490+
this.isSphinxImmaterialLikeTheme()
431491
);
432492
}
433493

@@ -476,7 +536,7 @@ export class DocumentationTool {
476536
// MkDocs version : 1.4.2
477537
// Build Date UTC : 2023-07-11 16:08:07.379780+00:00
478538
// -->
479-
if (document.lastChild.textContent.includes("MkDocs version :")) {
539+
if (document?.lastChild?.textContent.includes("MkDocs version :")) {
480540
return true;
481541
}
482542
return false;
@@ -531,6 +591,18 @@ export class DocumentationTool {
531591
return false;
532592
}
533593

594+
isSphinxImmaterialLikeTheme() {
595+
if (
596+
document.querySelectorAll(
597+
'link[href^="_static/sphinx_immaterial_theme"]',
598+
'a[href="https://github.com/jbms/sphinx-immaterial/"][rel="noopener"]',
599+
).length
600+
) {
601+
return true;
602+
}
603+
return false;
604+
}
605+
534606
isMaterialMkDocsTheme() {
535607
if (
536608
document.querySelectorAll(

0 commit comments

Comments
 (0)