diff --git a/.changeset/techdocs-moose-design.md b/.changeset/techdocs-moose-design.md
new file mode 100644
index 0000000000..c7c397ad4c
--- /dev/null
+++ b/.changeset/techdocs-moose-design.md
@@ -0,0 +1,20 @@
+---
+'@backstage/plugin-techdocs': minor
+---
+
+Adjust the Tech Docs page theme as a side effect of the `mkdocs-material` theme update.
+
+If you use the `spofify/techdocs` image to build your documentation, make sure you use version `spotify/techdocs:v0.3.7`.
+
+**Breaking**: The `PyMdown` extensions have also been updated and some syntax may have changed, so it is recommended that you check the extension's documentation if something stops working.
+For example, the syntax of tags below was deprecated in `PyMdown` extensions `v.7.0` and in `v.8.0.0` it has been removed. This means that the old syntax specified below no longer works.
+
+````markdown
+```markdown tab="tab"
+This is some markdown
+```
+
+```markdown tab="tab 2"
+This is some markdown in tab 2
+```
+````
diff --git a/packages/techdocs-cli-embedded-app/src/components/Root/Root.tsx b/packages/techdocs-cli-embedded-app/src/components/Root/Root.tsx
index 65e75a488a..249394fdc4 100644
--- a/packages/techdocs-cli-embedded-app/src/components/Root/Root.tsx
+++ b/packages/techdocs-cli-embedded-app/src/components/Root/Root.tsx
@@ -14,18 +14,20 @@
* limitations under the License.
*/
-import React, { useContext, PropsWithChildren } from 'react';
+import React, { PropsWithChildren, useContext } from 'react';
+
import { Link, makeStyles } from '@material-ui/core';
import LibraryBooks from '@material-ui/icons/LibraryBooks';
import LogoFull from './LogoFull';
import LogoIcon from './LogoIcon';
+
import {
Sidebar,
+ SidebarItem,
SidebarPage,
sidebarConfig,
- SidebarContext,
- SidebarItem,
SidebarDivider,
+ SidebarContext,
} from '@backstage/core-components';
import { NavLink } from 'react-router-dom';
@@ -70,7 +72,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => (
{/* Global nav, not org-specific */}
{/* End global nav */}
diff --git a/packages/techdocs-cli-embedded-app/src/components/TechDocsPage/TechDocsPage.tsx b/packages/techdocs-cli-embedded-app/src/components/TechDocsPage/TechDocsPage.tsx
index b90a49fb22..d9a518c165 100644
--- a/packages/techdocs-cli-embedded-app/src/components/TechDocsPage/TechDocsPage.tsx
+++ b/packages/techdocs-cli-embedded-app/src/components/TechDocsPage/TechDocsPage.tsx
@@ -14,7 +14,22 @@
* limitations under the License.
*/
-import React from 'react';
+import React, {
+ FC,
+ createContext,
+ useContext,
+ useState,
+ useCallback,
+} from 'react';
+
+import { Theme, makeStyles } from '@material-ui/core';
+
+import { ThemeProvider, Box, Tooltip, IconButton } from '@material-ui/core';
+import LightIcon from '@material-ui/icons/Brightness7';
+import DarkIcon from '@material-ui/icons/Brightness4';
+
+import { lightTheme, darkTheme } from '@backstage/theme';
+import { EntityName } from '@backstage/catalog-model';
import { Content } from '@backstage/core-components';
@@ -24,6 +39,106 @@ import {
TechDocsPageHeader,
} from '@backstage/plugin-techdocs';
+const useStyles = makeStyles((theme: Theme) => ({
+ headerIcon: {
+ color: theme.palette.common.white,
+ width: '32px',
+ height: '32px',
+ },
+ content: {
+ backgroundColor: theme.palette.background.default,
+ },
+ contentToolbar: {
+ display: 'flex',
+ justifyContent: 'flex-end',
+ padding: 0,
+ },
+}));
+
+enum Themes {
+ LIGHT = 'light',
+ DARK = 'dark',
+}
+
+type TechDocsThemeValue = {
+ theme: Themes;
+ toggleTheme: () => void;
+};
+
+const TechDocsThemeContext = createContext({
+ theme: Themes.LIGHT,
+ toggleTheme: () => {},
+});
+
+const TechdocsThemeProvider: FC = ({ children }) => {
+ const [theme, setTheme] = useState(Themes.LIGHT);
+
+ const toggleTheme = useCallback(() => {
+ setTheme(prevTheme =>
+ prevTheme === Themes.LIGHT ? Themes.DARK : Themes.LIGHT,
+ );
+ }, [setTheme]);
+
+ const value = { theme, toggleTheme };
+
+ const themes = {
+ [Themes.LIGHT]: lightTheme,
+ [Themes.DARK]: darkTheme,
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+const useTechDocsTheme = () => useContext(TechDocsThemeContext);
+
+const TechDocsThemeToggle = () => {
+ const classes = useStyles();
+ const { theme, toggleTheme } = useTechDocsTheme();
+
+ const themes = {
+ [Themes.LIGHT]: {
+ icon: DarkIcon,
+ title: 'Dark theme',
+ },
+ [Themes.DARK]: {
+ icon: LightIcon,
+ title: 'Light theme',
+ },
+ };
+
+ const { title, icon: Icon } = themes[theme];
+
+ return (
+
+
+
+
+
+
+
+ );
+};
+
+const TechDocsPageContent = ({
+ onReady,
+ entityRef,
+}: {
+ entityRef: EntityName;
+ onReady: () => void;
+}) => {
+ const classes = useStyles();
+
+ return (
+
+
+
+ );
+};
+
const DefaultTechDocsPage = () => {
const techDocsMetadata = {
site_name: 'Live preview environment',
@@ -35,20 +150,20 @@ const DefaultTechDocsPage = () => {
{({ entityRef, onReady }) => (
<>
-
-
-
+ techDocsMetadata={techDocsMetadata}
+ >
+
+
+
>
)}
);
};
-export const techDocsPage = ;
+export const techDocsPage = (
+
+
+
+);
diff --git a/packages/techdocs-cli/README.md b/packages/techdocs-cli/README.md
index 3ac423cb38..db0ef2dc40 100644
--- a/packages/techdocs-cli/README.md
+++ b/packages/techdocs-cli/README.md
@@ -17,7 +17,7 @@ bundle into the `packages/techdocs-cli/dist` which is then published with the
```sh
# From the root of this repository run
# NOTE: This will build the techdocs-cli-embedded-app and copy the output into the cli dist directory
-yarn build --scope @techdocs/cli
+yarn workspace @techdocs/cli build
# Now execute the binary
packages/techdocs-cli/bin/techdocs-cli
diff --git a/packages/techdocs-common/api-report.md b/packages/techdocs-common/api-report.md
index 281accca30..f189a85c0c 100644
--- a/packages/techdocs-common/api-report.md
+++ b/packages/techdocs-common/api-report.md
@@ -227,7 +227,7 @@ export class TechdocsGenerator implements GeneratorBase {
config: Config;
scmIntegrations: ScmIntegrationRegistry;
});
- static readonly defaultDockerImage = 'spotify/techdocs:v0.3.6';
+ static readonly defaultDockerImage = 'spotify/techdocs:v0.3.7';
// (undocumented)
static fromConfig(
config: Config,
diff --git a/packages/techdocs-common/src/stages/generate/techdocs.ts b/packages/techdocs-common/src/stages/generate/techdocs.ts
index 5cf992092c..99554a6218 100644
--- a/packages/techdocs-common/src/stages/generate/techdocs.ts
+++ b/packages/techdocs-common/src/stages/generate/techdocs.ts
@@ -44,7 +44,7 @@ export class TechdocsGenerator implements GeneratorBase {
* The default docker image (and version) used to generate content. Public
* and static so that techdocs-common consumers can use the same version.
*/
- public static readonly defaultDockerImage = 'spotify/techdocs:v0.3.6';
+ public static readonly defaultDockerImage = 'spotify/techdocs:v0.3.7';
private readonly logger: Logger;
private readonly containerRunner: ContainerRunner;
private readonly options: GeneratorConfig;
diff --git a/plugins/techdocs/api-report.md b/plugins/techdocs/api-report.md
index 9459e616a1..768363f8fb 100644
--- a/plugins/techdocs/api-report.md
+++ b/plugins/techdocs/api-report.md
@@ -16,6 +16,7 @@ import { EntityName } from '@backstage/catalog-model';
import { FetchApi } from '@backstage/core-plugin-api';
import { IdentityApi } from '@backstage/core-plugin-api';
import { LocationSpec } from '@backstage/catalog-model';
+import { PropsWithChildren } from 'react';
import { default as React_2 } from 'react';
import { RouteRef } from '@backstage/core-plugin-api';
import { TableColumn } from '@backstage/core-components';
@@ -263,16 +264,17 @@ export const TechDocsPageHeader: ({
entityRef,
entityMetadata,
techDocsMetadata,
+ children,
}: TechDocsPageHeaderProps) => JSX.Element;
// Warning: (ae-missing-release-tag) "TechDocsPageHeaderProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export type TechDocsPageHeaderProps = {
+export type TechDocsPageHeaderProps = PropsWithChildren<{
entityRef: EntityName;
entityMetadata?: TechDocsEntityMetadata;
techDocsMetadata?: TechDocsMetadata;
-};
+}>;
// Warning: (ae-missing-release-tag) "TechDocsPageProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx
index c12b17b486..a8ee52740e 100644
--- a/plugins/techdocs/src/reader/components/Reader.tsx
+++ b/plugins/techdocs/src/reader/components/Reader.tsx
@@ -25,7 +25,14 @@ import React, {
useState,
} from 'react';
import { useNavigate, useParams } from 'react-router-dom';
-import { Grid, makeStyles, useTheme } from '@material-ui/core';
+import {
+ Grid,
+ makeStyles,
+ useTheme,
+ Theme,
+ lighten,
+ alpha,
+} from '@material-ui/core';
import { EntityName } from '@backstage/catalog-model';
import { useApi, configApiRef } from '@backstage/core-plugin-api';
@@ -62,8 +69,8 @@ type Props = {
const useStyles = makeStyles(theme => ({
searchBar: {
- marginLeft: '20rem',
- maxWidth: 'calc(100% - 20rem * 2 - 3rem)',
+ marginLeft: '16rem',
+ maxWidth: 'calc(100% - 16rem * 2)',
marginTop: theme.spacing(1),
'@media screen and (max-width: 76.1875em)': {
marginLeft: '10rem',
@@ -121,6 +128,15 @@ export const withTechDocsReaderProvider =
*/
export const useTechDocsReader = () => useContext(TechDocsReaderContext);
+type TypographyHeadings = Pick<
+ Theme['typography'],
+ 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
+>;
+
+type TypographyHeadingsKeys = keyof TypographyHeadings;
+
+const headings: TypographyHeadingsKeys[] = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
+
/**
* 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
@@ -209,185 +225,469 @@ export const useTechDocsReaderDom = (entityRef: EntityName): Element | null => {
simplifyMkdocsFooter(),
addGitFeedbackLink(scmIntegrationsApi),
injectCss({
+ // Variables
css: `
- body {
- font-family: ${theme.typography.fontFamily};
- --md-text-color: ${theme.palette.text.primary};
- --md-text-link-color: ${theme.palette.primary.main};
+ /*
+ 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,
+ )};
- --md-code-fg-color: ${theme.palette.text.primary};
- --md-code-bg-color: ${theme.palette.background.paper};
- --md-accent-fg-color: ${theme.palette.primary.main};
- --md-default-fg-color--lightest: ${theme.palette.textVerySubtle};
- }
- .codehilite .gd, .highlight .gd {
- background-color: ${isDarkTheme ? 'rgba(248,81,73,0.65)' : '#fdd'};
- }
- .codehilite .gi, .highlight .gi {
- background-color: ${isDarkTheme ? 'rgba(46,160,67,0.65)' : '#dfd'};
- }
- .highlight .kd {
- color: ${isDarkTheme ? '#4aaaf7' : '#3b78e7'};
- }
- .highlight .k {
- color: ${isDarkTheme ? '#4aaaf7' : '#3b78e7'};
- }
- .highlight .nx {
- color: ${isDarkTheme ? '#ff53a3' : '#ec407a'};
- }
- .highlight .s1 {
- color: ${isDarkTheme ? '#1cad46' : 'rgb(13, 144, 79)'};
- }
- .highlight .kt {
- color: ${isDarkTheme ? '#4aaaf7' : '#3e61a2'};
- }
- .md-main__inner { margin-top: 0; }
- .md-sidebar { position: fixed; bottom: 100px; width: 20rem; }
- .md-sidebar--secondary { right: 2rem; }
- .md-content { margin-bottom: 50px }
- .md-footer { position: fixed; bottom: 0px; }
- .md-footer-nav__link { width: 20rem;}
- .md-content { margin-left: 20rem; max-width: calc(100% - 20rem * 2 - 3rem); }
- .md-typeset { font-size: 1rem; }
- .md-typeset h1, .md-typeset h2, .md-typeset h3 { font-weight: bold; }
- .md-nav { font-size: 1rem; }
- .md-grid { max-width: 90vw; margin: 0 }
- .md-typeset blockquote {
- color: ${theme.palette.textSubtle};
- border-left: 0.2rem solid ${theme.palette.textVerySubtle};
- }
- .md-typeset hr {
- border-bottom: 0.05rem dotted ${theme.palette.textVerySubtle};
- }
- .md-typeset table:not([class]) {
- font-size: 1rem;
- border: 1px solid ${theme.palette.text.primary};
- border-bottom: none;
- border-collapse: collapse;
- }
- .md-typeset table:not([class]) td, .md-typeset table:not([class]) th {
- border-bottom: 1px solid ${theme.palette.text.primary};
- }
- .md-typeset table:not([class]) th { font-weight: bold; }
- .md-typeset .admonition, .md-typeset details {
- font-size: inherit;
+ /* 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,');
}
- /* style the checkmarks of the task list */
- .md-typeset .task-list-control .task-list-indicator::before {
- background-color: ${theme.palette.action.disabledBackground};
+ :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: ${
+ isDarkTheme
+ ? theme.palette.primary.light
+ : theme.palette.primary.dark
+ };
+ --md-code-hl-function-color: ${
+ isDarkTheme
+ ? theme.palette.secondary.light
+ : theme.palette.secondary.dark
+ };
+ --md-code-hl-string-color: ${
+ isDarkTheme
+ ? theme.palette.success.light
+ : theme.palette.success.dark
+ };
+ --md-code-hl-number-color: ${
+ isDarkTheme ? 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: ${
+ isDarkTheme
+ ? alpha(theme.palette.error.dark, 0.5)
+ : alpha(theme.palette.error.light, 0.5)
+ };
+ --md-typeset-ins-color: ${
+ isDarkTheme
+ ? alpha(theme.palette.success.dark, 0.5)
+ : alpha(theme.palette.success.light, 0.5)
+ };
+ --md-typeset-mark-color: ${
+ isDarkTheme
+ ? alpha(theme.palette.warning.dark, 0.5)
+ : alpha(theme.palette.warning.light, 0.5)
+ };
}
- .md-typeset .task-list-control [type="checkbox"]:checked + .task-list-indicator:before {
- background-color: ${theme.palette.success.main};
- }
- /**/
@media screen and (max-width: 76.1875em) {
- .md-nav {
- background-color: ${theme.palette.background.default};
- transition: none !important
- }
- .md-sidebar--secondary { display: none; }
- .md-sidebar--primary { left: ${
- isPinned ? '242px' : '72px'
- }; width: 10rem }
- .md-content { margin-left: 10rem; max-width: calc(100% - 10rem); }
- .md-content__inner { font-size: 0.9rem }
- .md-footer {
- position: static;
- padding-left: 10rem;
- }
- .md-footer-nav__link {
- /* footer links begin to overlap at small sizes without setting width */
- width: 50%;
- }
- .md-nav--primary .md-nav__title {
- white-space: normal;
- height: auto;
- line-height: 1rem;
- cursor: auto;
- }
- .md-nav--primary > .md-nav__title [for="none"] {
- padding-top: 0;
+ :host > * {
+ /* TYPESET */
+ --md-typeset-font-size: .9rem;
}
}
- `,
+
+ @media screen and (max-width: 600px) {
+ :host > * {
+ /* TYPESET */
+ --md-typeset-font-size: .7rem;
+ }
+ }
+ `,
}),
injectCss({
- // 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.
+ // Reset
css: `
- .md-nav__link, .md-typeset a, .md-typeset a::before, .md-typeset .headerlink {
- transition: none;
+ 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({
- // Properly style code blocks.
+ // Layout
css: `
+ .md-grid {
+ max-width: 90vw;
+ margin: 0;
+ }
+
+ .md-nav {
+ font-size: calc(var(--md-typeset-font-size) * 0.9);
+ }
+ .md-nav__icon {
+ width: auto !important;
+ height: auto !important;
+ }
+ .md-nav__icon:after {
+ width: 20px !important;
+ height: 20px !important;
+ }
+
+ .md-main__inner {
+ margin-top: 0;
+ }
+
+ .md-sidebar {
+ position: fixed;
+ bottom: 100px;
+ width: 16rem;
+ }
+ .md-sidebar--secondary {
+ right: ${theme.spacing(3)}px;
+ }
+
+ .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 (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-sidebar--primary {
+ width: 10rem !important;
+ left: ${isPinned ? '242px' : '72px'} !important;
+ }
+ .md-sidebar--secondary:not([hidden]) {
+ display: none;
+ }
+
+ .md-content {
+ max-width: calc(100% - 10rem);
+ margin-left: 10rem;
+ }
+
+ .md-footer {
+ position: static;
+ padding-left: 10rem;
+ }
+ .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: 1rem !important;
+ }
+ }
+ `,
+ }),
+ 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 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({
- // Admonitions and others are using SVG masks to define icons. These
- // masks are defined as CSS variables.
- // As the MkDocs output is rendered in shadow DOM, the CSS variable
- // definitions on the root selector are not applied. Instead, the 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.
+ // Animations
css: `
- :host {
- --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,');
+ /*
+ 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;
}
- :host {
- --md-footnotes-icon: url('data:image/svg+xml;charset=utf-8,');
+ `,
+ }),
+ injectCss({
+ // Extensions
+ css: `
+ /* HIGHLIGHT */
+ .highlight .md-clipboard:after {
+ content: unset;
}
- :host {
- --md-details-icon: url('data:image/svg+xml;charset=utf-8,');
+
+ .highlight .nx {
+ color: ${isDarkTheme ? '#ff53a3' : '#ec407a'};
}
- :host {
- --md-tasklist-icon: url('data:image/svg+xml;charset=utf-8,');
- --md-tasklist-icon--checked: url('data:image/svg+xml;charset=utf-8,');
+
+ /* CODE HILITE */
+ .codehilite .gd {
+ background-color: ${
+ isDarkTheme ? 'rgba(248,81,73,0.65)' : '#fdd'
+ };
}
- `,
+
+ .codehilite .gi {
+ background-color: ${
+ isDarkTheme ? '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;
+ }
+ `,
}),
]),
[
- techdocsSanitizer,
- techdocsStorageApi,
kind,
name,
namespace,
scmIntegrationsApi,
- theme.typography.fontFamily,
- theme.palette.text.primary,
- theme.palette.primary.main,
- theme.palette.background.paper,
- theme.palette.background.default,
- theme.palette.textVerySubtle,
- theme.palette.textSubtle,
- theme.palette.action.disabledBackground,
- theme.palette.success.main,
+ techdocsSanitizer,
+ techdocsStorageApi,
+ theme,
isDarkTheme,
isPinned,
],
diff --git a/plugins/techdocs/src/reader/components/TechDocsPageHeader.tsx b/plugins/techdocs/src/reader/components/TechDocsPageHeader.tsx
index 7c25333d3f..f56542773f 100644
--- a/plugins/techdocs/src/reader/components/TechDocsPageHeader.tsx
+++ b/plugins/techdocs/src/reader/components/TechDocsPageHeader.tsx
@@ -14,29 +14,32 @@
* limitations under the License.
*/
-import { EntityName, RELATION_OWNED_BY } from '@backstage/catalog-model';
-import { Header, HeaderLabel } from '@backstage/core-components';
+import React, { PropsWithChildren } from 'react';
+import CodeIcon from '@material-ui/icons/Code';
+
import { useRouteRef } from '@backstage/core-plugin-api';
+import { Header, HeaderLabel } from '@backstage/core-components';
+import { EntityName, RELATION_OWNED_BY } from '@backstage/catalog-model';
import {
EntityRefLink,
EntityRefLinks,
getEntityRelations,
} from '@backstage/plugin-catalog-react';
-import CodeIcon from '@material-ui/icons/Code';
-import React from 'react';
+
import { rootRouteRef } from '../../routes';
import { TechDocsEntityMetadata, TechDocsMetadata } from '../../types';
-export type TechDocsPageHeaderProps = {
+export type TechDocsPageHeaderProps = PropsWithChildren<{
entityRef: EntityName;
entityMetadata?: TechDocsEntityMetadata;
techDocsMetadata?: TechDocsMetadata;
-};
+}>;
export const TechDocsPageHeader = ({
entityRef,
entityMetadata,
techDocsMetadata,
+ children,
}: TechDocsPageHeaderProps) => {
const { name } = entityRef;
@@ -107,6 +110,7 @@ export const TechDocsPageHeader = ({
typeLink={docsRootLink}
>
{labels}
+ {children}
);
};
diff --git a/plugins/techdocs/src/reader/transformers/simplifyMkdocsFooter.test.ts b/plugins/techdocs/src/reader/transformers/simplifyMkdocsFooter.test.ts
index 020befd522..ac4c9fa986 100644
--- a/plugins/techdocs/src/reader/transformers/simplifyMkdocsFooter.test.ts
+++ b/plugins/techdocs/src/reader/transformers/simplifyMkdocsFooter.test.ts
@@ -27,7 +27,7 @@ describe('simplifyMkdocsFooter', () => {
},
);
- expect(shadowDom.querySelector('.md-footer-copyright')).toBeTruthy();
+ expect(shadowDom.querySelector('.md-footer .md-copyright')).toBeTruthy();
});
it('does remove mkdocs copyright', async () => {
@@ -39,6 +39,6 @@ describe('simplifyMkdocsFooter', () => {
},
);
- expect(shadowDom.querySelector('.md-footer-copyright')).toBeFalsy();
+ expect(shadowDom.querySelector('.md-footer .md-copyright')).toBeFalsy();
});
});
diff --git a/plugins/techdocs/src/reader/transformers/simplifyMkdocsFooter.ts b/plugins/techdocs/src/reader/transformers/simplifyMkdocsFooter.ts
index 2b96926270..b583a72fbf 100644
--- a/plugins/techdocs/src/reader/transformers/simplifyMkdocsFooter.ts
+++ b/plugins/techdocs/src/reader/transformers/simplifyMkdocsFooter.ts
@@ -19,7 +19,7 @@ import type { Transformer } from './transformer';
export const simplifyMkdocsFooter = (): Transformer => {
return dom => {
// Remove mkdocs copyright
- dom.querySelector('.md-footer-copyright')?.remove();
+ dom.querySelector('.md-footer .md-copyright')?.remove();
return dom;
};
diff --git a/plugins/techdocs/src/test-utils/fixtures/mkdocs-index.ts b/plugins/techdocs/src/test-utils/fixtures/mkdocs-index.ts
index afb84dc10b..21d703fa2e 100644
--- a/plugins/techdocs/src/test-utils/fixtures/mkdocs-index.ts
+++ b/plugins/techdocs/src/test-utils/fixtures/mkdocs-index.ts
@@ -1520,7 +1520,7 @@ css img js mkdocs sitemap.xml