From c63b34fe859f9a98655bd63e5aa98c871d7b6620 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 20 May 2022 15:36:02 +0200 Subject: [PATCH 01/16] feat(techdocs): define style rule options Signed-off-by: Camila Belo --- .../reader/transformers/styles/rules/types.ts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 plugins/techdocs/src/reader/transformers/styles/rules/types.ts diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/types.ts b/plugins/techdocs/src/reader/transformers/styles/rules/types.ts new file mode 100644 index 0000000000..6419ef8e39 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/rules/types.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BackstageTheme } from '@backstage/theme'; + +/** + * A Backstage sidebar object that contains properties such as its pin state. + */ +type BackstageSidebar = { + /** Tracks whether the user pinned the sidebar or not. */ + isPinned: boolean; +}; + +/** + * A dependencies object injected into rules by the style processor. + */ +export type RuleOptions = { + /** + * A Backstage theme object that contains the application's design tokens. + */ + theme: BackstageTheme; + /** + * A Backstage sidebar, see {@link BackstageSidebar} for more details. + */ + sidebar: BackstageSidebar; +}; From 97a7e8bb0f7c66c44613b6b8fb7489ffd4a933e2 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 20 May 2022 15:37:29 +0200 Subject: [PATCH 02/16] feat(techdocs): extract variables rules Signed-off-by: Camila Belo --- .../transformers/styles/rules/variables.ts | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 plugins/techdocs/src/reader/transformers/styles/rules/variables.ts diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/variables.ts b/plugins/techdocs/src/reader/transformers/styles/rules/variables.ts new file mode 100644 index 0000000000..908cb39faf --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/rules/variables.ts @@ -0,0 +1,168 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RuleOptions } from './types'; +import { alpha, lighten } from '@material-ui/core'; + +export default ({ theme }: RuleOptions) => ` +/*================== Variables ==================*/ +/* + As the MkDocs output is rendered in shadow DOM, the CSS variable definitions on the root selector are not applied. Instead, they have to be applied on :host. + As there is no way to transform the served main*.css yet (for example in the backend), we have to copy from main*.css and modify them. +*/ + +:host { + /* FONT */ + --md-default-fg-color: ${theme.palette.text.primary}; + --md-default-fg-color--light: ${theme.palette.text.secondary}; + --md-default-fg-color--lighter: ${lighten(theme.palette.text.secondary, 0.7)}; + --md-default-fg-color--lightest: ${lighten( + theme.palette.text.secondary, + 0.3, + )}; + + /* BACKGROUND */ + --md-default-bg-color:${theme.palette.background.default}; + --md-default-bg-color--light: ${theme.palette.background.paper}; + --md-default-bg-color--lighter: ${lighten( + theme.palette.background.paper, + 0.7, + )}; + --md-default-bg-color--lightest: ${lighten( + theme.palette.background.paper, + 0.3, + )}; + + /* PRIMARY */ + --md-primary-fg-color: ${theme.palette.primary.main}; + --md-primary-fg-color--light: ${theme.palette.primary.light}; + --md-primary-fg-color--dark: ${theme.palette.primary.dark}; + --md-primary-bg-color: ${theme.palette.primary.contrastText}; + --md-primary-bg-color--light: ${lighten( + theme.palette.primary.contrastText, + 0.7, + )}; + + /* ACCENT */ + --md-accent-fg-color: var(--md-primary-fg-color); + + /* SHADOW */ + --md-shadow-z1: ${theme.shadows[1]}; + --md-shadow-z2: ${theme.shadows[2]}; + --md-shadow-z3: ${theme.shadows[3]}; + + /* EXTENSIONS */ + --md-admonition-fg-color: var(--md-default-fg-color); + --md-admonition-bg-color: var(--md-default-bg-color); + /* Admonitions and others are using SVG masks to define icons. These masks are defined as CSS variables. */ + --md-admonition-icon--note: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--abstract: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--info: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--tip: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--success: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--question: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--warning: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--failure: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--danger: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--bug: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--example: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--quote: url('data:image/svg+xml;charset=utf-8,'); + --md-footnotes-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-details-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-tasklist-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-tasklist-icon--checked: url('data:image/svg+xml;charset=utf-8,'); + --md-nav-icon--prev: url('data:image/svg+xml;charset=utf-8,'); + --md-nav-icon--next: url('data:image/svg+xml;charset=utf-8,'); + --md-toc-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-clipboard-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-search-result-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-source-forks-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-source-repositories-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-source-stars-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-source-version-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-version-icon: url('data:image/svg+xml;charset=utf-8,'); +} + +:host > * { + /* CODE */ + --md-code-fg-color: ${theme.palette.text.primary}; + --md-code-bg-color: ${theme.palette.background.paper}; + --md-code-hl-color: ${alpha(theme.palette.warning.main, 0.5)}; + --md-code-hl-keyword-color: ${ + theme.palette.type === 'dark' + ? theme.palette.primary.light + : theme.palette.primary.dark + }; + --md-code-hl-function-color: ${ + theme.palette.type === 'dark' + ? theme.palette.secondary.light + : theme.palette.secondary.dark + }; + --md-code-hl-string-color: ${ + theme.palette.type === 'dark' + ? theme.palette.success.light + : theme.palette.success.dark + }; + --md-code-hl-number-color: ${ + theme.palette.type === 'dark' + ? theme.palette.error.light + : theme.palette.error.dark + }; + --md-code-hl-constant-color: var(--md-code-hl-function-color); + --md-code-hl-special-color: var(--md-code-hl-function-color); + --md-code-hl-name-color: var(--md-code-fg-color); + --md-code-hl-comment-color: var(--md-default-fg-color--light); + --md-code-hl-generic-color: var(--md-default-fg-color--light); + --md-code-hl-variable-color: var(--md-default-fg-color--light); + --md-code-hl-operator-color: var(--md-default-fg-color--light); + --md-code-hl-punctuation-color: var(--md-default-fg-color--light); + + /* TYPESET */ + --md-typeset-font-size: 1rem; + --md-typeset-color: var(--md-default-fg-color); + --md-typeset-a-color: var(--md-accent-fg-color); + --md-typeset-table-color: ${theme.palette.text.primary}; + --md-typeset-del-color: ${ + theme.palette.type === 'dark' + ? alpha(theme.palette.error.dark, 0.5) + : alpha(theme.palette.error.light, 0.5) + }; + --md-typeset-ins-color: ${ + theme.palette.type === 'dark' + ? alpha(theme.palette.success.dark, 0.5) + : alpha(theme.palette.success.light, 0.5) + }; + --md-typeset-mark-color: ${ + theme.palette.type === 'dark' + ? alpha(theme.palette.warning.dark, 0.5) + : alpha(theme.palette.warning.light, 0.5) + }; +} + +@media screen and (max-width: 76.1875em) { + :host > * { + /* TYPESET */ + --md-typeset-font-size: .9rem; + } +} + +@media screen and (max-width: 600px) { + :host > * { + /* TYPESET */ + --md-typeset-font-size: .7rem; + } +} +`; From eeba7f240c8521b148c1fd12ce2777f5b5272cf1 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 20 May 2022 15:37:49 +0200 Subject: [PATCH 03/16] feat(techdocs): extract reset rules Signed-off-by: Camila Belo --- .../reader/transformers/styles/rules/reset.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 plugins/techdocs/src/reader/transformers/styles/rules/reset.ts diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/reset.ts b/plugins/techdocs/src/reader/transformers/styles/rules/reset.ts new file mode 100644 index 0000000000..16868460c3 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/rules/reset.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RuleOptions } from './types'; + +export default ({ theme }: RuleOptions) => ` +/*================== Reset ==================*/ + +body { + --md-text-color: var(--md-default-fg-color); + --md-text-link-color: var(--md-accent-fg-color); + --md-text-font-family: ${theme.typography.fontFamily}; + font-family: var(--md-text-font-family); + background-color: unset; +} +`; From c8df6bfd7e28664cf34bed322935b3b3c3a74f14 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 20 May 2022 15:38:24 +0200 Subject: [PATCH 04/16] feat(techdocs): extract layout rules Signed-off-by: Camila Belo --- .../transformers/styles/rules/layout.ts | 210 ++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 plugins/techdocs/src/reader/transformers/styles/rules/layout.ts diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/layout.ts b/plugins/techdocs/src/reader/transformers/styles/rules/layout.ts new file mode 100644 index 0000000000..2488504dd9 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/rules/layout.ts @@ -0,0 +1,210 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RuleOptions } from './types'; + +export default ({ theme, sidebar }: RuleOptions) => ` +/*================== Layout ==================*/ + +.md-grid { + max-width: 100%; + margin: 0; +} + +.md-nav { + font-size: calc(var(--md-typeset-font-size) * 0.9); +} +.md-nav__link { + display: flex; + align-items: center; + justify-content: space-between; +} +.md-nav__icon { + height: 20px !important; + width: 20px !important; + margin-left:${theme.spacing(1)}px; +} +.md-nav__icon svg { + margin: 0; + width: 20px !important; + height: 20px !important; +} +.md-nav__icon:after { + width: 20px !important; + height: 20px !important; +} + +.md-main__inner { + margin-top: 0; +} + +.md-sidebar { + bottom: 75px; + position: fixed; + width: 16rem; + overflow-y: auto; + overflow-x: hidden; + scrollbar-color: rgb(193, 193, 193) #eee; + scrollbar-width: thin; +} +.md-sidebar .md-sidebar__scrollwrap { + width: calc(16rem - 10px); +} +.md-sidebar--secondary { + right: ${theme.spacing(3)}px; +} +.md-sidebar::-webkit-scrollbar { + width: 5px; +} +.md-sidebar::-webkit-scrollbar-button { + width: 5px; + height: 5px; +} +.md-sidebar::-webkit-scrollbar-track { + background: #eee; + border: 1 px solid rgb(250, 250, 250); + box-shadow: 0px 0px 3px #dfdfdf inset; + border-radius: 3px; +} +.md-sidebar::-webkit-scrollbar-thumb { + width: 5px; + background: rgb(193, 193, 193); + border: transparent; + border-radius: 3px; +} +.md-sidebar::-webkit-scrollbar-thumb:hover { + background: rgb(125, 125, 125); +} + +.md-content { + max-width: calc(100% - 16rem * 2); + margin-left: 16rem; + margin-bottom: 50px; +} + +.md-footer { + position: fixed; + bottom: 0px; +} +.md-footer__title { + background-color: unset; +} +.md-footer-nav__link { + width: 16rem; +} + +.md-dialog { + background-color: unset; +} + +@media screen and (min-width: 76.25em) { + .md-sidebar { + height: auto; + } +} + +@media screen and (max-width: 76.1875em) { + .md-nav { + transition: none !important; + background-color: var(--md-default-bg-color) + } + .md-nav--primary .md-nav__title { + cursor: auto; + color: var(--md-default-fg-color); + font-weight: 700; + white-space: normal; + line-height: 1rem; + height: auto; + display: flex; + flex-flow: column; + row-gap: 1.6rem; + padding: 1.2rem .8rem .8rem; + background-color: var(--md-default-bg-color); + } + .md-nav--primary .md-nav__title~.md-nav__list { + box-shadow: none; + } + .md-nav--primary .md-nav__title ~ .md-nav__list > :first-child { + border-top: none; + } + .md-nav--primary .md-nav__title .md-nav__button { + display: none; + } + .md-nav--primary .md-nav__title .md-nav__icon { + color: var(--md-default-fg-color); + position: static; + height: auto; + margin: 0 0 0 -0.2rem; + } + .md-nav--primary > .md-nav__title [for="none"] { + padding-top: 0; + } + .md-nav--primary .md-nav__item { + border-top: none; + } + .md-nav--primary :is(.md-nav__title,.md-nav__item) { + font-size : var(--md-typeset-font-size); + } + .md-nav .md-source { + display: none; + } + + .md-sidebar { + height: 100%; + } + .md-sidebar--primary { + width: 12.1rem !important; + z-index: 200; + left: ${ + sidebar.isPinned ? 'calc(-12.1rem + 242px)' : 'calc(-12.1rem + 72px)' + } !important; + } + .md-sidebar--secondary:not([hidden]) { + display: none; + } + + .md-content { + max-width: 100%; + margin-left: 0; + } + + .md-header__button { + margin: 0.4rem 0; + margin-left: 0.4rem; + padding: 0; + } + + .md-overlay { + left: 0; + } + + .md-footer { + position: static; + padding-left: 0; + } + .md-footer-nav__link { + /* footer links begin to overlap at small sizes without setting width */ + width: 50%; + } +} + +@media screen and (max-width: 600px) { + .md-sidebar--primary { + left: -12.1rem !important; + width: 12.1rem; + } +} +`; From 432ed4ea1d02755a5d9c273882a8728ea94b9527 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 20 May 2022 15:38:47 +0200 Subject: [PATCH 05/16] feat(techdocs): extract typeset rules Signed-off-by: Camila Belo --- .../transformers/styles/rules/typeset.ts | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 plugins/techdocs/src/reader/transformers/styles/rules/typeset.ts diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/typeset.ts b/plugins/techdocs/src/reader/transformers/styles/rules/typeset.ts new file mode 100644 index 0000000000..e24b61da83 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/rules/typeset.ts @@ -0,0 +1,109 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RuleOptions } from './types'; + +type TypographyHeadings = Pick< + RuleOptions['theme']['typography'], + 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' +>; + +type TypographyHeadingsKeys = keyof TypographyHeadings; + +const headings: TypographyHeadingsKeys[] = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']; + +export default ({ theme }: RuleOptions) => ` +/*================== Typeset ==================*/ + +.md-typeset { + font-size: var(--md-typeset-font-size); +} + +${headings.reduce((style, heading) => { + const styles = theme.typography[heading]; + const { lineHeight, fontFamily, fontWeight, fontSize } = styles; + const calculate = (value: typeof fontSize) => { + let factor: number | string = 1; + if (typeof value === 'number') { + // 60% of the size defined because it is too big + factor = (value / 16) * 0.6; + } + if (typeof value === 'string') { + factor = value.replace('rem', ''); + } + return `calc(${factor} * var(--md-typeset-font-size))`; + }; + return style.concat(` + .md-typeset ${heading} { + color: var(--md-default-fg-color); + line-height: ${lineHeight}; + font-family: ${fontFamily}; + font-weight: ${fontWeight}; + font-size: ${calculate(fontSize)}; + } + `); +}, '')} + +.md-typeset .md-content__button { + color: var(--md-default-fg-color); +} + +.md-typeset hr { + border-bottom: 0.05rem dotted ${theme.palette.divider}; +} + +.md-typeset details { + font-size: var(--md-typeset-font-size) !important; +} +.md-typeset details summary { + padding-left: 2.5rem !important; +} +.md-typeset details summary:before, +.md-typeset details summary:after { + top: 50% !important; + width: 20px !important; + height: 20px !important; + transform: rotate(0deg) translateY(-50%) !important; +} +.md-typeset details[open] > summary:after { + transform: rotate(90deg) translateX(-50%) !important; +} + +.md-typeset blockquote { + color: var(--md-default-fg-color--light); + border-left: 0.2rem solid var(--md-default-fg-color--light); +} + +.md-typeset table:not([class]) { + font-size: var(--md-typeset-font-size); + border: 1px solid var(--md-default-fg-color); + border-bottom: none; + border-collapse: collapse; +} +.md-typeset table:not([class]) th { + font-weight: bold; +} +.md-typeset table:not([class]) td, .md-typeset table:not([class]) th { + border-bottom: 1px solid var(--md-default-fg-color); +} + +.md-typeset pre > code::-webkit-scrollbar-thumb { + background-color: hsla(0, 0%, 0%, 0.32); +} +.md-typeset pre > code::-webkit-scrollbar-thumb:hover { + background-color: hsla(0, 0%, 0%, 0.87); +} +`; From 49fe071b65eb45fb3d4deb215cfaadc4ad6b34c0 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 20 May 2022 15:39:12 +0200 Subject: [PATCH 06/16] feat(techdocs): extract animations rules Signed-off-by: Camila Belo --- .../transformers/styles/rules/animations.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 plugins/techdocs/src/reader/transformers/styles/rules/animations.ts diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/animations.ts b/plugins/techdocs/src/reader/transformers/styles/rules/animations.ts new file mode 100644 index 0000000000..0c50fb0633 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/rules/animations.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default () => ` +/*================== Animations ==================*/ +/* + Disable CSS animations on link colors as they lead to issues in dark mode. + The dark mode color theme is applied later and theirfore there is always an animation from light to dark mode when navigation between pages. +*/ +.md-dialog, .md-nav__link, .md-footer__link, .md-typeset a, .md-typeset a::before, .md-typeset .headerlink { + transition: none; +} +`; From 906568604ec9125072a4f71598ed52e7e0d0c5c6 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 20 May 2022 15:39:43 +0200 Subject: [PATCH 07/16] feat(techdocs): extract extensions rules Signed-off-by: Camila Belo --- .../transformers/styles/rules/extensions.ts | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 plugins/techdocs/src/reader/transformers/styles/rules/extensions.ts diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/extensions.ts b/plugins/techdocs/src/reader/transformers/styles/rules/extensions.ts new file mode 100644 index 0000000000..403858339e --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/rules/extensions.ts @@ -0,0 +1,91 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RuleOptions } from './types'; + +export default ({ theme }: RuleOptions) => ` +/*================== Extensions ==================*/ + +/* HIGHLIGHT */ +.highlight .md-clipboard:after { + content: unset; +} + +.highlight .nx { + color: ${theme.palette.type === 'dark' ? '#ff53a3' : '#ec407a'}; +} + +/* CODE HILITE */ +.codehilite .gd { + background-color: ${ + theme.palette.type === 'dark' ? 'rgba(248,81,73,0.65)' : '#fdd' + }; +} + +.codehilite .gi { + background-color: ${ + theme.palette.type === 'dark' ? 'rgba(46,160,67,0.65)' : '#dfd' + }; +} + +/* TABBED */ +.tabbed-set>input:nth-child(1):checked~.tabbed-labels>:nth-child(1), +.tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2), +.tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3), +.tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4), +.tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5), +.tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6), +.tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7), +.tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8), +.tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9), +.tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10), +.tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11), +.tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12), +.tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13), +.tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14), +.tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15), +.tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16), +.tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17), +.tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18), +.tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19), +.tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20) { + color: var(--md-accent-fg-color); + border-color: var(--md-accent-fg-color); +} + +/* TASK-LIST */ +.task-list-control .task-list-indicator::before { + background-color: ${theme.palette.action.disabledBackground}; +} +.task-list-control [type="checkbox"]:checked + .task-list-indicator:before { + background-color: ${theme.palette.success.main}; +} + +/* ADMONITION */ +.admonition { + font-size: var(--md-typeset-font-size) !important; +} +.admonition .admonition-title { + padding-left: 2.5rem !important; +} + +.admonition .admonition-title:before { + top: 50% !important; + width: 20px !important; + height: 20px !important; + transform: translateY(-50%) !important; +} +`; From cfa8b5be752d5b94578453033a8c09913fe6aedb Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 20 May 2022 15:40:31 +0200 Subject: [PATCH 08/16] feat(techdocs): sort style rules Signed-off-by: Camila Belo --- .../reader/transformers/styles/rules/rules.ts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 plugins/techdocs/src/reader/transformers/styles/rules/rules.ts diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/rules.ts b/plugins/techdocs/src/reader/transformers/styles/rules/rules.ts new file mode 100644 index 0000000000..917793733e --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/rules/rules.ts @@ -0,0 +1,38 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { default as variables } from './variables'; +import { default as reset } from './reset'; +import { default as layout } from './layout'; +import { default as typeset } from './typeset'; +import { default as animations } from './animations'; +import { default as extensions } from './extensions'; + +/** + * A list of style rules that will be applied to an element in the order they were added. + * + * @remarks + * The order of items is important, which means that a rule can override any other rule previously added to the list, + * i.e. the rules will be applied from the first added to the last added. + */ +export const rules = [ + variables, + reset, + layout, + typeset, + animations, + extensions, +]; From 0d969cc4dfe00a99ac3035402aa7f86923ce50fa Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 20 May 2022 15:40:56 +0200 Subject: [PATCH 09/16] feat(techdocs): export style rules Signed-off-by: Camila Belo --- .../reader/transformers/styles/rules/index.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 plugins/techdocs/src/reader/transformers/styles/rules/index.ts diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/index.ts b/plugins/techdocs/src/reader/transformers/styles/rules/index.ts new file mode 100644 index 0000000000..b41da508f0 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/rules/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './rules'; From b20cdd0efa3badccdd786f752b6f3a409c08a660 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 20 May 2022 15:41:37 +0200 Subject: [PATCH 10/16] feat(techdocs): create styles transformer Signed-off-by: Camila Belo --- .../reader/transformers/styles/transformer.ts | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 plugins/techdocs/src/reader/transformers/styles/transformer.ts diff --git a/plugins/techdocs/src/reader/transformers/styles/transformer.ts b/plugins/techdocs/src/reader/transformers/styles/transformer.ts new file mode 100644 index 0000000000..f16ee951c6 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/transformer.ts @@ -0,0 +1,61 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { useCallback, useContext, useMemo } from 'react'; + +import { useTheme } from '@material-ui/core'; + +import { SidebarPinStateContext } from '@backstage/core-components'; +import { BackstageTheme } from '@backstage/theme'; + +import { Transformer } from '..'; +import { rules } from './rules'; + +/** + * Sidebar pinned state to be used in computing style injections. + */ +const useSidebar = () => useContext(SidebarPinStateContext); + +/** + * Process all rules and concatenate their definitions into a single style. + * @returns a string containing all processed style definitions. + */ +const useRuleStyles = () => { + const sidebar = useSidebar(); + const theme = useTheme(); + + return useMemo(() => { + const options = { theme, sidebar }; + return rules.reduce((styles, rule) => styles + rule(options), ''); + }, [theme, sidebar]); +}; + +/** + * Returns a transformer that inserts all style rules into the given element's head tag. + */ +export const useStylesTransformer = (): Transformer => { + const styles = useRuleStyles(); + + return useCallback( + (dom: Element) => { + dom + .getElementsByTagName('head')[0] + .insertAdjacentHTML('beforeend', ``); + return dom; + }, + [styles], + ); +}; From c7e4026c948fd54a4902c8e114c408fa2a3bee74 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 20 May 2022 15:42:24 +0200 Subject: [PATCH 11/16] test(techdocs): cover styles transformer Signed-off-by: Camila Belo --- .../transformers/styles/transformer.test.ts | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 plugins/techdocs/src/reader/transformers/styles/transformer.test.ts diff --git a/plugins/techdocs/src/reader/transformers/styles/transformer.test.ts b/plugins/techdocs/src/reader/transformers/styles/transformer.test.ts new file mode 100644 index 0000000000..84ba16d6e6 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/transformer.test.ts @@ -0,0 +1,48 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { useStylesTransformer } from './transformer'; + +describe('Transformers > Styles', () => { + it('should return a function that injects all styles into a given dom element', () => { + const { result } = renderHook(() => useStylesTransformer()); + + const dom = document.createElement('html'); + dom.innerHTML = ''; + result.current(dom); // calling styles transformer + + const style = dom.querySelector('head > style'); + expect(style).toHaveTextContent( + '/*================== Variables ==================*/', + ); + expect(style).toHaveTextContent( + '/*================== Reset ==================*/', + ); + expect(style).toHaveTextContent( + '/*================== Layout ==================*/', + ); + expect(style).toHaveTextContent( + '/*================== Typeset ==================*/', + ); + expect(style).toHaveTextContent( + '/*================== Animations ==================*/', + ); + expect(style).toHaveTextContent( + '/*================== Extensions ==================*/', + ); + }); +}); From 4e42ccad35ad642d16f876a02192a9734c45dc2f Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 20 May 2022 15:42:47 +0200 Subject: [PATCH 12/16] feat(techdocs): export styles transformer Signed-off-by: Camila Belo --- .../techdocs/src/reader/transformers/index.ts | 1 + .../src/reader/transformers/styles/index.ts | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 plugins/techdocs/src/reader/transformers/styles/index.ts diff --git a/plugins/techdocs/src/reader/transformers/index.ts b/plugins/techdocs/src/reader/transformers/index.ts index bc72f2d78f..56846a51cd 100644 --- a/plugins/techdocs/src/reader/transformers/index.ts +++ b/plugins/techdocs/src/reader/transformers/index.ts @@ -15,6 +15,7 @@ */ export * from './html'; +export * from './styles'; export * from './addBaseUrl'; export * from './addGitFeedbackLink'; export * from './addSidebarToggle'; diff --git a/plugins/techdocs/src/reader/transformers/styles/index.ts b/plugins/techdocs/src/reader/transformers/styles/index.ts new file mode 100644 index 0000000000..50d5a6a368 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { useStylesTransformer } from './transformer'; From 307581d8b60bf53c7aa7bac2baca9b29495b50cb Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 20 May 2022 15:45:18 +0200 Subject: [PATCH 13/16] feat(techdocs): use styles transformer Signed-off-by: Camila Belo --- .../TechDocsReaderPageContent/dom.tsx | 561 +----------------- 1 file changed, 6 insertions(+), 555 deletions(-) diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx index 6a825a4788..9f561bfcd3 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx @@ -14,16 +14,14 @@ * limitations under the License. */ -import { useContext, useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { Theme, useTheme, useMediaQuery } from '@material-ui/core'; -import { lighten, alpha } from '@material-ui/core/styles'; +import { useTheme, useMediaQuery } from '@material-ui/core'; import { BackstageTheme } from '@backstage/theme'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { useApi } from '@backstage/core-plugin-api'; -import { SidebarPinStateContext } from '@backstage/core-components'; import { scmIntegrationsApiRef } from '@backstage/integration-react'; import { @@ -38,7 +36,6 @@ import { addGitFeedbackLink, addLinkClickListener, addSidebarToggle, - injectCss, onCssReady, removeMkdocsHeader, rewriteDocLinks, @@ -47,24 +44,11 @@ import { transform as transformer, copyToClipboard, useSanitizerTransformer, + useStylesTransformer, } from '../../transformers'; const MOBILE_MEDIA_QUERY = 'screen and (max-width: 76.1875em)'; -type TypographyHeadings = Pick< - Theme['typography'], - 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' ->; - -type TypographyHeadingsKeys = keyof TypographyHeadings; - -const headings: TypographyHeadingsKeys[] = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']; - -/** - * Sidebar pinned status to be used in computing CSS style injections - */ -const useSidebar = () => useContext(SidebarPinStateContext); - /** * Hook that encapsulates the behavior of getting raw HTML and applying * transforms to it in order to make it function at a basic level in the @@ -74,10 +58,10 @@ export const useTechDocsReaderDom = ( entityRef: CompoundEntityRef, ): Element | null => { const navigate = useNavigate(); - const sidebar = useSidebar(); const theme = useTheme(); const isMobileMedia = useMediaQuery(MOBILE_MEDIA_QUERY); const sanitizerTransformer = useSanitizerTransformer(); + const stylesTransformer = useStylesTransformer(); const techdocsStorageApi = useApi(techdocsStorageApiRef); const scmIntegrationsApi = useApi(scmIntegrationsApiRef); @@ -157,548 +141,15 @@ export const useTechDocsReaderDom = ( removeMkdocsHeader(), simplifyMkdocsFooter(), addGitFeedbackLink(scmIntegrationsApi), - injectCss({ - // Variables - css: ` - /* - As the MkDocs output is rendered in shadow DOM, the CSS variable definitions on the root selector are not applied. Instead, they have to be applied on :host. - As there is no way to transform the served main*.css yet (for example in the backend), we have to copy from main*.css and modify them. - */ - :host { - /* FONT */ - --md-default-fg-color: ${theme.palette.text.primary}; - --md-default-fg-color--light: ${theme.palette.text.secondary}; - --md-default-fg-color--lighter: ${lighten( - theme.palette.text.secondary, - 0.7, - )}; - --md-default-fg-color--lightest: ${lighten( - theme.palette.text.secondary, - 0.3, - )}; - - /* BACKGROUND */ - --md-default-bg-color:${theme.palette.background.default}; - --md-default-bg-color--light: ${theme.palette.background.paper}; - --md-default-bg-color--lighter: ${lighten( - theme.palette.background.paper, - 0.7, - )}; - --md-default-bg-color--lightest: ${lighten( - theme.palette.background.paper, - 0.3, - )}; - - /* PRIMARY */ - --md-primary-fg-color: ${theme.palette.primary.main}; - --md-primary-fg-color--light: ${theme.palette.primary.light}; - --md-primary-fg-color--dark: ${theme.palette.primary.dark}; - --md-primary-bg-color: ${theme.palette.primary.contrastText}; - --md-primary-bg-color--light: ${lighten( - theme.palette.primary.contrastText, - 0.7, - )}; - - /* ACCENT */ - --md-accent-fg-color: var(--md-primary-fg-color); - - /* SHADOW */ - --md-shadow-z1: ${theme.shadows[1]}; - --md-shadow-z2: ${theme.shadows[2]}; - --md-shadow-z3: ${theme.shadows[3]}; - - /* EXTENSIONS */ - --md-admonition-fg-color: var(--md-default-fg-color); - --md-admonition-bg-color: var(--md-default-bg-color); - /* Admonitions and others are using SVG masks to define icons. These masks are defined as CSS variables. */ - --md-admonition-icon--note: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--abstract: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--info: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--tip: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--success: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--question: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--warning: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--failure: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--danger: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--bug: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--example: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--quote: url('data:image/svg+xml;charset=utf-8,'); - --md-footnotes-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-details-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-tasklist-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-tasklist-icon--checked: url('data:image/svg+xml;charset=utf-8,'); - --md-nav-icon--prev: url('data:image/svg+xml;charset=utf-8,'); - --md-nav-icon--next: url('data:image/svg+xml;charset=utf-8,'); - --md-toc-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-clipboard-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-search-result-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-source-forks-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-source-repositories-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-source-stars-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-source-version-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-version-icon: url('data:image/svg+xml;charset=utf-8,'); - } - - :host > * { - /* CODE */ - --md-code-fg-color: ${theme.palette.text.primary}; - --md-code-bg-color: ${theme.palette.background.paper}; - --md-code-hl-color: ${alpha(theme.palette.warning.main, 0.5)}; - --md-code-hl-keyword-color: ${ - theme.palette.type === 'dark' - ? theme.palette.primary.light - : theme.palette.primary.dark - }; - --md-code-hl-function-color: ${ - theme.palette.type === 'dark' - ? theme.palette.secondary.light - : theme.palette.secondary.dark - }; - --md-code-hl-string-color: ${ - theme.palette.type === 'dark' - ? theme.palette.success.light - : theme.palette.success.dark - }; - --md-code-hl-number-color: ${ - theme.palette.type === 'dark' - ? theme.palette.error.light - : theme.palette.error.dark - }; - --md-code-hl-constant-color: var(--md-code-hl-function-color); - --md-code-hl-special-color: var(--md-code-hl-function-color); - --md-code-hl-name-color: var(--md-code-fg-color); - --md-code-hl-comment-color: var(--md-default-fg-color--light); - --md-code-hl-generic-color: var(--md-default-fg-color--light); - --md-code-hl-variable-color: var(--md-default-fg-color--light); - --md-code-hl-operator-color: var(--md-default-fg-color--light); - --md-code-hl-punctuation-color: var(--md-default-fg-color--light); - - /* TYPESET */ - --md-typeset-font-size: 1rem; - --md-typeset-color: var(--md-default-fg-color); - --md-typeset-a-color: var(--md-accent-fg-color); - --md-typeset-table-color: ${theme.palette.text.primary}; - --md-typeset-del-color: ${ - theme.palette.type === 'dark' - ? alpha(theme.palette.error.dark, 0.5) - : alpha(theme.palette.error.light, 0.5) - }; - --md-typeset-ins-color: ${ - theme.palette.type === 'dark' - ? alpha(theme.palette.success.dark, 0.5) - : alpha(theme.palette.success.light, 0.5) - }; - --md-typeset-mark-color: ${ - theme.palette.type === 'dark' - ? alpha(theme.palette.warning.dark, 0.5) - : alpha(theme.palette.warning.light, 0.5) - }; - } - - @media screen and (max-width: 76.1875em) { - :host > * { - /* TYPESET */ - --md-typeset-font-size: .9rem; - } - } - - @media screen and (max-width: 600px) { - :host > * { - /* TYPESET */ - --md-typeset-font-size: .7rem; - } - } - `, - }), - injectCss({ - // Reset - css: ` - body { - --md-text-color: var(--md-default-fg-color); - --md-text-link-color: var(--md-accent-fg-color); - --md-text-font-family: ${theme.typography.fontFamily}; - font-family: var(--md-text-font-family); - background-color: unset; - } - `, - }), - injectCss({ - // Layout - css: ` - .md-grid { - max-width: 100%; - margin: 0; - } - - .md-nav { - font-size: calc(var(--md-typeset-font-size) * 0.9); - } - .md-nav__link { - display: flex; - align-items: center; - justify-content: space-between; - } - .md-nav__icon { - height: 20px !important; - width: 20px !important; - margin-left:${theme.spacing(1)}px; - } - .md-nav__icon svg { - margin: 0; - width: 20px !important; - height: 20px !important; - } - .md-nav__icon:after { - width: 20px !important; - height: 20px !important; - } - - .md-main__inner { - margin-top: 0; - } - - .md-sidebar { - bottom: 75px; - position: fixed; - width: 16rem; - overflow-y: auto; - overflow-x: hidden; - scrollbar-color: rgb(193, 193, 193) #eee; - scrollbar-width: thin; - } - .md-sidebar .md-sidebar__scrollwrap { - width: calc(16rem - 10px); - } - .md-sidebar--secondary { - right: ${theme.spacing(3)}px; - } - .md-sidebar::-webkit-scrollbar { - width: 5px; - } - .md-sidebar::-webkit-scrollbar-button { - width: 5px; - height: 5px; - } - .md-sidebar::-webkit-scrollbar-track { - background: #eee; - border: 1 px solid rgb(250, 250, 250); - box-shadow: 0px 0px 3px #dfdfdf inset; - border-radius: 3px; - } - .md-sidebar::-webkit-scrollbar-thumb { - width: 5px; - background: rgb(193, 193, 193); - border: transparent; - border-radius: 3px; - } - .md-sidebar::-webkit-scrollbar-thumb:hover { - background: rgb(125, 125, 125); - } - - .md-content { - max-width: calc(100% - 16rem * 2); - margin-left: 16rem; - margin-bottom: 50px; - } - - .md-footer { - position: fixed; - bottom: 0px; - } - .md-footer__title { - background-color: unset; - } - .md-footer-nav__link { - width: 16rem; - } - - .md-dialog { - background-color: unset; - } - - @media screen and (min-width: 76.25em) { - .md-sidebar { - height: auto; - } - } - - @media screen and (max-width: 76.1875em) { - .md-nav { - transition: none !important; - background-color: var(--md-default-bg-color) - } - .md-nav--primary .md-nav__title { - cursor: auto; - color: var(--md-default-fg-color); - font-weight: 700; - white-space: normal; - line-height: 1rem; - height: auto; - display: flex; - flex-flow: column; - row-gap: 1.6rem; - padding: 1.2rem .8rem .8rem; - background-color: var(--md-default-bg-color); - } - .md-nav--primary .md-nav__title~.md-nav__list { - box-shadow: none; - } - .md-nav--primary .md-nav__title ~ .md-nav__list > :first-child { - border-top: none; - } - .md-nav--primary .md-nav__title .md-nav__button { - display: none; - } - .md-nav--primary .md-nav__title .md-nav__icon { - color: var(--md-default-fg-color); - position: static; - height: auto; - margin: 0 0 0 -0.2rem; - } - .md-nav--primary > .md-nav__title [for="none"] { - padding-top: 0; - } - .md-nav--primary .md-nav__item { - border-top: none; - } - .md-nav--primary :is(.md-nav__title,.md-nav__item) { - font-size : var(--md-typeset-font-size); - } - .md-nav .md-source { - display: none; - } - - .md-sidebar { - height: 100%; - } - .md-sidebar--primary { - width: 12.1rem !important; - z-index: 200; - left: ${ - sidebar.isPinned - ? 'calc(-12.1rem + 242px)' - : 'calc(-12.1rem + 72px)' - } !important; - } - .md-sidebar--secondary:not([hidden]) { - display: none; - } - - .md-content { - max-width: 100%; - margin-left: 0; - } - - .md-header__button { - margin: 0.4rem 0; - margin-left: 0.4rem; - padding: 0; - } - - .md-overlay { - left: 0; - } - - .md-footer { - position: static; - padding-left: 0; - } - .md-footer-nav__link { - /* footer links begin to overlap at small sizes without setting width */ - width: 50%; - } - } - - @media screen and (max-width: 600px) { - .md-sidebar--primary { - left: -12.1rem !important; - width: 12.1rem; - } - } - `, - }), - injectCss({ - // Typeset - css: ` - .md-typeset { - font-size: var(--md-typeset-font-size); - } - - ${headings.reduce((style, heading) => { - const styles = theme.typography[heading]; - const { lineHeight, fontFamily, fontWeight, fontSize } = styles; - const calculate = (value: typeof fontSize) => { - let factor: number | string = 1; - if (typeof value === 'number') { - // 60% of the size defined because it is too big - factor = (value / 16) * 0.6; - } - if (typeof value === 'string') { - factor = value.replace('rem', ''); - } - return `calc(${factor} * var(--md-typeset-font-size))`; - }; - return style.concat(` - .md-typeset ${heading} { - color: var(--md-default-fg-color); - line-height: ${lineHeight}; - font-family: ${fontFamily}; - font-weight: ${fontWeight}; - font-size: ${calculate(fontSize)}; - } - `); - }, '')} - - .md-typeset .md-content__button { - color: var(--md-default-fg-color); - } - - .md-typeset hr { - border-bottom: 0.05rem dotted ${theme.palette.divider}; - } - - .md-typeset details { - font-size: var(--md-typeset-font-size) !important; - } - .md-typeset details summary { - padding-left: 2.5rem !important; - } - .md-typeset details summary:before, - .md-typeset details summary:after { - top: 50% !important; - width: 20px !important; - height: 20px !important; - transform: rotate(0deg) translateY(-50%) !important; - } - .md-typeset details[open] > summary:after { - transform: rotate(90deg) translateX(-50%) !important; - } - - .md-typeset blockquote { - color: var(--md-default-fg-color--light); - border-left: 0.2rem solid var(--md-default-fg-color--light); - } - - .md-typeset table:not([class]) { - font-size: var(--md-typeset-font-size); - border: 1px solid var(--md-default-fg-color); - border-bottom: none; - border-collapse: collapse; - } - .md-typeset table:not([class]) th { - font-weight: bold; - } - .md-typeset table:not([class]) td, .md-typeset table:not([class]) th { - border-bottom: 1px solid var(--md-default-fg-color); - } - - .md-typeset pre > code::-webkit-scrollbar-thumb { - background-color: hsla(0, 0%, 0%, 0.32); - } - .md-typeset pre > code::-webkit-scrollbar-thumb:hover { - background-color: hsla(0, 0%, 0%, 0.87); - } - `, - }), - injectCss({ - // Animations - css: ` - /* - Disable CSS animations on link colors as they lead to issues in dark mode. - The dark mode color theme is applied later and theirfore there is always an animation from light to dark mode when navigation between pages. - */ - .md-dialog, .md-nav__link, .md-footer__link, .md-typeset a, .md-typeset a::before, .md-typeset .headerlink { - transition: none; - } - `, - }), - injectCss({ - // Extensions - css: ` - /* HIGHLIGHT */ - .highlight .md-clipboard:after { - content: unset; - } - - .highlight .nx { - color: ${theme.palette.type === 'dark' ? '#ff53a3' : '#ec407a'}; - } - - /* CODE HILITE */ - .codehilite .gd { - background-color: ${ - theme.palette.type === 'dark' - ? 'rgba(248,81,73,0.65)' - : '#fdd' - }; - } - - .codehilite .gi { - background-color: ${ - theme.palette.type === 'dark' - ? 'rgba(46,160,67,0.65)' - : '#dfd' - }; - } - - /* TABBED */ - .tabbed-set>input:nth-child(1):checked~.tabbed-labels>:nth-child(1), - .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2), - .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3), - .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4), - .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5), - .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6), - .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7), - .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8), - .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9), - .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10), - .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11), - .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12), - .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13), - .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14), - .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15), - .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16), - .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17), - .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18), - .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19), - .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20) { - color: var(--md-accent-fg-color); - border-color: var(--md-accent-fg-color); - } - - /* TASK-LIST */ - .task-list-control .task-list-indicator::before { - background-color: ${theme.palette.action.disabledBackground}; - } - .task-list-control [type="checkbox"]:checked + .task-list-indicator:before { - background-color: ${theme.palette.success.main}; - } - - /* ADMONITION */ - .admonition { - font-size: var(--md-typeset-font-size) !important; - } - .admonition .admonition-title { - padding-left: 2.5rem !important; - } - - .admonition .admonition-title:before { - top: 50% !important; - width: 20px !important; - height: 20px !important; - transform: translateY(-50%) !important; - } - `, - }), + stylesTransformer, ]), [ // only add dependencies that are in state or memorized variables to avoid unnecessary calls between re-renders entityRef, - theme, - sidebar, scmIntegrationsApi, techdocsStorageApi, sanitizerTransformer, + stylesTransformer, ], ); From a805d841af1f23be1b94461d7db3410010159a46 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 20 May 2022 15:45:52 +0200 Subject: [PATCH 14/16] refactor(techdocs): delete inject css Signed-off-by: Camila Belo --- .../techdocs/src/reader/transformers/index.ts | 1 - .../src/reader/transformers/injectCss.test.ts | 40 ------------------- .../src/reader/transformers/injectCss.ts | 31 -------------- 3 files changed, 72 deletions(-) delete mode 100644 plugins/techdocs/src/reader/transformers/injectCss.test.ts delete mode 100644 plugins/techdocs/src/reader/transformers/injectCss.ts diff --git a/plugins/techdocs/src/reader/transformers/index.ts b/plugins/techdocs/src/reader/transformers/index.ts index 56846a51cd..dc3c43a584 100644 --- a/plugins/techdocs/src/reader/transformers/index.ts +++ b/plugins/techdocs/src/reader/transformers/index.ts @@ -25,6 +25,5 @@ export * from './copyToClipboard'; export * from './removeMkdocsHeader'; export * from './simplifyMkdocsFooter'; export * from './onCssReady'; -export * from './injectCss'; export * from './scrollIntoAnchor'; export * from './transformer'; diff --git a/plugins/techdocs/src/reader/transformers/injectCss.test.ts b/plugins/techdocs/src/reader/transformers/injectCss.test.ts deleted file mode 100644 index 6d0eb8daa9..0000000000 --- a/plugins/techdocs/src/reader/transformers/injectCss.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { createTestShadowDom } from '../../test-utils'; -import { injectCss } from './injectCss'; - -describe('injectCss', () => { - it('should inject style with passed css in head', async () => { - const html = ` - - - - - `; - const injectedCss = '* {background-color: #fff}'; - - const shadowDom = await createTestShadowDom(html, { - preTransformers: [injectCss({ css: injectedCss })], - postTransformers: [], - }); - - const styleElement = shadowDom.querySelector('head > style'); - - expect(styleElement).toBeTruthy(); - expect(styleElement!.innerHTML).toEqual(injectedCss); - }); -}); diff --git a/plugins/techdocs/src/reader/transformers/injectCss.ts b/plugins/techdocs/src/reader/transformers/injectCss.ts deleted file mode 100644 index c847d3e1d8..0000000000 --- a/plugins/techdocs/src/reader/transformers/injectCss.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { Transformer } from './transformer'; - -type InjectCssOptions = { - css: string; -}; - -export const injectCss = ({ css }: InjectCssOptions): Transformer => { - return dom => { - dom - .getElementsByTagName('head')[0] - .insertAdjacentHTML('beforeend', ``); - - return dom; - }; -}; From 17c059dfd0ff93168955d1b43b28e59bf08dc04a Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 20 May 2022 16:13:01 +0200 Subject: [PATCH 15/16] chore: add changeset file Signed-off-by: Camila Belo --- .changeset/techdocs-crabs-retire.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .changeset/techdocs-crabs-retire.md diff --git a/.changeset/techdocs-crabs-retire.md b/.changeset/techdocs-crabs-retire.md new file mode 100644 index 0000000000..b15c983bc4 --- /dev/null +++ b/.changeset/techdocs-crabs-retire.md @@ -0,0 +1,9 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Restructures reader style transformations to improve code readability: + +- Extracts the style rules to separate files; +- Creates a hook that processes each rule; +- And creates another hook that returns a transformer responsible for injecting them into the head tag of a given element. From b862e974cbdcbab2de6c6d947f6a4709fad19b27 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Mon, 23 May 2022 14:53:35 +0200 Subject: [PATCH 16/16] refactor(techdocs): apply review suggestions Signed-off-by: Camila Belo --- plugins/techdocs/src/reader/transformers/styles/transformer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/techdocs/src/reader/transformers/styles/transformer.ts b/plugins/techdocs/src/reader/transformers/styles/transformer.ts index f16ee951c6..26ab4ceb26 100644 --- a/plugins/techdocs/src/reader/transformers/styles/transformer.ts +++ b/plugins/techdocs/src/reader/transformers/styles/transformer.ts @@ -21,7 +21,7 @@ import { useTheme } from '@material-ui/core'; import { SidebarPinStateContext } from '@backstage/core-components'; import { BackstageTheme } from '@backstage/theme'; -import { Transformer } from '..'; +import { Transformer } from '../transformer'; import { rules } from './rules'; /**