mkdocs-audience-toggle 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mkdocs_audience_toggle-0.1.0/.github/workflows/publish.yml +23 -0
- mkdocs_audience_toggle-0.1.0/.gitignore +9 -0
- mkdocs_audience_toggle-0.1.0/LICENSE +21 -0
- mkdocs_audience_toggle-0.1.0/PKG-INFO +380 -0
- mkdocs_audience_toggle-0.1.0/README.md +360 -0
- mkdocs_audience_toggle-0.1.0/mkdocs_audience_toggle/__init__.py +0 -0
- mkdocs_audience_toggle-0.1.0/mkdocs_audience_toggle/plugin.py +122 -0
- mkdocs_audience_toggle-0.1.0/mkdocs_audience_toggle/static/audience_toggle.css +133 -0
- mkdocs_audience_toggle-0.1.0/mkdocs_audience_toggle/static/audience_toggle.js +332 -0
- mkdocs_audience_toggle-0.1.0/pyproject.toml +39 -0
- mkdocs_audience_toggle-0.1.0/tests/conftest.py +89 -0
- mkdocs_audience_toggle-0.1.0/tests/fixture_site/docs/extra.css +10 -0
- mkdocs_audience_toggle-0.1.0/tests/fixture_site/docs/index.md +30 -0
- mkdocs_audience_toggle-0.1.0/tests/fixture_site/docs/other.md +21 -0
- mkdocs_audience_toggle-0.1.0/tests/fixture_site/mkdocs.yml +51 -0
- mkdocs_audience_toggle-0.1.0/tests/test_accessibility.py +42 -0
- mkdocs_audience_toggle-0.1.0/tests/test_behavior.py +271 -0
- mkdocs_audience_toggle-0.1.0/tests/test_keyboard.py +70 -0
- mkdocs_audience_toggle-0.1.0/tests/vendor/axe.min.js +12 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
id-token: write
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.x"
|
|
18
|
+
|
|
19
|
+
- run: python -m pip install --upgrade build
|
|
20
|
+
|
|
21
|
+
- run: python -m build
|
|
22
|
+
|
|
23
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Luka Sherman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: mkdocs-audience-toggle
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Material for MkDocs plugin: an N-way content-mode toggle (e.g. Essentials/Advanced) that shows or hides marked content per mode.
|
|
5
|
+
Project-URL: Homepage, https://github.com/luka-sherman/mkdocs-audience-toggle
|
|
6
|
+
Author-email: Luka Sherman <luka.msherman@gmail.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Framework :: MkDocs
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
|
+
Requires-Dist: mkdocs>=1.5
|
|
14
|
+
Provides-Extra: test
|
|
15
|
+
Requires-Dist: mkdocs-material; extra == 'test'
|
|
16
|
+
Requires-Dist: playwright; extra == 'test'
|
|
17
|
+
Requires-Dist: pytest; extra == 'test'
|
|
18
|
+
Requires-Dist: pytest-playwright; extra == 'test'
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# mkdocs-audience-toggle
|
|
22
|
+
|
|
23
|
+
A [MkDocs](https://www.mkdocs.org/) plugin (built for and tested with
|
|
24
|
+
[Material for MkDocs](https://squidfunk.github.io/mkdocs-material/)) that adds a toggle to the header that allows the user to switch the content mode. A mode allows you to hide content sections that you do not want that audience to see. By default all content is otherwise available in all modes (see ["Marking content"](#marking-content)). State persists across pages via `localStorage`.
|
|
25
|
+
|
|
26
|
+
With two modes, Beginner hides the advanced sections and Advanced shows everything. Switching
|
|
27
|
+
modes updates the page without reloading it.
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
plugins:
|
|
31
|
+
- audience_toggle:
|
|
32
|
+
modes:
|
|
33
|
+
- name: beginner
|
|
34
|
+
label: Beginner
|
|
35
|
+
icon: url(...)
|
|
36
|
+
- name: advanced
|
|
37
|
+
label: Advanced
|
|
38
|
+
default: true
|
|
39
|
+
icon: url(...)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+

|
|
43
|
+
|
|
44
|
+
In this example, the last two sections are marked `{: data-fcm-hide="beginner" }` (see
|
|
45
|
+
["Marking content"](#marking-content)), so Beginner mode hides them:
|
|
46
|
+
|
|
47
|
+

|
|
48
|
+
|
|
49
|
+

|
|
50
|
+
|
|
51
|
+
You can add more modes. Each mode's `icon` is optional.
|
|
52
|
+
|
|
53
|
+
```yaml
|
|
54
|
+
modes:
|
|
55
|
+
- name: beginner
|
|
56
|
+
icon: url(...)
|
|
57
|
+
- name: intermediate
|
|
58
|
+
default: true
|
|
59
|
+
icon: url(...)
|
|
60
|
+
- name: advanced
|
|
61
|
+
icon: url(...)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+

|
|
65
|
+
|
|
66
|
+
Each heading lists the modes it's hidden in, so sections can appear in stages:
|
|
67
|
+
|
|
68
|
+
```markdown
|
|
69
|
+
## Handling and health checks {: data-fcm-hide="beginner" }
|
|
70
|
+
|
|
71
|
+
## Breeding cycles {: data-fcm-hide="beginner intermediate" }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+

|
|
75
|
+
|
|
76
|
+

|
|
77
|
+
|
|
78
|
+

|
|
79
|
+
|
|
80
|
+
Below a 45em viewport width, `collapse_labels` shows only the icons:
|
|
81
|
+
|
|
82
|
+
```yaml
|
|
83
|
+
collapse_labels: true
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+

|
|
87
|
+
|
|
88
|
+
Without `collapse_labels`, a toggle that doesn't fit in the header moves to its own row:
|
|
89
|
+
|
|
90
|
+

|
|
91
|
+
|
|
92
|
+
## Requirements
|
|
93
|
+
|
|
94
|
+
Python 3.9+ and MkDocs 1.5+. The plugin doesn't depend on Material for MkDocs, but it was built
|
|
95
|
+
and tested with it, and two options rely on Material's markup:
|
|
96
|
+
|
|
97
|
+
- `insert_selector` defaults to Material's palette toggle. If nothing matches, the toggle is added
|
|
98
|
+
to the end of `<body>`. Set `insert_selector` to place it somewhere else.
|
|
99
|
+
- `hide_toc_entries` hides entries in Material's table of contents. With other themes it does
|
|
100
|
+
nothing, but the content itself is still hidden.
|
|
101
|
+
|
|
102
|
+
## Hidden content is not private
|
|
103
|
+
|
|
104
|
+
Content is hidden in the browser with JavaScript and CSS. Every visitor downloads the full page in
|
|
105
|
+
every mode, and hidden content can be read in the page source, with JavaScript turned off, or by
|
|
106
|
+
switching modes. Don't use the plugin to restrict access to anything.
|
|
107
|
+
|
|
108
|
+
MkDocs' `search` plugin indexes all content regardless of mode. A search result can point to a
|
|
109
|
+
heading that's hidden in the current mode. Following it switches modes, as described in
|
|
110
|
+
["Linking to hidden content"](#linking-to-hidden-content).
|
|
111
|
+
|
|
112
|
+
## Install
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
pip install mkdocs-audience-toggle
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Configure
|
|
119
|
+
|
|
120
|
+
```yaml
|
|
121
|
+
plugins:
|
|
122
|
+
- audience_toggle:
|
|
123
|
+
modes:
|
|
124
|
+
- name: essentials
|
|
125
|
+
label: Essentials
|
|
126
|
+
default: true
|
|
127
|
+
description: Show only what you need to write your first programs
|
|
128
|
+
announcement: Just the basics, start here!
|
|
129
|
+
icon: url(...)
|
|
130
|
+
- name: advanced
|
|
131
|
+
label: Advanced
|
|
132
|
+
description: Show all site content
|
|
133
|
+
announcement: Viewing all content.
|
|
134
|
+
icon: url(...)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`modes` is required. List at least two. Each entry has these keys:
|
|
138
|
+
|
|
139
|
+
| Key | Required | Description |
|
|
140
|
+
| -------------- | -------- | ---------------------------------------------------------------------------- |
|
|
141
|
+
| `name` | yes | Identifier used in `data-fcm-hide`, the URL parameter, and `localStorage`. |
|
|
142
|
+
| `label` | no | Button text. Defaults to `name.title()`. |
|
|
143
|
+
| `default` | no | Makes this the mode on a reader's first visit. Defaults to the first mode. |
|
|
144
|
+
| `icon` | no | A CSS `mask-image` value, such as `"url('data:image/svg+xml,...')"`. |
|
|
145
|
+
| `description` | no | Tooltip text for the mode's button. |
|
|
146
|
+
| `announcement` | no | Text shown in the toast after switching to this mode. Defaults to `label`. |
|
|
147
|
+
|
|
148
|
+
Other options:
|
|
149
|
+
|
|
150
|
+
| Key | Default | Description |
|
|
151
|
+
| ------------------ | ------------------------------- | --------------------------------------------------------------------------- |
|
|
152
|
+
| `storage_key` | `fcm-mode` | `localStorage` key for the active mode. |
|
|
153
|
+
| `query_param` | none | URL parameter that sets the mode, such as `?mode=advanced`. |
|
|
154
|
+
| `insert_selector` | `[data-md-component="palette"]` | The toggle is inserted before the first element matching this selector. If nothing matches, it's added to the end of `<body>`. |
|
|
155
|
+
| `attribute` | `data-fcm-hide` | Attribute used to mark content. |
|
|
156
|
+
| `hide_toc_entries` | `true` | Also hide a hidden heading's entry in Material's table of contents. |
|
|
157
|
+
| `wrapper_class` | `[]` | Class names of wrapper elements to hide along with a marked heading, when the heading is the wrapper's first child. |
|
|
158
|
+
| `aria_label` | `Content mode` | Accessible label for the toggle. |
|
|
159
|
+
| `collapse_labels` | `false` | Below a 45em viewport width, show only the icons. Every mode needs an `icon`. |
|
|
160
|
+
| `show_toast` | `true` | Show a short message after the mode changes. |
|
|
161
|
+
|
|
162
|
+
Below 45em, if the toggle doesn't fit in Material's header row, it moves to its own row below it.
|
|
163
|
+
When it fits, it stays next to the title.
|
|
164
|
+
This needs browser support for CSS `:has()`. Without it, the toggle stays in the header row and
|
|
165
|
+
can overflow on narrow screens.
|
|
166
|
+
|
|
167
|
+
## Marking content
|
|
168
|
+
|
|
169
|
+
The plugin hides elements whose `data-fcm-hide` attribute (or the attribute set in `attribute`)
|
|
170
|
+
includes the active mode. The value is a space-separated list of mode names. There are three ways
|
|
171
|
+
to add the attribute.
|
|
172
|
+
|
|
173
|
+
### 1. `attr_list` attributes
|
|
174
|
+
|
|
175
|
+
With the [`attr_list`](https://python-markdown.github.io/extensions/attr_list/) extension enabled
|
|
176
|
+
in `markdown_extensions`, add the attribute to a heading, paragraph, list item, or admonition:
|
|
177
|
+
|
|
178
|
+
```markdown
|
|
179
|
+
## Decorators {: data-fcm-hide="essentials" }
|
|
180
|
+
|
|
181
|
+
This section is hidden in Essentials mode.
|
|
182
|
+
|
|
183
|
+
## Functions
|
|
184
|
+
|
|
185
|
+
This paragraph is hidden in Essentials mode. The rest of the section is shown.
|
|
186
|
+
{: data-fcm-hide="essentials" }
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
A marked heading hides its whole section, up to the next heading of the same or higher level. Any
|
|
190
|
+
other marked element hides only itself.
|
|
191
|
+
|
|
192
|
+
### 2. HTML wrappers
|
|
193
|
+
|
|
194
|
+
To hide content that isn't a single block, wrap it in a `<div>` or `<span>` with the attribute. On
|
|
195
|
+
a `<div>`, add `markdown="block"` (from the
|
|
196
|
+
[`md_in_html`](https://python-markdown.github.io/extensions/md_in_html/) extension) so the Markdown
|
|
197
|
+
inside it is still rendered:
|
|
198
|
+
|
|
199
|
+
```markdown
|
|
200
|
+
<div data-fcm-hide="essentials" markdown="block">
|
|
201
|
+
This block is hidden in Essentials mode.
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
This sentence has <span data-fcm-hide="essentials">an inline aside</span> in it.
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 3. CSS for multi-paragraph list items
|
|
208
|
+
|
|
209
|
+
The plugin hides only the marked element and, for a heading, its section. It doesn't hide parent
|
|
210
|
+
elements. For a list item with more than one paragraph, such as a card in a Material
|
|
211
|
+
[card grid](https://squidfunk.github.io/mkdocs-material/reference/grids/#using-card-grids),
|
|
212
|
+
`attr_list` can only mark the first paragraph, not the `<li>`. To hide the whole item, add a CSS
|
|
213
|
+
rule that uses the `data-fcm-mode` attribute the plugin sets on `<html>`:
|
|
214
|
+
|
|
215
|
+
```css
|
|
216
|
+
html[data-fcm-mode="essentials"] .grid.cards > ul > li:has(> p[data-fcm-hide~="essentials"]) {
|
|
217
|
+
display: none;
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The default mode is the one active on a reader's first visit. Content can be hidden in it like in
|
|
222
|
+
any other mode.
|
|
223
|
+
|
|
224
|
+
The attribute has no effect without the plugin. If you remove the plugin, all marked content is
|
|
225
|
+
shown.
|
|
226
|
+
|
|
227
|
+
## Linking to hidden content
|
|
228
|
+
|
|
229
|
+
When a link points to content that's hidden in the reader's current mode, the plugin switches to
|
|
230
|
+
the nearest mode that shows it. This works whether the target is marked itself or is hidden
|
|
231
|
+
because of something around it, such as a subheading inside a hidden section or a heading inside
|
|
232
|
+
a hidden `<div>`.
|
|
233
|
+
|
|
234
|
+
Distance is measured from the current mode's position in the `modes` list: the plugin checks the
|
|
235
|
+
modes one position away, then two, and so on. If two modes are the same distance away, it picks
|
|
236
|
+
the one later in the list.
|
|
237
|
+
|
|
238
|
+
For example, with the modes `beginner`, `intermediate`, and `advanced`, a heading marked
|
|
239
|
+
`data-fcm-hide="beginner advanced"` is shown only in Intermediate. Following a link to it from
|
|
240
|
+
Beginner or Advanced switches to Intermediate. A heading marked `data-fcm-hide="beginner"` is
|
|
241
|
+
shown in both Intermediate and Advanced, so a Beginner reader following a link to it switches to
|
|
242
|
+
Intermediate, the closer of the two.
|
|
243
|
+
|
|
244
|
+
If the target is hidden in every mode, the mode doesn't change.
|
|
245
|
+
|
|
246
|
+
### Setting the mode from a URL
|
|
247
|
+
|
|
248
|
+
Set `query_param` to let a link choose the mode:
|
|
249
|
+
|
|
250
|
+
```yaml
|
|
251
|
+
query_param: mode
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
https://example.com/some-page/?mode=advanced
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Opening this link switches to Advanced mode and saves it to `localStorage`, so the mode stays the
|
|
259
|
+
same on other pages. Unrecognized values are ignored. To also jump to a section, add a heading
|
|
260
|
+
anchor: `?mode=advanced#some-heading`.
|
|
261
|
+
|
|
262
|
+
The parameter stays in the URL after the mode is applied. With Material's `navigation.instant`
|
|
263
|
+
feature, Material rewrites the page's navigation links as absolute URLs, including the query
|
|
264
|
+
string, before the plugin runs. If the plugin removed the parameter afterward, those links would no
|
|
265
|
+
longer match the current URL, and clicking one would reload the page instead of scrolling to the
|
|
266
|
+
heading.
|
|
267
|
+
|
|
268
|
+
As a result, analytics tools that count page views by URL record the landing page as
|
|
269
|
+
`/some-page/?mode=advanced`, separately from `/some-page/`. Only the page opened from the link is
|
|
270
|
+
affected. To track modes without relying on the URL, use the event described in
|
|
271
|
+
["Analytics"](#analytics).
|
|
272
|
+
|
|
273
|
+
## Styling
|
|
274
|
+
|
|
275
|
+
The toggle's CSS is controlled with custom properties. Override them in your `extra_css` file on
|
|
276
|
+
`#fcm-toggle`, or on a parent element such as `:root`:
|
|
277
|
+
|
|
278
|
+
```css
|
|
279
|
+
#fcm-toggle {
|
|
280
|
+
--fcm-accent: #2e7d32; /* border and highlight color (default: currentColor) */
|
|
281
|
+
--fcm-track-bg: #fdf6e3; /* toggle background (default: transparent) */
|
|
282
|
+
--fcm-active-fg: #fdf6e3; /* text color of the active option (default: Canvas) */
|
|
283
|
+
--fcm-radius: 1rem; /* corner radius of the toggle and highlight (default: 1rem) */
|
|
284
|
+
--fcm-height: 1.2rem; /* toggle height (default: 1.2rem) */
|
|
285
|
+
--fcm-font-size: 0.6rem; /* label font size (default: 0.6rem) */
|
|
286
|
+
--fcm-icon-size: 0.7rem; /* icon size (default: 0.7rem) */
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
For other changes, target the classes `.fcm-toggle`, `.fcm-highlight`, `.fcm-option`,
|
|
291
|
+
`.fcm-option--icon`, and `.fcm-label`. The script sets the highlight's `left` and `width` inline to
|
|
292
|
+
match the active option.
|
|
293
|
+
|
|
294
|
+
## Accessibility
|
|
295
|
+
|
|
296
|
+
- The color properties aren't checked for contrast. Check your color choices against WCAG
|
|
297
|
+
contrast requirements.
|
|
298
|
+
- With `collapse_labels: true`, give every mode an `icon`. Below 45em the labels are hidden
|
|
299
|
+
visually but still read by screen readers, so a mode without an icon appears as an empty button.
|
|
300
|
+
- Each option is a toggle button with `aria-pressed` and its own tab stop. The toggle doesn't use
|
|
301
|
+
the ARIA radio group pattern, which has a single tab stop and arrow-key navigation.
|
|
302
|
+
- The [card grid CSS rule](#3-css-for-multi-paragraph-list-items) and the toggle's mobile row both
|
|
303
|
+
need CSS `:has()` (Chrome 105+, Safari 15.4+, Firefox 121+). In older browsers, the card's first
|
|
304
|
+
paragraph is still hidden but the rest of the card isn't, and the toggle doesn't move to its own
|
|
305
|
+
row.
|
|
306
|
+
- Transitions are turned off when `prefers-reduced-motion: reduce` is set.
|
|
307
|
+
- Hidden content uses `display: none`, which removes it from the accessibility tree.
|
|
308
|
+
- If the plugin's JavaScript doesn't run, no content is hidden.
|
|
309
|
+
|
|
310
|
+
## Active mode attribute
|
|
311
|
+
|
|
312
|
+
The plugin sets `data-fcm-mode` on `<html>` to the name of the active mode. Use it to style other
|
|
313
|
+
elements or to read the mode from other scripts.
|
|
314
|
+
|
|
315
|
+
## Analytics
|
|
316
|
+
|
|
317
|
+
The plugin doesn't add the mode to URLs, so page views counted by URL don't include it. To record
|
|
318
|
+
the mode, listen for the `fcm:modechange` event on `document`. `event.detail` contains `mode` and
|
|
319
|
+
`previousMode`:
|
|
320
|
+
|
|
321
|
+
```js
|
|
322
|
+
document.addEventListener("fcm:modechange", (event) => {
|
|
323
|
+
const { mode, previousMode } = event.detail;
|
|
324
|
+
// Google Analytics (gtag.js)
|
|
325
|
+
gtag("event", "content_mode_change", { mode, previous_mode: previousMode });
|
|
326
|
+
// Plausible
|
|
327
|
+
plausible("Content Mode Change", { props: { mode } });
|
|
328
|
+
});
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
The event fires when the plugin sets the mode on page load (`previousMode` is `null`) and each time
|
|
332
|
+
the mode changes. Clicking the option that's already active doesn't fire it.
|
|
333
|
+
|
|
334
|
+
A script that loads after the plugin, such as a tag manager snippet, misses the page-load event.
|
|
335
|
+
Read `document.documentElement.dataset.fcmMode` when the script starts to get the current mode,
|
|
336
|
+
then listen for the event.
|
|
337
|
+
|
|
338
|
+
### Using a MutationObserver
|
|
339
|
+
|
|
340
|
+
You can also watch the `data-fcm-mode` attribute instead of listening for the event. With
|
|
341
|
+
`navigation.instant`, the plugin sets the attribute again on each page change even when the mode
|
|
342
|
+
hasn't changed, so this example skips repeated values:
|
|
343
|
+
|
|
344
|
+
```js
|
|
345
|
+
const html = document.documentElement;
|
|
346
|
+
let lastMode = null;
|
|
347
|
+
|
|
348
|
+
function reportMode() {
|
|
349
|
+
const mode = html.dataset.fcmMode;
|
|
350
|
+
if (!mode || mode === lastMode) return;
|
|
351
|
+
gtag("event", "content_mode_change", { mode, previous_mode: lastMode });
|
|
352
|
+
lastMode = mode;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
reportMode();
|
|
356
|
+
new MutationObserver(reportMode).observe(html, { attributeFilter: ["data-fcm-mode"] });
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## Testing
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
python3 -m venv .venv
|
|
363
|
+
source .venv/bin/activate
|
|
364
|
+
pip install -e ".[test]"
|
|
365
|
+
playwright install chromium
|
|
366
|
+
pytest
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
`tests/fixture_site/` is a small Material for MkDocs site that uses the plugin. The tests build it
|
|
370
|
+
once, serve it locally, and run Playwright against it:
|
|
371
|
+
|
|
372
|
+
- `test_behavior.py`: hiding, persistence, links to hidden content, and toggle layout.
|
|
373
|
+
- `test_accessibility.py`: axe-core checks.
|
|
374
|
+
- `test_keyboard.py`: keyboard use and focus.
|
|
375
|
+
|
|
376
|
+
axe-core is included in `tests/vendor/`, so the tests don't need network access.
|
|
377
|
+
|
|
378
|
+
## License
|
|
379
|
+
|
|
380
|
+
[MIT](LICENSE)
|