diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 6ff20ff..a35d0c3 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -28,7 +28,7 @@ body: attributes: label: DocSprout version description: Output of `docsprout --version`. - placeholder: docsprout 1.2.1 + placeholder: docsprout 1.2.2 validations: required: true - type: input diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 60eb381..a23e665 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -32,7 +32,7 @@ jobs: - uses: actions/setup-python@v7 with: python-version: '3.11' - - run: python -m pip install "git+https://github.com/ikelaiah/docsprout.git@v1.2.1" + - run: python -m pip install "git+https://github.com/ikelaiah/docsprout.git@v1.2.2" - run: docsprout check - if: inputs.versioned run: docsprout check-release diff --git a/CHANGELOG.md b/CHANGELOG.md index 803f44d..3edbc5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## 1.2.2 + +A reading and link-validation patch. No configuration, route, machine-format, +public CSS token or dependency change is required. Update package and workflow +pins to `v1.2.2`, then rebuild. + +### Changed + +- Add a keyboard skip link to every generated page. On phones, the sticky + header reduces its brand row after scrolling while keeping search and + settings available. +- Measure reading progress against article content instead of the whole page. +- Lead the qualification guide with current support information and clarify + the maintainer architecture path. + +### Fixed + +- Reject broken same-page heading fragments during `check` and `build`. +- Reject links to missing or unpublished Markdown instead of copying a private + Markdown file into the output as an asset. + ## 1.2.1 A reading-layout patch. No configuration, route, theme-specific CSS or public diff --git a/README.md b/README.md index 905355b..e134f62 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 🌱 DocSprout -[](https://github.com/ikelaiah/docsprout/actions/workflows/ci.yml?query=branch%3Amain) [](https://ikelaiah.github.io/docsprout/1.2.1/) [](https://github.com/ikelaiah/docsprout/releases/latest) [](https://github.com/ikelaiah/docsprout/blob/main/docs/qualification.md#supported-python-versions) [](https://github.com/ikelaiah/docsprout/blob/main/LICENSE) +[](https://github.com/ikelaiah/docsprout/actions/workflows/ci.yml?query=branch%3Amain) [](https://ikelaiah.github.io/docsprout/1.2.2/) [](https://github.com/ikelaiah/docsprout/releases/latest) [](https://github.com/ikelaiah/docsprout/blob/main/docs/qualification.md#supported-python-versions) [](https://github.com/ikelaiah/docsprout/blob/main/LICENSE)  @@ -23,7 +23,7 @@ Open a terminal in your project's top-level folder—the folder that normally contains files such as `README.md`, `src/` or `.git/`—and run: ```bash -python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.2.1.zip" +python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.2.2.zip" docsprout init docsprout serve ``` diff --git a/docs/architecture.md b/docs/architecture.md index dd81521..363c406 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,12 +1,36 @@ # Architecture -DocSprout has six small layers: configuration discovery, validation, safe -Markdown rendering, static-site generation, brand and contrast derivation -(`palette.py` proves link and focus colours against every reading surface), and -immutable-version orchestration. -The package carries the CSS and browser code so consuming projects do not copy -the implementation. `build` renders a working tree for preview; `build-all` -archives every declared Git tag or full SHA into an isolated temporary tree. +DocSprout is a command-line application that turns project-owned Markdown and +navigation into static files. A consuming project owns its content and visual +identity; the installed package supplies the renderer, CSS and browser code. + +## Follow one build + +1. `cli.py` chooses a command and project root. `check` calls the same builder + as `build`, but writes into a temporary directory. +2. `config.py` loads `docsprout.json` and the navigation layout, validates + paths and fields, and returns a `SiteConfig`. `layout_markdown.py` parses + `layout.md` into the same navigation shape as `layout.json`. +3. `palette.py` derives colours and checks contrast against the reading + surfaces. `build.py` assigns routes and rejects route collisions. +4. `markdown.py` renders the supported Markdown subset and assigns heading + IDs. `build.py` resolves links against published pages, validates heading + fragments and copies linked local assets. +5. `build.py` writes each page in the shared HTML shell, then writes the search + index and release metadata. `assets.py` supplies the site CSS and JavaScript; + the package also carries local KaTeX assets. + +`serve` rebuilds that same site when source files change. `audit.py` adds +publication diagnostics, including warnings about image descriptions and +heading structure. `versions.py` checks immutable release references and +builds historical releases from isolated Git archives. `safety.py` limits +replacement of existing output to DocSprout-owned directories. + +For a first code change, start with the command in `cli.py`, follow its call +into the owning module above, and read the corresponding tests listed in the +repository's `CONTRIBUTING.md` code map. Run the focused test while editing, +then the full suite. Changes to routes, configuration, generated formats or +command behavior also need a review of the contract below. ## Compatibility surface @@ -53,6 +77,6 @@ needs no custom CSS; the custom stylesheet mechanism is a deliberately bounded escape hatch whose inclusion mechanics DocSprout owns and whose accessibility the author owns. -Downstream projects should pin released tags such as `v1.2.1`, never the main +Downstream projects should pin released tags such as `v1.2.2`, never the main branch. The stable boundary and deprecation policy are in [Machine-readable contracts](machine-contracts.md). diff --git a/docs/beginners-guide.md b/docs/beginners-guide.md index f612c8d..b2ea329 100644 --- a/docs/beginners-guide.md +++ b/docs/beginners-guide.md @@ -26,7 +26,7 @@ that normally contains `README.md` or `src/`. Open a terminal in your project folder and run: ```bash -python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.2.1.zip" +python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.2.2.zip" ``` Then check that the command is available: @@ -254,7 +254,7 @@ appear in the navigation. in [How DocSprout renders Markdown](markdown-showcase.md). - Learn the three configuration files in [Configuration](configuration.md). - Copy a small working project from the - [minimal example](https://github.com/ikelaiah/docsprout/tree/v1.2.1/examples/minimal). + [minimal example](https://github.com/ikelaiah/docsprout/tree/v1.2.2/examples/minimal). - When you truly want a public site, choose the simpler or historical path in [GitHub Pages](github-pages.md). - Look up unfamiliar words in the [glossary](glossary.md). diff --git a/docs/building.md b/docs/building.md index 6bc62ea..6d3b8cd 100644 --- a/docs/building.md +++ b/docs/building.md @@ -24,7 +24,7 @@ asset is local, and `index.html` opens directly in a browser. ## Ship a ZIP ```bash -docsprout build --release 1.2.1 --offline-archive dist/docsprout-docs-1.2.1.zip +docsprout build --release 1.2.2 --offline-archive dist/docsprout-docs-1.2.2.zip ``` The release label used for an offline archive must start with a letter or diff --git a/docs/ci-cd.md b/docs/ci-cd.md index 14a0f6b..385535d 100644 --- a/docs/ci-cd.md +++ b/docs/ci-cd.md @@ -40,7 +40,7 @@ jobs: with: python-version: "3.12" - name: Install DocSprout - run: python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.2.1.zip" + run: python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.2.2.zip" - name: Validate the site run: docsprout check - name: Audit publications @@ -52,7 +52,7 @@ jobs: path: audit.json ``` -Pin a released tag (`v1.2.1` above), never a branch. `--format json` writes the +Pin a released tag (`v1.2.2` above), never a branch. `--format json` writes the machine-readable report; the artifact keeps it after the job, including when the audit fails. @@ -76,7 +76,7 @@ is yours. The same checks in GitLab CI: docs-checks: image: python:3.12 script: - - python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.2.1.zip" + - python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.2.2.zip" - docsprout check - docsprout audit --strict ``` diff --git a/docs/customisation.md b/docs/customisation.md index 887bece..4a1a05f 100644 --- a/docs/customisation.md +++ b/docs/customisation.md @@ -70,8 +70,8 @@ identity asset: Fields that are not relevant here, such as `banner` and `homepage`, are omitted for brevity. You can inspect the complete maintained -[`docs/docsprout.json`](https://github.com/ikelaiah/docsprout/blob/v1.2.1/docs/docsprout.json) -and its [local SVG asset](https://github.com/ikelaiah/docsprout/blob/v1.2.1/docs/assets/docsprout-mark.svg). +[`docs/docsprout.json`](https://github.com/ikelaiah/docsprout/blob/v1.2.2/docs/docsprout.json) +and its [local SVG asset](https://github.com/ikelaiah/docsprout/blob/v1.2.2/docs/assets/docsprout-mark.svg). On generated DocSprout pages, purple is the accent and highlight colour; the potted sprout mark (green leaves, brown soil and a beige pot) appears immediately before the project name in the top header; diff --git a/docs/existing-repository.md b/docs/existing-repository.md index f8ee2f7..a052a89 100644 --- a/docs/existing-repository.md +++ b/docs/existing-repository.md @@ -18,7 +18,7 @@ Run both commands from the repository's top-level folder—the one that contains `README.md` or `.git/`: ```bash -python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.2.1.zip" +python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.2.2.zip" docsprout init ``` diff --git a/docs/github-pages.md b/docs/github-pages.md index 653db75..b67252f 100644 --- a/docs/github-pages.md +++ b/docs/github-pages.md @@ -48,7 +48,7 @@ from feature branches do not deploy Pages. workflow are current, it makes no repository changes. The generated workflow carries a DocSprout ownership marker and pins the reusable -workflow to the installed release, such as `@v1.2.1`. It never follows +workflow to the installed release, such as `@v1.2.2`. It never follows `@main`. If a recognised managed workflow is older, ordinary setup reports the version and leaves it unchanged. Update only that workflow deliberately: @@ -101,13 +101,13 @@ jobs: contents: read pages: write id-token: write - uses: ikelaiah/docsprout/.github/workflows/publish-docs.yml@v1.2.1 + uses: ikelaiah/docsprout/.github/workflows/publish-docs.yml@v1.2.2 with: versioned: false release: latest ``` -The maintained [single-version example](https://github.com/ikelaiah/docsprout/tree/v1.2.1/examples/single-version) +The maintained [single-version example](https://github.com/ikelaiah/docsprout/tree/v1.2.2/examples/single-version) uses this form. ## Historical site for versioned projects @@ -129,7 +129,7 @@ jobs: contents: read pages: write id-token: write - uses: ikelaiah/docsprout/.github/workflows/publish-docs.yml@v1.2.1 + uses: ikelaiah/docsprout/.github/workflows/publish-docs.yml@v1.2.2 ``` Check a historical release locally after creating its tag and before pushing: @@ -140,7 +140,7 @@ docsprout check-release docsprout build-all --output build/docs-site ``` -The maintained [historical example](https://github.com/ikelaiah/docsprout/tree/v1.2.1/examples/historical) +The maintained [historical example](https://github.com/ikelaiah/docsprout/tree/v1.2.2/examples/historical) uses this form. Follow the [pre-publish checklist](pre-publish-checklist.md) for the exact release order. diff --git a/docs/historical-docs.md b/docs/historical-docs.md index 7e68ef4..71de513 100644 --- a/docs/historical-docs.md +++ b/docs/historical-docs.md @@ -71,6 +71,6 @@ the release check and full build, then push the tag. Use the exact commands in t [pre-publish checklist](pre-publish-checklist.md). The maintained -[historical example](https://github.com/ikelaiah/docsprout/tree/v1.2.1/examples/historical) +[historical example](https://github.com/ikelaiah/docsprout/tree/v1.2.2/examples/historical) demonstrates two small releases. Its automated test builds the same Git history twice and compares every generated byte. diff --git a/docs/homepage-recipes.md b/docs/homepage-recipes.md index 7110d44..8ff350e 100644 --- a/docs/homepage-recipes.md +++ b/docs/homepage-recipes.md @@ -18,7 +18,7 @@ which is a top-level setting, are `homepage` values. ## See it in DocSprout DocSprout uses the same configuration on this site. Its -[`docs/docsprout.json`](https://github.com/ikelaiah/docsprout/blob/v1.2.1/docs/docsprout.json) +[`docs/docsprout.json`](https://github.com/ikelaiah/docsprout/blob/v1.2.2/docs/docsprout.json) uses these exact values for its home-page banner and capability cards: ```json diff --git a/docs/migration.md b/docs/migration.md index c88d98e..f1d67fb 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -3,7 +3,7 @@ The 1.x upgrades are deliberately boring: v1.0.0 froze the qualified v0.18.1 surface, and every later 1.x release adds no configuration schema change. Pin the released package and workflow, then run the same checks you already use. -Current guidance targets `v1.2.1`; each section below remains authoritative for +Current guidance targets `v1.2.2`; each section below remains authoritative for its own step. ## DocKit to DocSprout (1.x rebrand) @@ -64,7 +64,7 @@ each published release manifest on immutable tags while upgrading. ## Upgrade directly from any supported 0.x release All released 0.x configurations use schema version 1. Upgrade the pinned package -and workflow to the latest 1.x release (`v1.2.1`), then use this table before +and workflow to the latest 1.x release (`v1.2.2`), then use this table before running `doctor`, `check`, and the appropriate publish build. | Starting release | Required compatibility work | @@ -96,6 +96,15 @@ segments and option-like refs, requires the current source to match `HEAD`, and requires documentation changes to be committed. These checks make existing valid manifests more dependable; they do not change generated routes. +## v1.2.1 to v1.2.2 + +No configuration or content migration is required. Update package and workflow +pins to `v1.2.2`, then rebuild. Reading pages gain a keyboard skip link, a +smaller phone header after scrolling, and article-based reading progress. +`check` and `build` now report missing same-page heading fragments and links to +unpublished Markdown. Fix the link or publish the target page in the navigation +layout before upgrading. + ## v1.2.0 to v1.2.1 No configuration or content migration is required. Update the package and @@ -275,7 +284,7 @@ the contract qualified by v0.18.1: 1. Pin the package archive or source installation to `v1.0.0` and pin the reusable Pages workflow to `@v1.0.0`; never use `main`. For a new upgrade - today, pin the latest 1.x release (`v1.2.1`) instead. + today, pin the latest 1.x release (`v1.2.2`) instead. 2. Keep `"schema_version": 1` in `docsprout.json`, `layout.json` and `versions.json`. There is no schema rewrite or generated-route migration. 3. If custom CSS still uses pre-v0.18 generic names such as `--bg`, `--text` @@ -407,9 +416,9 @@ safe, readable plain code. Version 1.0 is the formal commitment point for the contracts that v0.18.1 qualified, and later 1.x releases keep that contract. There is no schema change. -Before adopting the latest 1.x release (`v1.2.1`): +Before adopting the latest 1.x release (`v1.2.2`): -1. Upgrade the pinned package and Pages workflow to `v1.2.1` following this +1. Upgrade the pinned package and Pages workflow to `v1.2.2` following this guide's patterns (config-compatible, then rebuild). 2. Remove references to any pre-v0.18 generic theme token names (`--bg`, `--text`, `--interactive`, …) in custom CSS; the `--dk-*` names diff --git a/docs/pre-publish-checklist.md b/docs/pre-publish-checklist.md index 6aa76e0..5a9ed20 100644 --- a/docs/pre-publish-checklist.md +++ b/docs/pre-publish-checklist.md @@ -62,10 +62,10 @@ This path updates the public site from a branch such as `main`. ## Historical site This path publishes an immutable site for every listed release. In the example -below, replace `1.2.1` with your version. +below, replace `1.2.2` with your version. -1. Add release `1.2.1` and source ref `v1.2.1` to `docs/versions.json`. Set - `current` to `1.2.1`. +1. Add release `1.2.2` and source ref `v1.2.2` to `docs/versions.json`. Set + `current` to `1.2.2`. 2. Run `docsprout check`. 3. Commit all release files, including package metadata, documentation, examples and workflows, on a branch named `release/vX.Y.Z`: @@ -73,7 +73,7 @@ below, replace `1.2.1` with your version. ```bash git add pyproject.toml src/docsprout/__init__.py README.md CHANGELOG.md docs .github examples tests git diff --cached --check - git commit -m "Prepare v1.2.1 documentation" + git commit -m "Prepare v1.2.2 documentation" ``` 4. Push the qualified branch and open a PR against `main`. Wait for every @@ -83,7 +83,7 @@ below, replace `1.2.1` with your version. 5. Check out the merged `main` commit and create the tag on that exact commit: ```bash - git tag -a v1.2.1 -m "v1.2.1" + git tag -a v1.2.2 -m "v1.2.2" ``` 6. Run the release checks and historical build: @@ -100,7 +100,7 @@ below, replace `1.2.1` with your version. 8. Push the annotated tag (the merge commit is already on `main`): ```bash - git push origin v1.2.1 + git push origin v1.2.2 ``` 9. Create the GitHub Release from the tag. Confirm both CI and the Documentation @@ -111,9 +111,9 @@ below, replace `1.2.1` with your version. ## If you find a mistake If the tag exists only on your computer, correct and commit the files, delete -the local tag with `git tag -d v1.2.1`, then create it again on the corrected +the local tag with `git tag -d v1.2.2`, then create it again on the corrected commit. If the tag has been pushed, do not move or replace it. Publish the correction -as a new patch release, such as `v1.2.2`, so links and historical documentation +as a new patch release, such as `v1.2.3`, so links and historical documentation remain trustworthy. diff --git a/docs/qualification.md b/docs/qualification.md index 9aff25b..13f6de1 100644 --- a/docs/qualification.md +++ b/docs/qualification.md @@ -1,31 +1,25 @@ -# Qualification evidence for DocSprout v1.2.1 +# Qualification evidence for DocSprout v1.2.2 -DocSprout v1.2.1 improves reading-page orientation and small-screen outlines +DocSprout v1.2.2 improves keyboard access and reading-page behavior while preserving JSON and Markdown layout compatibility and the v1.0.0 stable contract. The claims below are the contract that CI and the maintained fixtures exercise. Every row names how it is verified. "Supported" means the combination is run by automated qualification on every pull request and release, not merely believed to work. -The v0.17 and v0.18 matrices are retained as historical evidence for the -contract candidate that v1.0.0 freezes. v1 adds the Five Promises assessment, -clean-room package rehearsal and the explicit compatibility policy; v1.1 adds -the rebrand compatibility evidence and the quality-gate additions; v1.1.2 adds -the typography evidence, v1.1.3 adds the documentation-quality evidence, -v1.1.4 adds the brand-hero, contrast and four-style evidence, v1.1.5 adds -the grouped-navigation and Markdown-coverage evidence, v1.1.6 adds the -image-alt escaping and custom-CSS contract evidence, v1.1.7 adds the -inline-code literalness and documentation-accuracy evidence, v1.1.8 adds -the protected math/resolver/auditor evidence, and v1.1.9 adds the audit, -preview and release-safety regressions below. v1.1.10 adds compact generated -navigation and accurate `init` publication guidance. v1.2.0 adds Markdown -layout parsing, group expansion, and a full-site migration from JSON. v1.2.1 -adds reading-page context, responsive outlines and active-section parity. - Evidence levels: **automated** rows run in CI with no browser or network dependency; **manual** rows are explicit review steps; **unavailable** rows are not claimed as completed when the required browser tooling is absent. +## Current support at a glance + +| Reader question | Current answer | Details | +| --- | --- | --- | +| Which Python versions? | Python 3.10–3.14 | [Supported Python versions](#supported-python-versions) | +| Which operating systems? | Linux, Windows and macOS | [Qualified operating systems](#qualified-operating-systems) | +| What is checked automatically? | The unit suite, package artifacts and generated-site contracts | [Package qualification](#package-qualification) and [accessibility qualification](#accessibility-qualification) | +| What still needs a person? | Browser, keyboard, touch and screen-reader checks | [Manual browser/keyboard matrix](#manual-browserkeyboard-matrix) and [known limitations](#known-limitations) | + ## Supported Python versions - Python 3.10, 3.11, 3.12, 3.13 and 3.14 on Linux, the full stable matrix. @@ -152,6 +146,23 @@ DocSprout's own built documentation: - Light/Dark/System coherence and the shared semantic token contract for Classic, Paper, E-ink and Glassmorphic +## Evidence history + +The v0.17 and v0.18 matrices are retained as historical evidence for the +contract candidate that v1.0.0 freezes. v1 adds the Five Promises assessment, +clean-room package rehearsal and the explicit compatibility policy; v1.1 adds +the rebrand compatibility evidence and the quality-gate additions; v1.1.2 adds +the typography evidence, v1.1.3 adds the documentation-quality evidence, +v1.1.4 adds the brand-hero, contrast and four-style evidence, v1.1.5 adds +the grouped-navigation and Markdown-coverage evidence, v1.1.6 adds the +image-alt escaping and custom-CSS contract evidence, v1.1.7 adds the +inline-code literalness and documentation-accuracy evidence, v1.1.8 adds +the protected math/resolver/auditor evidence, and v1.1.9 adds the audit, +preview and release-safety regressions below. v1.1.10 adds compact generated +navigation and accurate `init` publication guidance. v1.2.0 adds Markdown +layout parsing, group expansion, and a full-site migration from JSON. v1.2.1 +adds reading-page context, responsive outlines and active-section parity. + ## v0.18 additions The v0.17 matrix above is unchanged. v0.18 adds: @@ -276,6 +287,16 @@ site configuration; no schema, route or token value changes: distinguish page structure from home-page presentation, and the glossary covers the navigation, theme and command vocabulary. +## v1.2.2 additions + +- Generated-page tests cover the skip link, focusable article target, compact + phone-header styling and article-based progress calculation. +- Build tests cover missing same-page heading fragments and links to missing or + unpublished Markdown. The strict documentation audit and site build exercise + the same checks on this project's pages. +- The visual fixture checklist records the manual keyboard, phone-width and + progress checks. Browser and screen-reader checks remain manual release steps. + ## v1.2.1 additions - **Reading-page structure**: generated-page tests cover section and subsection @@ -481,11 +502,11 @@ automated contract above. | # | Check | | --- | --- | -| 1 | Tab from the address bar through search, version, style and mode controls, copy buttons, previous/next and every sidebar link; focus is always visible. | +| 1 | Tab once to reveal Skip to content, activate it, and confirm focus reaches the article below the header. Continue through search, version, style and mode controls, copy buttons, previous/next and every sidebar link; focus is always visible. | | 2 | `/` focuses search; type a query; ArrowDown/ArrowUp, Home, End, Enter and Escape behave as described; Tab leaves the result region predictably. | -| 3 | At phone width the mobile navigation opens and closes with Enter/Space on the disclosure and every section link is reachable by keyboard. | +| 3 | At phone width the mobile navigation opens and closes with Enter/Space on the disclosure and every section link is reachable by keyboard. Scroll until the brand row collapses; search and settings remain available, and keyboard focus reveals the brand link. | | 4 | Screenshot phone/tablet/desktop widths in Classic, Paper, E-ink and Glassmorphic × System, Light and Dark; nothing overlaps and no page-level horizontal scroll appears. | | 5 | With the OS reduced-motion preference on, search results, the reading-progress bar and theme changes do not animate. | | 6 | With Windows high-contrast / forced-colors enabled, focus outlines and the reading-progress indicator remain visible. | | 7 | At desktop and phone widths the home-page hero keeps its copy readable, the derived actions stay keyboard reachable, and a configured banner spans the content width without cutting its text. | -| 8 | On a long reading page, open the phone outline by touch and keyboard, confirm its current section and labelled navigation with a screen reader, follow a nested heading link, and check that the heading clears the sticky header. On desktop, confirm the rail marks the same section. | +| 8 | On a long reading page, open the phone outline by touch and keyboard, confirm its current section and labelled navigation with a screen reader, follow a nested heading link, and check that the heading clears the sticky header. On desktop, confirm the rail marks the same section. Check that reading progress completes at the article end, before page navigation and footer. | diff --git a/docs/themes.md b/docs/themes.md index d385fb7..a14ebb1 100644 --- a/docs/themes.md +++ b/docs/themes.md @@ -43,7 +43,7 @@ footer—never underlines, signalling hover with a background tint or colour shift instead, and buttons never underline. The maintained -[single-version example](https://github.com/ikelaiah/docsprout/tree/v1.2.1/examples/single-version) +[single-version example](https://github.com/ikelaiah/docsprout/tree/v1.2.2/examples/single-version) starts in Paper (`"style": "paper"`), so its built opening page shows the warm reading surface before a visitor changes the control. @@ -110,7 +110,7 @@ hexadecimal values: Test custom colours in both Light and Dark mode. Links, selected navigation and keyboard focus must remain easy to see. A preset is safer when you are unsure. The maintained -[minimal example](https://github.com/ikelaiah/docsprout/tree/v1.2.1/examples/minimal) +[minimal example](https://github.com/ikelaiah/docsprout/tree/v1.2.2/examples/minimal) uses the exact teal values shown above; its built links, selected navigation and focus state use that accent. diff --git a/docs/versions.json b/docs/versions.json index 55d65c6..bb54e86 100644 --- a/docs/versions.json +++ b/docs/versions.json @@ -1,7 +1,8 @@ { "schema_version": 1, - "current": "1.2.1", + "current": "1.2.2", "versions": [ + {"release": "1.2.2", "source_ref": "v1.2.2"}, {"release": "1.2.1", "source_ref": "v1.2.1"}, {"release": "1.2.0", "source_ref": "v1.2.0"}, {"release": "1.1.10", "source_ref": "v1.1.10"}, diff --git a/docs/visual-fixtures.md b/docs/visual-fixtures.md index 73d1351..0833fbc 100644 --- a/docs/visual-fixtures.md +++ b/docs/visual-fixtures.md @@ -30,7 +30,7 @@ errors or warnings. | View | Width | Checks | | --- | ---: | --- | -| Phone | 320px, 360px and 390px | Native navigation disclosure, visible copy control, no page overflow; the hero stacks its banner band above the copy, action buttons wrap, oversized Markdown images stay within the prose column and small badges remain intrinsic | +| Phone | 320px, 360px and 390px | Native navigation disclosure, visible copy control, no page overflow; the header loses its brand row after scrolling while search and settings stay available; the hero stacks its banner band above the copy, action buttons wrap, oversized Markdown images stay within the prose column and small badges remain intrinsic | | Tablet | 768px | Search and controls wrap cleanly, readable table scrolling, responsive Markdown images; the long-form page has a closed, keyboard-operable “On this page” disclosure after its title | | Narrow desktop | 1024px | Article measure and heading rhythm remain balanced; the long-form page uses the inline outline instead of a right rail | | Desktop | 1440px | Sidebar, article and on-page outline align without crowding; the hero banner spans the content width without cutting its text; card icons align on the card baseline; oversized Markdown images stay within the prose column | @@ -52,6 +52,13 @@ has no empty outline or reserved right column, and that Previous names the destination's section. Repeat these checks in print preview: the section label remains, while page navigation and both versions of the outline are omitted. +From the top of each page, press Tab once and use **Skip to content**. Focus +should move to the article below the header. On a phone, scroll until the +brand row collapses, then focus the brand with the keyboard to reveal it +without hiding search or settings. On the long-form page, the progress bar +should reach its end as the last article content reaches the viewport bottom, +before the page navigation and footer. + With a screen reader, confirm that the disclosure announces its collapsed and expanded state, the "Page outline" navigation is labelled, and the current section link is announced after opening it. diff --git a/examples/github-pages/.github/workflows/docsprout-pages.yml b/examples/github-pages/.github/workflows/docsprout-pages.yml index e74d070..205fc75 100644 --- a/examples/github-pages/.github/workflows/docsprout-pages.yml +++ b/examples/github-pages/.github/workflows/docsprout-pages.yml @@ -13,7 +13,7 @@ concurrency: jobs: documentation: if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch) - uses: ikelaiah/docsprout/.github/workflows/publish-docs.yml@v1.2.1 + uses: ikelaiah/docsprout/.github/workflows/publish-docs.yml@v1.2.2 with: versioned: false release: latest diff --git a/examples/historical/.github/workflows/documentation.yml b/examples/historical/.github/workflows/documentation.yml index 0f2edc9..40a6e16 100644 --- a/examples/historical/.github/workflows/documentation.yml +++ b/examples/historical/.github/workflows/documentation.yml @@ -12,7 +12,7 @@ permissions: jobs: documentation: - uses: ikelaiah/docsprout/.github/workflows/publish-docs.yml@v1.2.1 + uses: ikelaiah/docsprout/.github/workflows/publish-docs.yml@v1.2.2 permissions: contents: read pages: write diff --git a/examples/single-version/.github/workflows/documentation.yml b/examples/single-version/.github/workflows/documentation.yml index 54de21f..20604f1 100644 --- a/examples/single-version/.github/workflows/documentation.yml +++ b/examples/single-version/.github/workflows/documentation.yml @@ -12,7 +12,7 @@ permissions: jobs: documentation: - uses: ikelaiah/docsprout/.github/workflows/publish-docs.yml@v1.2.1 + uses: ikelaiah/docsprout/.github/workflows/publish-docs.yml@v1.2.2 with: versioned: false release: latest diff --git a/pyproject.toml b/pyproject.toml index 5e3d71e..8aa7a24 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "docsprout" -version = "1.2.1" +version = "1.2.2" description = "Versioned static documentation sites for code projects" keywords = ["documentation", "static-site", "markdown", "github-pages"] readme = "README.md" diff --git a/src/docsprout/__init__.py b/src/docsprout/__init__.py index 6c282bd..f567a1c 100644 --- a/src/docsprout/__init__.py +++ b/src/docsprout/__init__.py @@ -1,3 +1,3 @@ """DocSprout: versioned static documentation sites for code projects.""" -__version__ = "1.2.1" +__version__ = "1.2.2" diff --git a/src/docsprout/assets.py b/src/docsprout/assets.py index 93e3d50..8ad993f 100644 --- a/src/docsprout/assets.py +++ b/src/docsprout/assets.py @@ -51,6 +51,8 @@ @media(prefers-color-scheme:dark){html[data-visual-theme="glassmorphic"]:not([data-theme]){color-scheme:dark;--dk-bg:#070b16;--dk-surface:#111b2e;--dk-text:#e9eeff;--dk-muted:#98abd0;--dk-border:#2b3a58;--dk-code-bg:#050810;--dk-raised:#1a2540;--dk-focus-ring:#67e8f9;--dk-interactive:color-mix(in srgb,var(--dk-accent) 45%,#fff);--dk-shadow:0 .75rem 2rem color-mix(in srgb,#000 55%,transparent),0 1px 0 color-mix(in srgb,#fff 6%,transparent);--dk-mesh-1:15%;--dk-mesh-2:14%;--dk-mesh-3:11%;--dk-mesh-4:9%}} *{box-sizing:border-box} .visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0} +.skip-link{position:fixed;top:.5rem;left:.5rem;z-index:5;transform:translateY(-200%);padding:.5rem .75rem;border:2px solid var(--dk-focus-ring);border-radius:var(--dk-radius);background:var(--dk-bg);color:var(--dk-text);font-family:var(--dk-font-ui);font-weight:700} +.skip-link:focus{transform:none} body{margin:0;background:var(--dk-bg);color:var(--dk-text);font-family:var(--dk-font-body);font-size:17px;line-height:1.7;font-synthesis:none;font-kerning:normal;font-optical-sizing:auto;font-variant-ligatures:common-ligatures contextual;text-rendering:optimizeLegibility} ::selection{background:color-mix(in srgb,var(--dk-accent) 24%,transparent)} .reading-progress{position:fixed;inset:0 0 auto;z-index:3;height:3px;background:var(--dk-border);pointer-events:none}.reading-progress span{display:block;width:100%;height:100%;background:var(--dk-accent);transform:scaleX(0);transform-origin:left} @@ -79,7 +81,7 @@ .nav-group summary:hover{background:var(--dk-surface)} .nav-group-links{display:flex;flex-direction:column;margin:.15rem 0 .35rem .7rem;padding-left:.55rem;border-left:1px solid var(--dk-border)} .nav-group-links a{padding:.34rem .6rem;font-size:.84rem} -.prose{min-width:0;max-width:var(--dk-content-width);padding-bottom:var(--dk-space-2)} +.prose{min-width:0;max-width:var(--dk-content-width);padding-bottom:var(--dk-space-2);scroll-margin-top:6.5rem} .page-context{margin:0 0 .85rem;color:var(--dk-muted);font-family:var(--dk-font-ui);font-size:.72rem;font-weight:700;letter-spacing:.08em;line-height:1.4;text-transform:uppercase;overflow-wrap:anywhere} .prose h1,.prose h2,.prose h3{font-family:var(--dk-font-display);font-weight:720;letter-spacing:-.032em;text-wrap:balance;scroll-margin-top:6.5rem} .prose h1{font-size:clamp(2.3rem,4vw,3rem);line-height:1.12;max-width:none;margin:0 0 1.25rem} @@ -253,6 +255,7 @@ .mobile-nav a.active{color:var(--dk-interactive);font-weight:700} .toc{display:none} .prose h1{font-size:2.15rem} + .prose{scroll-margin-top:9rem} .prose[data-homepage="false"] :is(h1,h2,h3,h4,h5,h6){scroll-margin-top:9rem} .copy-code{opacity:1} .capability-strip[data-card-count="4"],.capability-strip[data-card-count="3"]{grid-template-columns:repeat(2,minmax(0,1fr))} @@ -260,10 +263,13 @@ @media(max-width:600px){ .topbar{gap:.5rem} .brand{flex:0 0 100%} + .site-header.is-compact .brand:not(:focus){position:absolute;width:1px;height:1px;min-height:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap} + .site-header.is-compact .topbar{padding-block:.4rem} .header-controls{width:100%;gap:.35rem} .header-control{flex:1;min-width:0} .header-control>span{font-size:.6rem} .topbar select{width:100%;min-width:0} + .prose{scroll-margin-top:12rem} .prose[data-homepage="false"] :is(h1,h2,h3,h4,h5,h6){scroll-margin-top:12rem} .prose .capability-strip,.prose[data-homepage="true"] .capability-strip{grid-template-columns:1fr} .page-navigation{grid-template-columns:1fr;margin-top:3rem} @@ -276,7 +282,7 @@ .prose pre{margin-inline:-.25rem;padding-inline:1rem} } @media print{ - .reading-progress,.site-header,.mobile-nav,.toc,.inline-toc,.page-navigation,.hero-actions{display:none} + .skip-link,.reading-progress,.site-header,.mobile-nav,.toc,.inline-toc,.page-navigation,.hero-actions{display:none} .hero::before,.hero::after{display:none} .hero{border-color:#999;background:none} body{background:#fff;color:#000;font-size:12pt;line-height:1.5} @@ -366,8 +372,16 @@ block.append(button); }); - const progress=document.querySelector('.reading-progress span'); - function updateProgress(){if(!progress)return;const range=document.documentElement.scrollHeight-window.innerHeight;const amount=range>0?Math.min(1,Math.max(0,window.scrollY/range)):0;progress.style.transform=`scaleX(${amount})`} + const progress=document.querySelector('.reading-progress span'),header=document.querySelector('.site-header'),article=document.querySelector('main.prose'); + function updateProgress(){ + if(!progress||!article)return; + header.classList.toggle('is-compact',window.scrollY>120); + const top=header.getBoundingClientRect().bottom,bounds=article.getBoundingClientRect(); + const bottom=article.querySelector('.page-navigation')?.getBoundingClientRect().top??bounds.bottom; + const range=bottom-bounds.top-(window.innerHeight-top); + const amount=range>0?Math.min(1,Math.max(0,(top-bounds.top)/range)):1; + progress.style.transform=`scaleX(${amount})`; + } if(progress){window.addEventListener('scroll',updateProgress,{passive:true});window.addEventListener('resize',updateProgress);updateProgress()} const tocLinks=[...document.querySelectorAll('.toc a[href^="#"],.inline-toc a[href^="#"]')]; diff --git a/src/docsprout/build.py b/src/docsprout/build.py index 9495c30..de0329b 100644 --- a/src/docsprout/build.py +++ b/src/docsprout/build.py @@ -269,6 +269,7 @@ def _shell(*, body: str, headings: tuple[tuple[int, str, str], ...], page: Page, "
" ) site_header = ( + 'Skip to content' '' '