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
29 changes: 26 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,29 @@ plugins:

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.

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

Builds the homepage's quick-reference grid from headings flagged across the site. Each page becomes a card: its title, a description, and a bold line per flagged `##` heading followed by links to the flagged headings under it. The grid on this homepage used to be written by hand, with every link typed out; renaming a heading silently broke its link. Now each heading carries its own flag, so the grid is rebuilt from the pages on every build.

```bash
pip install mkdocs-cheatsheet
```

```yaml
plugins:
- cheatsheet:
button: true
```

```markdown
<!-- cheatsheet -->

## Lists { cs="lists, item" }
#### Add item { cs="append, extend, insert" }
```

A section with its own cheatsheet page, like Libraries here, shows up on the homepage as title-and-description cards only. The optional header button replaces the logo with a "Cheatsheet" link home. It includes its own Playwright and axe-core tests. See the [plugin's README](https://github.com/luka-sherman/mkdocs-cheatsheet) for all options. This site's setup is under `cheatsheet` in `mkdocs.yml`, with the flag rules in STRUCTURE.md's "Cheatsheet flags" section.

## Theme

### Custom CSS
Expand All @@ -207,11 +230,11 @@ I found myself writing so much content for this, and needing to jump between dif
end with a period, numbered walkthroughs start at `0.`, short (1–2 word) subheadings because
`toc.integrate` mirrors them verbatim into the sidebar.
- **Where information goes** — the decision rules for heading level vs. admonition vs. glossary
entry vs. footnote, with a table of which `??? type` to use for what, plus how the homepage
keyword deep-links in `index.md` have to cover every heading.
entry vs. footnote, with a table of which `??? type` to use for what, plus how headings are flagged
for the homepage cheatsheet.

The mechanically-checkable subset of these rules (heading case, list-start number, admonition
types, `python-ref` comment format, homepage link coverage, clean `mkdocs build`) is enforced
types, `python-ref` comment format, clean `mkdocs build`) is enforced
by `tests/test_structure.py`; the rest need editorial judgment.

## Running locally
Expand Down
115 changes: 37 additions & 78 deletions STRUCTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,85 +143,44 @@ staying inline.**
each splitting into `#### Arithmetic`, `#### Convert`, etc.). Used throughout content pages, not
just `index.md`'s homepage category boxes or library pages.

### Homepage keyword deep-links (`index.md`, `libraries/index.md`)

Each card in `index.md`'s "What's inside" grid ends with a row of `` [`keyword`](page.md#anchor) ``
links — one per concept the page teaches, so a reader can jump straight to the specific thing
they're after instead of landing on the page and hunting. Library pages (`libraries/*.md`) are
the exception: their keyword links live only on their card in `libraries/index.md`'s own grid,
not on the main `index.md` — the top-level cards link to `libraries/<page>.md` as a whole,
without a per-heading keyword row. `tests/test_homepage_keyword_links_cover_all_headings` checks
each library subpage's headings against `libraries/index.md` instead of `index.md` for exactly
this reason.

- **Coverage — every `##` and `###` heading needs an entry.** Not just "the topic is
represented somewhere nearby" — each heading gets its own link, using its own anchor. A page
with 5 `##` sections needs at least 5 entries. If two headings share slug text (e.g.
`collections.md`'s three "Access items" sections), MkDocs disambiguates the anchor with a
suffix (`#access-items`, `#access-items_1`, ...) — confirm the real slug in the built HTML
(`grep -n 'id="' site/<page>/index.html`) rather than guessing, since the suffix isn't
predictable from the heading text alone.
- **Also include concrete Python syntax the page teaches, even without its own heading** — a
method or function genuinely explained in prose (e.g. `dict.get()`, explained inline under
Dictionaries' "Access items" on `collections.md`) is exactly the kind of thing a reader
searches for by name. Link it to the heading whose content covers it.
- **Exception: content inside a `???` admonition has no anchor of its own** (admonitions
aren't headings — see "Admonitions" below), so syntax explained only inside one (e.g.
`sort()` inside collections.md's "Sort lists" tip, `zip()` inside loops.md's tip) can't be
linked precisely. Link to the nearest real heading above it instead and accept the
imprecision (e.g. `.sort()` → `collections.md#loop-lists`, the section the tip sits inside)
— never invent an anchor that doesn't exist. If nothing precedes it (e.g. `type()` /
`isinstance()` sit in a tip before types.md's first `##`), link the bare page with no
fragment rather than a made-up one.
- Don't link *every* method mentioned in passing — only ones a page is actually teaching.
`print()` reappearing as an example call on `errors.md` or `style.md` isn't being taught
there (that's `foundations.md`'s job); only that page's own card should link it.
- **Skip purely narrative/descriptive subheadings** — "What do you see when a program runs?",
"How do variables work?", "Structure of a print() statement" name a *question*, not a
reusable keyword. If a heading doesn't name a concrete concept or piece of syntax, it doesn't
need a card entry even though it still needs to exist as a heading per the rules above... to
be clear, the heading itself is still fine on the page; it just doesn't earn a homepage link.
Mark it `{ data-card-link="skip" }` (attr_list, appended to the heading line) so
`tests/test_homepage_keyword_links_cover_all_headings` knows the omission is deliberate
rather than flagging it as a gap. Any other heading just needs *a* link to its anchor — the
test doesn't check the link's text, so renaming an entry (or the heading) is a manual concern.
- **Bold `##` entries stay in page order; their plain children are alphabetized.** Each `##`
heading gets its own bold entry (e.g. `` [**`def`**](functions.md#defining-a-function) ``),
and those bold entries keep the page's own top-to-bottom heading order — don't reshuffle them.
The flat list of plain (non-bold) links under a bold entry — its `###`/`####` children, plus
any bare-syntax entries with no heading of their own — sorts alphabetically by link text
(case-insensitive), not by importance or page position. Symbols sort before letters (plain
ASCII order), so a line like `` [`+= -= *= /=`] `` lands ahead of `` [`abs`] ``.
- **Verify with a real build, not by eye** — `mkdocs build` prints a `WARNING` for every
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).
### Cheatsheet flags (`index.md`, `libraries/index.md`)

The card grids on `index.md` and `libraries/index.md` are generated by the
[mkdocs-cheatsheet](https://github.com/luka-sherman/mkdocs-cheatsheet) plugin from flags on each
content page's headings. Neither index file lists links by hand; each holds a
`<!-- cheatsheet -->` placeholder. `libraries/index.md` has its own placeholder, so the homepage
shows library pages as title-and-description cards only, and their links appear only on
`libraries/index.md`.

- **Flag syntax.** Append the marker inside the heading's attr_list braces:
`## Integers { cs="integers" }`. `{ cs }` uses the heading text. Several comma-separated labels
each become a link to the same heading: `#### Add item { cs="append, extend, insert" }`.
- **What each level becomes.** Labels on the `#` title show at the top of the card and link to the
top of the page (e.g. `isinstance`, `type`). A flagged `##` starts a bold "label:" line; its
extra labels become links in that line. Flagged `###`/`####` headings become the links under
the nearest `##`. A `##` line only shows its bold label when the `##` itself is flagged.
- **What to flag.** Flag headings that name a concrete concept or piece of syntax a reader would
look up by name. Leave narrative headings ("What do you see when a program runs?", "Going
further") unflagged. Syntax taught in prose without its own heading (e.g. `dict.get()`) goes on
the nearest heading whose content covers it, as an extra label. Only flag what a page actually
teaches; `print()` appearing as an example on `errors.md` isn't taught there.
- **Order.** `##` lines keep page order. Links within a line sort alphabetically
(`sort: alphabetical` in `mkdocs.yml`), symbols before letters.
- **Card text.** Each page's front matter sets `cheatsheet_description` (falling back to
`description`), plus `cheatsheet_title`, `cheatsheet_icon` or `cheatsheet_title_suffix` (the
built-in/third-party badge on library cards) where the defaults don't fit.
- **Essentials mode.** A flagged heading that carries `data-fcm-hide="essentials"` passes it to
its cheatsheet line or link, so the audience toggle hides both together. A whole card is
hidden with `cheatsheet_attrs: {data-fcm-hide: essentials}` in front matter.
- **Verify with a real build.** A renamed heading moves its flag with it, so the cheatsheet can't
go stale, but `mkdocs build` still prints a `WARNING` for any broken link elsewhere.
- **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-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-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 — 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__](...)`) — 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.
control is provided by the `mkdocs-audience-toggle` plugin (configured under `plugins:` in
`mkdocs.yml`) and hides content marked `data-fcm-hide="essentials"`. On a content page, append
`{ data-fcm-hide="essentials" }` to the heading line (e.g. `functions.md`'s
`## Decorators { data-fcm-hide="essentials" }`). This hides that heading, everything up to the
next heading of the same or higher level, and its sidebar entry. The cheatsheet picks the
marker up from the heading, so there's no second place to tag.
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.
Expand Down
16 changes: 8 additions & 8 deletions docs/about.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ Python is often the fastest language to write *correct* code in, even though it'

## Why I built this

My name's Luka, I'm a software engineer and Intro Python teacher.
My name's Luka, I'm a software engineer and Intro Python teacher.

*Python Field Guide* is a free, in-browser reference — most code blocks are editable and runnable directly on the page. It started as a few quick-reference explanations for students working on their first programs, and evolved into this site.
*Python Field Guide* is a free, in-browser reference — most code blocks are editable and runnable directly on the page. It started as a few quick-reference explanations for students working on their first programs, and evolved into this site.

I couldn't find a site my students would consistently use that had:
I couldn't find a site my students would consistently use that had:

- simple explanations for beginners without technical jargon
- no advanced topics that intimidate or overwhelm beginners
Expand All @@ -37,13 +37,13 @@ I couldn't find a site my students would consistently use that had:

It's built for learners — self-taught, students in an intro course, or anyone who wants one combined reference to work through start to finish, instead of a scattered pile of search results.

I'm hoping this can be a helpful cheatsheet for others to quickly reference syntax and structures.
I'm hoping this can be a helpful cheatsheet for others to quickly reference syntax and structures.

</div>

<div class="pfg-section" markdown="block">

## About me
## About me

I build software and spend a lot of time thinking about the small interaction details that decide whether something actually gets used or just abandoned — I've always liked designing and building things that solve a need.

Expand Down Expand Up @@ -74,11 +74,11 @@ For more advanced Python:

## Helpful feedback

Spotted a mistake, or want to see something added? Let me know!
Spotted a mistake, or want to see something added? Let me know!

This *isn't* meant to be a comprehensive Python guide, it's just my self-published notes.
This *isn't* meant to be a comprehensive Python guide, it's just my self-published notes.

**Please be nice, I'm just one human out here doing my best.**
**Please be nice, I'm just one human out here doing my best.**

<form action="https://formspree.io/f/mgogjdop" method="POST" class="pt-feedback-form">
<input type="hidden" name="_next" value="https://pythonfieldguide.com/thanks.html">
Expand Down
Loading
Loading