From 9995d3295be6afb50995343bb4d1fbf5691d54cc Mon Sep 17 00:00:00 2001 From: Erlend Ellefsen Date: Tue, 23 Jun 2026 14:25:08 +0200 Subject: [PATCH] feat(plugin): inject default light/dark palette toggle The minimal config (just the plugin + search) now ships a teal light/dark palette toggle, injected the same way as fonts, features, and icons. A user-defined `palette` takes precedence and replaces the default --- README.md | 20 +++++-------- docs/index.md | 6 ++-- mkdocs.yaml | 14 --------- src/intility_bifrost_mkdocs/plugin.py | 38 +++++++++++++++++++++++++ tests/test_plugin.py | 41 +++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 55c9c8a..4cba609 100644 --- a/README.md +++ b/README.md @@ -32,29 +32,23 @@ uv pip install intility-bifrost-mkdocs ## Usage -Add `intility-bifrost` to your `mkdocs.yml` plugins: +Set the theme to `material` and add `intility-bifrost` to your `mkdocs.yml` plugins: ```yaml theme: name: material - palette: - - scheme: light - primary: &bifrost_theme teal # Options: teal, purple, pink, yellow - toggle: - icon: material/brightness-7 - name: Switch to dark mode - - scheme: dark - primary: *bifrost_theme - toggle: - icon: material/brightness-4 - name: Switch to light mode plugins: - intility-bifrost - search ``` -The `&bifrost_theme` anchor sets the color once and reuses it for both modes. +That's the whole setup. The plugin configures the markdown extensions, theme +features, fonts, icons, and a light/dark palette toggle for you. + +To pick a different theme color, set a version badge, or build navigation from +`.nav.yml` files, see the +**[Quick Start](https://intility.github.io/bifrost-mkdocs/)** in the docs. ## What it provides diff --git a/docs/index.md b/docs/index.md index 7255bcd..c7c0ef1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -20,7 +20,7 @@ See the [Feature Showcase](showcase/index.md) for a live demonstration of every 3. **Start writing docs.** The plugin configures the markdown extensions, theme features, fonts, and icons for you. -That's all you need. Everything else (markdown extensions, theme features, fonts, icons) is injected automatically and can be overridden by setting it yourself. Add a [color scheme and dark-mode toggle](#change-the-color-scheme) when you want them. +That's all you need. Everything else (markdown extensions, theme features, fonts, icons, and a teal light/dark palette toggle) is injected automatically and can be overridden by setting it yourself. To use a [different color](#change-the-color-scheme), set your own `palette`. ## Overriding Defaults @@ -36,7 +36,7 @@ markdown_extensions: ### Change the Color Scheme -Add a `palette` to your theme config. This sets the Bifrost theme color and enables the light/dark mode toggle: +The plugin injects a teal light/dark palette by default. To use a different color, set your own `palette` (this also lets you customize the toggle icons and labels): ```yaml theme: @@ -54,7 +54,7 @@ theme: name: Switch to light mode ``` -The `&bifrost_theme` anchor defines the color once and reuses it for both modes. Without a palette the site still works; it just uses the default color and has no mode toggle. +The `&bifrost_theme` anchor defines the color once and reuses it for both modes. A user-defined `palette` replaces the injected default entirely, so include both modes if you want to keep the toggle. ### Version Badge diff --git a/mkdocs.yaml b/mkdocs.yaml index f40e8b6..d616e62 100644 --- a/mkdocs.yaml +++ b/mkdocs.yaml @@ -16,17 +16,6 @@ theme: language: en logo: assets/bifrost-logo.svg favicon: assets/bifrost-logo.svg - palette: - - scheme: light - primary: &bifrost_theme teal # Options: teal, purple, pink, yellow - toggle: - icon: material/brightness-7 - name: Switch to dark mode - - scheme: dark - primary: *bifrost_theme - toggle: - icon: material/brightness-4 - name: Switch to light mode plugins: - intility-bifrost @@ -36,6 +25,3 @@ plugins: - git-revision-date-localized: enable_creation_date: true type: timeago - -# Navigation is defined with awesome-nav via .nav.yml files in docs/ and -# docs/showcase/ instead of an explicit nav: block here. diff --git a/src/intility_bifrost_mkdocs/plugin.py b/src/intility_bifrost_mkdocs/plugin.py index 58c7abd..835d016 100644 --- a/src/intility_bifrost_mkdocs/plugin.py +++ b/src/intility_bifrost_mkdocs/plugin.py @@ -1,5 +1,6 @@ from __future__ import annotations +import copy from pathlib import Path from typing import Any @@ -64,6 +65,32 @@ "content.tabs.link", ] +# --------------------------------------------------------------------------- +# Default palette: a light/dark toggle on the teal theme. +# --------------------------------------------------------------------------- +# Mirrors the quick start in the docs so the minimal config (just the plugin) +# ships with a working light/dark toggle. `scheme` uses light/dark rather than +# Material's default/slate; bifrost-theme.js maps those onto Bifrost's +# light/dark classes and Bifrost's cascade layers handle the colors. +DEFAULT_PALETTE: list[dict[str, Any]] = [ + { + "scheme": "light", + "primary": "teal", + "toggle": { + "icon": "material/brightness-7", + "name": "Switch to dark mode", + }, + }, + { + "scheme": "dark", + "primary": "teal", + "toggle": { + "icon": "material/brightness-4", + "name": "Switch to light mode", + }, + }, +] + # --------------------------------------------------------------------------- # Fonts (Bifrost branding). # --------------------------------------------------------------------------- @@ -188,6 +215,16 @@ def _inject_theme_features(config: MkDocsConfig) -> None: config.theme["features"] = features +def _inject_palette(config: MkDocsConfig) -> None: + """Set a default light/dark palette (with toggle) if the user hasn't. + + Gives the minimal config a working light/dark toggle. A user-defined + ``palette`` (even a single-entry one) is left untouched. + """ + if not config.theme.get("palette"): + config.theme["palette"] = copy.deepcopy(DEFAULT_PALETTE) + + def _inject_theme_settings(config: MkDocsConfig) -> None: """Set Bifrost fonts and admonition icons when the user hasn't customized them.""" # Fonts: Bifrost styles all text (vendored Satoshi) and code (system @@ -315,6 +352,7 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig: # Inject sensible defaults (never overwrites user-provided config). _inject_markdown_extensions(config) _inject_theme_features(config) + _inject_palette(config) _inject_theme_settings(config) _inject_extra_javascript(config) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 7687143..bcf0794 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -12,6 +12,7 @@ DEFAULT_ADMONITION_ICONS, DEFAULT_EXTENSIONS, DEFAULT_FEATURES, + DEFAULT_PALETTE, DEFAULT_TOP_ICON, IntilityBifrostPlugin, _build_layer_bootstrap_css, @@ -259,6 +260,46 @@ def test_user_top_icon_preserved(): assert result.theme["icon"]["top"] == "material/arrow-up" +# --------------------------------------------------------------------------- +# Palette +# --------------------------------------------------------------------------- + + +def test_default_palette_injected(): + """The minimal config should get a light/dark palette with a toggle.""" + plugin = IntilityBifrostPlugin() + config = _minimal_config() + + result = plugin.on_config(config) + + palette = result.theme["palette"] + assert [entry["scheme"] for entry in palette] == ["light", "dark"] + assert all("toggle" in entry for entry in palette) + + +def test_user_palette_preserved(): + """A user-provided palette should not be overwritten.""" + plugin = IntilityBifrostPlugin() + config = _minimal_config() + + config.theme["palette"] = [{"scheme": "slate", "primary": "purple"}] + + result = plugin.on_config(config) + + assert result.theme["palette"] == [{"scheme": "slate", "primary": "purple"}] + + +def test_default_palette_not_shared_across_configs(): + """Injected palette must be a copy, not the shared module constant.""" + plugin = IntilityBifrostPlugin() + config = _minimal_config() + + result = plugin.on_config(config) + result.theme["palette"][0]["primary"] = "pink" + + assert DEFAULT_PALETTE[0]["primary"] == "teal" + + # --------------------------------------------------------------------------- # Extra JavaScript # ---------------------------------------------------------------------------