Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,12 @@ A diagram renderer, which draws flowcharts and diagrams from a plain-text descri

### Essentials / Advanced toggle

A two-option [switch](docs/javascripts/essentials_toggle.js) that lets a reader hide everything beyond a first-pass beginner curriculum. Content is opted into hiding by marking it `data-advanced="true"`:
A two-option switch, provided by the [mkdocs-audience-toggle](#mkdocs-audience-toggle) plugin (configured under `plugins:` in `mkdocs.yml`), that lets a reader hide everything beyond a first-pass beginner curriculum. Content is opted into hiding by marking it `data-fcm-hide="essentials"`:

- On a `##`/`###` heading inside a content page (e.g. functions.md's `## Decorators`), it hides that heading plus every sibling up to the next heading of the same or higher level, and removes the matching entry from the `toc.integrate` sidebar — so there's no dead nav link to something that's hidden.
- On a homepage card-grid row, it hides just that row; `data-advanced="card"` hides an entire homepage card instead, for a whole linked page rather than one section.
- On a homepage card-grid row, it hides just that row. A whole homepage card hides too, once its first paragraph (the only one attr_list can attach the marker to) carries the marker — extra.css has a small `:has()` rule that extends that into hiding the entire `<li>`, since the plugin itself only hides the exact element marked.

Each marking is independent — there's no shared list of "advanced" topics to keep in sync, just the attribute at each spot in the Markdown. State persists in `localStorage` and applies on every page (also settable via a `?simplified=true`/`false` URL param, for sharing a pre-set link). If a visible link points at a heading that's currently hidden (e.g. collections.md's cheat-sheet table linking to `#tuples`), following it flips the toggle back to Advanced and reveals the target instead of landing on nothing.
Each marking is independent — there's no shared list of "advanced" topics to keep in sync, just the attribute at each spot in the Markdown. State persists in `localStorage` and applies on every page (also settable via a `?mode=essentials`/`advanced` URL param, for sharing a pre-set link). If a visible link points at a heading that's currently hidden (e.g. collections.md's cheat-sheet table linking to `#tuples`), following it flips the toggle back to Advanced and reveals the target instead of landing on nothing.

Some examples of content that is hidden while in "Essentials" mode, while a student is first learning to program:

Expand Down Expand Up @@ -157,6 +157,31 @@ plugins:

I extracted it because it fills a real, previously-requested gap — someone asked for exactly this in a [Material for MkDocs discussion](https://github.com/squidfunk/mkdocs-material/discussions/4765) and the maintainer's answer was horizontal scroll, not an expanded layout — and nothing on PyPI already does it (checked against the existing nav/dropdown/sidebar plugins first). It reads a site's `nav:` tree directly at runtime, so it needs no plugin-specific configuration for the common case, and falls back to Material's own theme variables for styling so it looks reasonable on any palette out of the box. This site is its first real consumer — see `mkdocs.yml`'s `plugins:` list and `extra.css`'s `--md-nested-tabs-*` overrides for how it's wired in here.

### [mkdocs-audience-toggle](https://pypi.org/project/mkdocs-audience-toggle/)

A header toggle that switches between content modes, such as Essentials and Advanced, and hides any content marked for the modes it shouldn't appear in. It started as this site's own Essentials/Advanced JavaScript, and I rewrote it as a published plugin that supports any number of modes, each with its own label and optional icon.

```bash
pip install mkdocs-audience-toggle
```

```yaml
plugins:
- audience_toggle:
modes:
- name: essentials
label: Essentials
- name: advanced
label: Advanced
default: true
```

```markdown
## Decorators {: data-fcm-hide="essentials" }
```

Material has no built-in way to tailor a page to different readers. The plugin hides a marked heading together with its whole section and its table of contents entry. It switches to the nearest mode that shows the content when a link points to something hidden. It also collapses to icons or moves to its own row on narrow screens. It includes its own Playwright and axe-core tests. See the [plugin's README](https://github.com/luka-sherman/mkdocs-audience-toggle) for all options. This site's setup is under `audience_toggle` in `mkdocs.yml`, with color overrides in `extra.css`'s `#fcm-toggle` rule.

## Theme

### Custom CSS
Expand Down Expand Up @@ -226,9 +251,9 @@ The standard Python test runner, which discovers `test_*` functions across the r
focus order, the output live region); and keyboard navigation (skip link, a visible focus
ring on every tab stop, no positive tabindex, palette toggle reachable). It's the heaviest
part of the suite — needs `playwright install chromium` above and launches a real browser.
- `tests/test_essentials_toggle.py` is a browser test (same Playwright setup) for the
- `tests/test_content_mode_toggle.py` is a browser test (same Playwright setup) for the
Essentials/Advanced toggle described above: the default (Advanced) state, that
`?simplified=true` hides marked content and carries onto a page's own heading + TOC entry,
`?mode=essentials` hides marked content and carries onto a page's own heading + TOC entry,
and the link-recovery behavior for a visible link into hidden content.

### [Playwright](https://playwright.dev/)
Expand Down
51 changes: 27 additions & 24 deletions STRUCTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,35 +196,38 @@ this reason.
anchor/link it can't resolve; treat a clean build as the actual pass/fail check for this list,
since hand-checked slugs are easy to get subtly wrong (trailing punctuation, duplicate-heading
suffixes, etc).
- **Marking content "advanced" for the Simplify toggle** — the header's "Essentials" / "Advanced"
segmented control (both labels always visible, on every page) hides content marked
`data-advanced="true"`, at one of two granularities. Each spot that should hide is marked
directly, in its own markdown source — there's no derived/shared list, so a new advanced entry
needs tagging in every place it should disappear from:
- **Marking content "advanced" for the Essentials/Advanced toggle** — the header's segmented
control (both labels always visible, on every page) is provided by the
`mkdocs-audience-toggle` plugin (configured under `plugins:` in `mkdocs.yml`; see
CLAUDE.md's "Planned extraction" section) and hides content marked `data-fcm-hide="essentials"`, at
one of two granularities. Each spot that should hide is marked directly, in its own markdown
source — there's no derived/shared list, so a new advanced entry needs tagging in every place
it should disappear from:
- **A homepage keyword-link row** — the bolded keyword plus its row of related links (e.g.
`functions.md#decorators` or `collections.md#sets`, in `index.md`) — append
`{: data-advanced="true" }` on its own line directly after the row, at the same indentation,
with no blank line before it (attr_list attaches it to that paragraph, which
`.simplify-active [data-advanced]` then hides).
`{: data-fcm-hide="essentials" }` on its own line directly after the row, at the same indentation,
with no blank line before it (attr_list attaches it to that paragraph, which the plugin then
hides directly).
- **The matching heading on the actual content page** — e.g. `functions.md`'s
`## Decorators { data-advanced="true" }` — append `{ data-advanced="true" }` directly on the
heading line (same attr_list convention as `data-card-link="skip"` above). This hides that
`## Decorators { data-fcm-hide="essentials" }` — append `{ data-fcm-hide="essentials" }` directly on
the heading line (same attr_list convention as `data-card-link="skip"` above). This hides that
heading, everything up to the next heading of the same or higher level, and its
integrated-TOC sidebar entry, on that page specifically. Tag the homepage row and the
content-page heading independently — `docs/javascripts/essentials_toggle.js` doesn't infer one
from the other, by design (simpler and more robust than deriving a map at runtime).
- **A whole homepage card** (e.g. the OpenCV card) — append `{: data-advanced="card" }` the
content-page heading independently — the plugin doesn't infer one from the other, by design
(simpler and more robust than deriving a map at runtime).
- **A whole homepage card** (e.g. the OpenCV card) — append `{: data-fcm-hide="essentials" }` the
same way, right after the card's first paragraph (the icon + title link, e.g.
`[__OpenCV__](...)`). `.simplify-active .grid.cards > ul > li:has(> p[data-advanced="card"])`
in `extra.css` walks up from that paragraph to hide the whole enclosing `<li>`. This one has
no content-page equivalent — it marks a whole linked page, not a section within one, so
there's nothing on that page itself to hide.
Which value to use, and what counts as advanced/niche vs. core, is a per-editor judgment call
— there's no test enforcing it either way. See `docs/javascripts/essentials_toggle.js` for the
toggle mechanism. A page can still show a *link* to a hidden section (e.g. `collections.md`'s
own cheat-sheet table links to `#tuples` even though the "Tuples" heading is hidden) —
following such a link automatically switches back to Complete and reveals the target, so this
doesn't need special-casing when adding new advanced content.
`[__OpenCV__](...)`) — attr_list can only attach it there, not to the enclosing `<li>`, so the
plugin alone would only hide that one line. `html[data-fcm-mode="essentials"] .grid.cards > ul
> li:has(> p[data-fcm-hide~="essentials"])` in `extra.css` walks up from that paragraph to hide
the whole enclosing `<li>` too. This one has no content-page equivalent — it marks a whole
linked page, not a section within one, so there's nothing on that page itself to hide.
Which spots to mark, and what counts as advanced/niche vs. core, is a per-editor judgment call
— there's no test enforcing it either way. See the `mkdocs-audience-toggle` plugin's own
README for the toggle mechanism itself. A page can still show a *link* to a hidden section (e.g.
`collections.md`'s own cheat-sheet table links to `#tuples` even though the "Tuples" heading is
hidden) — following such a link automatically switches back to Advanced and reveals the target,
so this doesn't need special-casing when adding new advanced content.

### Admonitions (`??? type "..."`)

Expand All @@ -249,7 +252,7 @@ Pick the existing type that matches the branch, don't invent new ones without a
| `??? info` | Defining a term/concept adjacent to the page but not the topic itself. |
| `??? failure` | The negative counterpart to a `success` branch — "this didn't work, here's what to do about it" (e.g. workspace.md's "download Python here" branch when `python --version` doesn't show 3.x.x). |
| `??? ai` | Opinion/meta content specifically about learning with or around AI (e.g. index.md's FAQ tabs on whether/how to use AI while learning) — not used for teaching content about Python itself. |
| `??? efficiency` | A runtime/space aside naming the cost behind a choice already shown in prose (e.g. list vs. set membership, `sort()` vs. `sorted()`) — usually a Big O difference, occasionally a constant-factor one (`.get()` vs. two hash lookups, vectorized NumPy vs. a Python loop) where it's still worth flagging but doesn't change the O(...) class. Wrap it in `<div data-advanced="true" markdown="block">` on a page that participates in the Essentials/Advanced toggle (skip it on a page that doesn't, like the library reference pages), and close with a link to [style.md's "Efficiency"](docs/practices/style.md#efficiency) section. Formalizes a tradeoff the surrounding prose already states in plain language; doesn't introduce the tradeoff for the first time. |
| `??? efficiency` | A runtime/space aside naming the cost behind a choice already shown in prose (e.g. list vs. set membership, `sort()` vs. `sorted()`) — usually a Big O difference, occasionally a constant-factor one (`.get()` vs. two hash lookups, vectorized NumPy vs. a Python loop) where it's still worth flagging but doesn't change the O(...) class. Wrap it in `<div data-fcm-hide="essentials" markdown="block">` on a page that participates in the Essentials/Advanced toggle (skip it on a page that doesn't, like the library reference pages), and close with a link to [style.md's "Efficiency"](docs/practices/style.md#efficiency) section. Formalizes a tradeoff the surrounding prose already states in plain language; doesn't introduce the tradeoff for the first time. |
| `!!! example` | An always-open side-by-side comparison the reader is meant to see without a click, not a branch — e.g. "how to loop each type," showing every collection type's loop pattern in one visible table. |

Default to collapsed (`???`), not always-open (`!!!`) — an always-open admonition competes with
Expand Down
Loading
Loading