diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 28792a6..e54c2df 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -15,7 +15,12 @@ const config: Config = { trailingSlash: false, onBrokenLinks: 'throw', - onBrokenMarkdownLinks: 'warn', + + markdown: { + hooks: { + onBrokenMarkdownLinks: 'warn', + }, + }, i18n: { defaultLocale: 'en', @@ -40,6 +45,13 @@ const config: Config = { themeConfig: { image: 'img/social-card.png', + metadata: [ + { + name: 'keywords', + content: + 'sched_ext, sched_ext scheduler, Linux scheduler, BPF, OCI, Kubernetes operator, eBPF', + }, + ], colorMode: { defaultMode: 'dark', respectPrefersColorScheme: true, @@ -71,6 +83,7 @@ const config: Config = { href: 'https://github.com/schedkit', label: 'GitHub', position: 'right', + className: 'navbar__cta', }, ], }, @@ -107,7 +120,7 @@ const config: Config = { prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula, - additionalLanguages: ['bash', 'yaml', 'toml', 'go', 'docker'], + additionalLanguages: ['bash', 'yaml', 'toml', 'go', 'docker', 'json'], }, } satisfies Preset.ThemeConfig, }; diff --git a/src/css/custom.css b/src/css/custom.css index f6701cc..ff297f6 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -1,38 +1,1039 @@ /** - * Light customisations on top of the default Docusaurus theme. - * Keep this file small. Heavy theming belongs in a swizzled component. + * schedkit docs — monochrome with one earned accent. + * + * The page is grey. There is exactly one colour, and it has exactly one job: + * to mark the product, a pointer, the current position, or keyboard focus. + * + * --sk-accent is allowed on: the logo, links, the active sidebar/TOC + * entry, and the focus ring. Nothing else. + * + * The test for adding more colour is not "would this look nicer" but "does + * this encode something a reader could act on". Decoration loses. + * + * Structure is carried by type scale and 1px hairlines; mono is used only + * for literal code, commands and check IDs. Dark and light are the same + * design inverted, not two designs. */ +/* ------------------------------------------------------------------ * + * Typography + * ------------------------------------------------------------------ */ + :root { - --ifm-color-primary: #2174b6; - --ifm-color-primary-dark: #1e69a4; - --ifm-color-primary-darker: #1c639b; - --ifm-color-primary-darkest: #175280; - --ifm-color-primary-light: #257fc4; - --ifm-color-primary-lighter: #2885cb; - --ifm-color-primary-lightest: #4296d9; - --ifm-code-font-size: 95%; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); -} - -[data-theme='dark'] { - --ifm-color-primary: #69ace2; - --ifm-color-primary-dark: #4799dc; - --ifm-color-primary-darker: #358fd9; - --ifm-color-primary-darkest: #237ec3; - --ifm-color-primary-light: #8bc0e9; - --ifm-color-primary-lighter: #a0cbed; - --ifm-color-primary-lightest: #c7e0f4; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); -} - -/* A bit more breathing room between paragraphs in long-form pages. */ -.markdown > p { - margin-bottom: 1.1rem; -} - -/* Slightly tighter inline code so it doesn't feel pasted on. */ + --ifm-font-family-base: + ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + 'Helvetica Neue', Arial, 'Noto Sans', sans-serif; + --ifm-font-family-monospace: + ui-monospace, 'SFMono-Regular', 'SF Mono', Menlo, Consolas, + 'Liberation Mono', monospace; + + --ifm-font-size-base: 16.5px; + --ifm-line-height-base: 1.72; + --ifm-code-font-size: 87.5%; + --ifm-heading-font-weight: 700; + --ifm-heading-line-height: 1.12; + + --ifm-global-radius: 0; + + --docusaurus-highlighted-code-line-bg: rgba(128, 128, 128, 0.18); + --ifm-h1-font-size: 2.5rem; + --ifm-h2-font-size: 1.6rem; + --ifm-h3-font-size: 1.2rem; +} + +/* ------------------------------------------------------------------ * + * Dark (default). True black, off-white text, achromatic hairlines. + * ------------------------------------------------------------------ */ + +/* + * Infima ships its whole dark palette on `html[data-theme=dark]`, which is + * specificity (0,1,1) — it beats a bare `:root` or `[data-theme='light']` + * selector. That silently overrode the page background (landing on #1b1b1d + * slate instead of true black) plus ~30 other tokens. `:root[data-theme=...]` + * is (0,2,0) and wins, so both theme blocks are scoped this way. + */ +:root, +:root[data-theme='dark'] { + --sk-bg: #000000; + --sk-surface: #0e0e0e; + --sk-surface-2: #171717; + --sk-fg: #ededed; + --sk-fg-muted: #a8a8a8; + --sk-fg-subtle: #8a8a8a; + --sk-line: #2b2b2b; + --sk-line-strong: #4a4a4a; + /* The one colour. Logo, links, current position, focus. Nothing else. */ + --sk-accent: #4c9be8; + + --ifm-background-color: var(--sk-bg); + --ifm-background-surface-color: var(--sk-surface); + --ifm-font-color-base: var(--sk-fg); + --ifm-heading-color: var(--sk-fg); + --ifm-font-color-dark: var(--sk-fg); + --ifm-font-color-darker: var(--sk-fg); + --ifm-font-color-darkest: #ffffff; + --ifm-font-color-light: var(--sk-fg-muted); + --ifm-font-color-lighter: var(--sk-fg-muted); + --ifm-font-color-lightest: var(--sk-fg-subtle); + --ifm-link-color: var(--sk-accent); + + /* Infima derives the font ramp from these; pin them so it stays grey. */ + --ifm-color-content: var(--sk-fg); + --ifm-color-content-secondary: var(--sk-fg-muted); + + --ifm-color-emphasis-0: var(--sk-fg); + --ifm-color-emphasis-100: var(--sk-bg); + --ifm-color-emphasis-200: var(--sk-surface); + --ifm-color-emphasis-300: var(--sk-surface-2); + --ifm-color-emphasis-400: var(--sk-line-strong); + --ifm-color-emphasis-500: var(--sk-line-strong); + --ifm-color-emphasis-600: var(--sk-fg-muted); + --ifm-color-emphasis-700: var(--sk-fg); + --ifm-color-emphasis-800: var(--sk-fg); + --ifm-color-emphasis-900: #ffffff; + --ifm-color-emphasis-1000: #ffffff; + + /* + * Every stop is the same achromatic foreground so nothing can smuggle hue + * back in. + */ + --ifm-color-primary: #ededed; + --ifm-color-primary-dark: #d8d8d8; + --ifm-color-primary-darker: #cccccc; + --ifm-color-primary-darkest: #b0b0b0; + --ifm-color-primary-light: #ffffff; + --ifm-color-primary-lighter: #ffffff; + --ifm-color-primary-lightest: #ffffff; + --ifm-color-primary-contrast-background: #ededed; + --ifm-color-primary-contrast-foreground: #000000; + + /* Infima tints these by default; this theme is untinted. */ + --ifm-table-stripe-background: transparent; + --ifm-code-background: var(--sk-surface-2); + --ifm-hover-overlay: rgba(255, 255, 255, 0.06); + --ifm-breadcrumb-item-background-active: transparent; + + --ifm-navbar-background-color: rgba(0, 0, 0, 0.88); + --ifm-navbar-shadow: none; + --ifm-footer-background-color: #000000; +} + +/* ------------------------------------------------------------------ * + * Light. Same design, inverted. + * ------------------------------------------------------------------ */ + +:root[data-theme='light'] { + --sk-bg: #ffffff; + --sk-surface: #ffffff; + --sk-surface-2: #f4f4f4; + --sk-fg: #111111; + --sk-fg-muted: #5c5c5c; + --sk-fg-subtle: #6e6e6e; + --sk-line: #e2e2e2; + --sk-line-strong: #c2c2c2; + /* Exactly the logo blue — 4.8:1 on white, so it clears AA for link text. */ + --sk-accent: #2376b8; + + --ifm-background-color: var(--sk-bg); + --ifm-background-surface-color: var(--sk-surface); + --ifm-font-color-base: var(--sk-fg); + --ifm-heading-color: #000000; + --ifm-font-color-light: var(--sk-fg-muted); + --ifm-font-color-lighter: var(--sk-fg-muted); + --ifm-font-color-lightest: var(--sk-fg-subtle); + --ifm-link-color: var(--sk-accent); + --ifm-color-content: var(--sk-fg); + --ifm-color-content-secondary: var(--sk-fg-muted); + + --ifm-color-emphasis-0: var(--sk-fg); + --ifm-color-emphasis-100: var(--sk-bg); + --ifm-color-emphasis-200: var(--sk-surface-2); + --ifm-color-emphasis-300: var(--sk-surface-2); + --ifm-color-emphasis-400: var(--sk-line-strong); + --ifm-color-emphasis-500: var(--sk-line-strong); + --ifm-color-emphasis-600: var(--sk-fg-muted); + --ifm-color-emphasis-700: var(--sk-fg); + --ifm-color-emphasis-800: var(--sk-fg); + --ifm-color-emphasis-900: #000000; + --ifm-color-emphasis-1000: #000000; + + --ifm-color-primary: #111111; + --ifm-color-primary-dark: #000000; + --ifm-color-primary-darker: #000000; + --ifm-color-primary-darkest: #000000; + --ifm-color-primary-light: #3d3d3d; + --ifm-color-primary-lighter: #4a4a4a; + --ifm-color-primary-lightest: #6e6e6e; + --ifm-color-primary-contrast-background: #111111; + --ifm-color-primary-contrast-foreground: #ffffff; + + --ifm-table-stripe-background: transparent; + --ifm-code-background: var(--sk-surface-2); + --ifm-hover-overlay: rgba(0, 0, 0, 0.05); + --ifm-breadcrumb-item-background-active: transparent; + + --ifm-navbar-background-color: rgba(255, 255, 255, 0.9); + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.07); +} + +/* ------------------------------------------------------------------ * + * Base + * ------------------------------------------------------------------ */ + +html { + scroll-behavior: smooth; + -webkit-text-size-adjust: 100%; +} + +body { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* Inverted selection: the loudest thing on the page, for the right reason. */ +::selection { + background: var(--sk-fg); + color: var(--sk-bg); +} + +/* + * Focus is blue. A keyboard user must be able to tell focus apart from a + * decorative state at a glance, and blue is the only thing on this page that + * already means "actionable". + */ +:focus-visible { + outline: 2px solid var(--sk-accent); + outline-offset: 3px; +} + +* { + scrollbar-width: thin; + scrollbar-color: var(--sk-line-strong) transparent; +} + +*::-webkit-scrollbar { + width: 10px; + height: 10px; +} + +*::-webkit-scrollbar-track { + background: transparent; +} + +*::-webkit-scrollbar-thumb { + background: var(--sk-line-strong); + border: 3px solid transparent; + background-clip: content-box; +} + +*::-webkit-scrollbar-thumb:hover { + background: var(--sk-fg-subtle); + background-clip: content-box; +} + +/* ------------------------------------------------------------------ * + * Navbar + * ------------------------------------------------------------------ */ + +.navbar { + border-bottom: 1px solid var(--sk-line); + padding-inline: 0.75rem; + backdrop-filter: saturate(120%) blur(10px); + -webkit-backdrop-filter: saturate(120%) blur(10px); +} + +.navbar__inner { + max-width: 1440px; + margin-inline: auto; +} + +.navbar__brand { + margin-right: 2rem; +} + +.navbar__logo { + height: 22px; + width: auto; + /* Full saturation: the logo is the one surface that earns the accent. */ +} + +.navbar__title { + font-weight: 700; + font-size: 1rem; + letter-spacing: -0.03em; + padding-inline-start: 0.6rem; +} + +.navbar__logo + .navbar__title { + padding-inline-start: 0.6rem; +} + +.navbar__link { + font-size: 0.875rem; + font-weight: 500; + color: var(--sk-fg-muted); + transition: color 120ms ease; +} + +.navbar__link:hover { + color: var(--sk-fg); +} + +.navbar__link--active { + color: var(--sk-accent); + font-weight: 700; +} + +.navbar__link--github { + font-family: var(--ifm-font-family-monospace); + font-size: 0.78rem; + letter-spacing: 0.02em; +} + +.navbar__items--right { + margin-inline-start: auto; +} + +.navbar .clean-btn { + border: 1px solid var(--sk-line); + border-radius: 0; + transition: color 120ms ease, border-color 120ms ease; +} + +.navbar .clean-btn:hover { + border-color: var(--sk-line-strong); + background: transparent; + color: var(--sk-fg); +} + +.navbar__toggle, +.navbar__login { + border-radius: 0; +} + +/* ------------------------------------------------------------------ * + * Sidebar + * ------------------------------------------------------------------ */ + +.sidebar { + border-right: 1px solid var(--sk-line); + padding-block: 1.75rem 3rem; +} + +.sidebar__group { + padding-inline-start: 0.6rem; +} + +.menu__list-item-collapsible { + list-style-type: none; +} + +.menu__list-item-collapsible > .menu__list-item-collapsible-toggle { + font-family: var(--ifm-font-family-monospace); + font-size: 0.68rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--sk-fg-subtle); + padding-block: 0.9rem 0.45rem; +} + +.menu__list-item-collapsible > .menu__list-item-collapsible-toggle:hover { + background: transparent; + color: var(--sk-fg); +} + +.menu__list-item-collapsible-toggle::before { + background: var(--sk-line); + height: 1px; + opacity: 1; +} + +.menu__link { + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0; + padding-block: 0.34rem; + padding-inline: 0.5rem; + color: var(--sk-fg-muted); + transition: color 120ms ease, background-color 120ms ease; +} + +.menu__link:hover { + background: var(--sk-surface-2); + color: var(--sk-fg); + text-decoration: none; +} + +/* + * Active sidebar entry: blue, no fill. "You are here" is a pointer, so it + * gets the pointer colour rather than a chip — a background fill would be the + * one place on the page where the accent stops meaning anything. + */ +.menu__link--active:not(.menu__link--sublist) { + background: transparent; + color: var(--sk-accent); + font-weight: 700; + border-left: 2px solid var(--sk-accent); + padding-left: calc(0.5rem - 2px); +} + +.menu__link--active:not(.menu__link--sublist)::before { + display: none; +} + +.menu__list-item > .menu__list-item-collapsible:hover { + background: transparent; +} + +/* ------------------------------------------------------------------ * + * Doc content + * ------------------------------------------------------------------ */ + +.docItemContainer { + padding-block: 2.75rem 4rem; +} + +.theme-doc-markdown { + font-size: 1.01rem; +} + +.theme-doc-markdown > p, +.theme-doc-markdown > ul, +.theme-doc-markdown > ol { + margin-bottom: 1.15rem; +} + +/* Docusaurus wraps the title in

, so cover both shapes. */ +.theme-doc-markdown header > h1, +.theme-doc-markdown > h1 { + font-size: 2.4rem; + font-weight: 700; + letter-spacing: -0.04em; + line-height: 1.08; + margin-bottom: 1.75rem; +} + +.theme-doc-markdown > h2 { + font-size: 1.55rem; + letter-spacing: -0.028em; + margin-top: 3rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid var(--sk-line); +} + +.theme-doc-markdown > h3 { + font-size: 1.15rem; + letter-spacing: -0.015em; + margin-top: 2.1rem; +} + +.theme-doc-markdown > h4, +.theme-doc-markdown > h5, +.theme-doc-markdown > h6 { + font-family: var(--ifm-font-family-monospace); + font-size: 0.78rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--sk-fg-subtle); + margin-top: 2rem; +} + +@media screen and (min-width: 997px) { + .theme-doc-markdown { + max-width: 45rem; + } +} + +/* Links: blue and underlined. The underline means "pointer" even when the + colour is missed or the link is visited. */ +.theme-doc-markdown a { + color: var(--sk-accent); + text-decoration: underline; + text-decoration-thickness: 1px; + text-underline-offset: 0.2em; + text-decoration-color: var(--sk-line-strong); + transition: text-decoration-color 120ms ease; +} + +.theme-doc-markdown a:hover { + color: var(--sk-accent); + text-decoration-color: var(--sk-accent); +} + +.theme-doc-markdown strong { + font-weight: 700; +} + +.theme-doc-markdown blockquote { + border-inline-start: 1px solid var(--sk-line-strong); + padding-inline-start: 1.1rem; + font-style: normal; + color: var(--sk-fg-muted); +} + +.theme-doc-markdown ul, +.theme-doc-markdown ol { + padding-inline-start: 1.35rem; +} + +.theme-doc-markdown li { + margin-bottom: 0.35rem; +} + +.theme-doc-markdown li::marker { + color: var(--sk-fg-subtle); +} + +.theme-doc-markdown hr { + border: 0; + border-top: 1px solid var(--sk-line); + margin-block: 3rem; +} + +.theme-doc-markdown img { + border: 1px solid var(--sk-line); +} + +/* Breadcrumbs: plain path, mono, no chips. */ +.theme-doc-breadcrumbs { + font-family: var(--ifm-font-family-monospace); + font-size: 0.72rem; + letter-spacing: 0.02em; + margin-bottom: 1.25rem; +} + +/* + * Docusaurus puts only `theme-doc-breadcrumbs` on the