Metadata-Version: 2.5
Name: mkdocs-light-dark-toggle
Version: 0.1.0
Summary: Material for MkDocs plugin: an always-visible two-button light/dark switch, replacing Material's native single-knob palette toggle.
Project-URL: Homepage, https://github.com/luka-sherman/mkdocs-light-dark-toggle
Author-email: Luka Sherman <luka.msherman@gmail.com>
License: MIT
License-File: LICENSE
Classifier: Framework :: MkDocs
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.9
Requires-Dist: mkdocs>=1.5
Provides-Extra: test
Requires-Dist: mkdocs-material; extra == 'test'
Requires-Dist: playwright; extra == 'test'
Requires-Dist: pytest; extra == 'test'
Requires-Dist: pytest-playwright; extra == 'test'
Description-Content-Type: text/markdown

# mkdocs-light-dark-toggle

A [MkDocs](https://www.mkdocs.org/) plugin (built for and tested with
[Material for MkDocs](https://squidfunk.github.io/mkdocs-material/)) that replaces Material's
native light/dark switch with an always-visible two-button toggle. Material's own switch is a
single knob: the current scheme is the only thing you can see, and the knob itself is the only
clickable spot. This plugin shows both options side by side, with a sliding highlight behind
whichever is active, so switching is a single click on either option rather than a click-to-cycle
knob.

The native palette radios stay in the DOM, hidden. Their own JavaScript still applies and persists
the scheme via `localStorage` — this plugin only drives them, it doesn't reimplement scheme
switching.

Light mode, sun active:

![Light/dark toggle in light mode, sun option active](screenshots/toggle-light.png)

Dark mode, moon active:

![Light/dark toggle in dark mode, moon option active](screenshots/toggle-dark.png)

```yaml
plugins:
  - light_dark_toggle
```

That's it — the toggle needs no configuration to work, since it ships with built-in sun/moon
icons and sensible default text.

## Requirements

Python 3.9+ and MkDocs 1.5+. Built for Material for MkDocs: the plugin looks for Material's own
`[data-md-component="palette"]` form and the two radio inputs inside it
(`input[data-md-color-scheme="default"]` and `="slate"`). Your `mkdocs.yml` needs both schemes
configured under `theme.palette`:

```yaml
theme:
  name: material
  palette:
    - scheme: default
      toggle:
        icon: material/brightness-7
        name: Switch to dark mode
    - scheme: slate
      toggle:
        icon: material/brightness-4
        name: Switch to light mode
```

If either radio isn't found, the plugin leaves Material's native switch alone.

## Configure

```yaml
plugins:
  - light_dark_toggle:
      light:
        description: Switch to light mode
        announcement: Lights on
      dark:
        description: Switch to dark mode
        announcement: Lights off
```

| Key           | Default                             | Description                                                        |
| ------------- | ------------------------------------ | -------------------------------------------------------------------- |
| `light`       | see below                           | `description`/`announcement` for the light option.                   |
| `dark`        | see below                           | `description`/`announcement` for the dark option.                    |
| `aria_label`  | `Color theme`                       | Accessible label for the toggle group.                               |
| `show_toast`  | `true`                              | Show a short message after the scheme changes.                       |

`light`/`dark` each take:

| Key            | Default                  | Description                                            |
| -------------- | ------------------------- | -------------------------------------------------------- |
| `description`  | `Switch to light/dark mode` | Button title and accessible label.                      |
| `announcement` | `Light mode`/`Dark mode`  | Text shown in the toast after switching to this scheme. |

## Styling

The toggle's CSS is controlled with custom properties. Override them in your `extra_css` file on
`#light-dark-toggle`, or on a parent element such as `:root`:

```css
#light-dark-toggle {
  --light-dark-accent: #2e7d32;      /* border and highlight color (default: currentColor) */
  --light-dark-track-bg: #fdf6e3;    /* toggle background (default: transparent) */
  --light-dark-active-fg: #fdf6e3;   /* icon color of the active option (default: Canvas) */
  --light-dark-radius: 1rem;         /* corner radius of the toggle and highlight (default: 1rem) */
  --light-dark-height: 1.2rem;       /* toggle height (default: 1.2rem) */
  --light-dark-icon-size: 0.7rem;    /* icon size (default: 0.7rem) */
}
```

Icons are CSS `mask-image` values and default to a built-in sun and moon:

```css
#light-dark-toggle {
  --light-dark-icon-light: url("data:image/svg+xml,...");
  --light-dark-icon-dark: url("data:image/svg+xml,...");
}
```

The toast has its own properties, kept separate from `--light-dark-accent`/`--light-dark-active-fg`
so theming the toggle doesn't also recolor the toast:

```css
#light-dark-toast {
  --light-dark-toast-bg: #2e7d32;   /* toast background (default: CanvasText) */
  --light-dark-toast-fg: #fdf6e3;   /* toast text color (default: Canvas) */
}
```

Left at their defaults, `CanvasText`/`Canvas` auto-invert the toast against the page's
`color-scheme` CSS property. If your site switches schemes manually rather than relying on
`prefers-color-scheme` — which, using this plugin, it does — set `color-scheme: light`/`dark`
yourself on the selector your palette CSS already scopes to (e.g.
`[data-md-color-scheme="slate"] { color-scheme: dark; }`) for that to track the active scheme
instead of the OS preference.

For other changes, target the classes `.light-dark-toggle`, `.light-dark-highlight`,
`.light-dark-option`, and `.light-dark-toast`. The script sets the highlight's position via
`[data-active]` on `.light-dark-toggle`, not inline styles.

## Accessibility

- The color properties aren't checked for contrast. Check your color choices against WCAG
  contrast requirements.
- Each option is a toggle button with `aria-pressed` and its own tab stop. The toggle doesn't use
  the ARIA radio group pattern, which has a single tab stop and arrow-key navigation.
- Transitions are turned off when `prefers-reduced-motion: reduce` is set.
- If the plugin's JavaScript doesn't run, Material's native palette switch is left visible and
  fully functional — nothing is hidden until this plugin's own script confirms it found both
  radios and successfully built its replacement.

## Analytics

Listen for the `light-dark:modechange` event on `document` to record scheme changes.
`event.detail` contains `mode` (`"light"` or `"dark"`) and `previousMode`:

```js
document.addEventListener("light-dark:modechange", (event) => {
  const { mode, previousMode } = event.detail;
  gtag("event", "color_scheme_change", { mode, previous_mode: previousMode });
});
```

The event fires only on a click that changes the scheme — not on page load, and not when clicking
the option that's already active. To read the scheme at any time, including on page load, use
Material's own `data-md-color-scheme` attribute on `<body>`:

```js
const scheme = document.body.getAttribute("data-md-color-scheme"); // "default" or "slate"
```

## Testing

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[test]"
playwright install chromium
pytest
```

`tests/fixture_site/` is a small Material for MkDocs site that uses the plugin. The tests build it
once, serve it locally, and run Playwright against it:

- `test_behavior.py`: replacing the native form, switching scheme, persistence, toast, events.
- `test_accessibility.py`: axe-core checks.
- `test_keyboard.py`: keyboard use and focus.

axe-core is included in `tests/vendor/`, so the tests don't need network access.

## License

[MIT](LICENSE)
