On section and subcategory pages, every row repeated its use case and current page as tags (and as the label under the name on phones), right under a heading that already said the same thing. They are now hidden in editorial order and shown again when a column sort flattens the groups.
Co-Authored-By: Claude <noreply@anthropic.com>
After sorting by a column, or while a search hid a group, category page group headings stayed hidden, so the header's jump links (and any use-case heading link) pointed at a hidden element and did nothing.
Co-Authored-By: Claude <noreply@anthropic.com>
Clicking a category or group tag filtered the homepage table in place while rewriting the address bar to the category URL, so one URL rendered two different pages and readers never reached category pages with their intros and guides; category pages also showed a redundant self-referential filter bar.
Co-Authored-By: Claude <noreply@anthropic.com>
Category pages sorted rows by downloads, which buried editorial leads (the Django ORM showed as row 7 of 7, tkinter as 14 of 14) against CONTRIBUTING.md's "position is the marker"; the full intro in the hero pushed the table 2.5 screens down on a phone; and every page repeated the same "Search every project in one place" H2.
Co-Authored-By: Claude <noreply@anthropic.com>
Entries with a download count now sort first (descending), with
stars, then Built-in, then name as fallback tiers for entries that
lack a count. main.js mirrors this in its default activeSort, clean
URL check, and third-click reset target. Sorting by stars remains one
header click away.
Co-Authored-By: Claude <noreply@anthropic.com>
Sourced from website/data/pypi_downloads.tsv the same way
github_stars.json feeds the stars column. The new sortable column
sits between GitHub Stars and Last Commit on the homepage and
category pages, formatted with thousands separators like stars, with
an em dash when no PyPI data exists. Rows are matched by normalized
README display name; Built-in entries never show counts since
same-named PyPI packages are stdlib backports (e.g. the asyncio
package).
Below 960px the column hides and the count moves into the expand
row, mirroring the existing Last Commit treatment. main.js gains the
downloads sort branch and URL param.
The deploy workflow fetches the TSV via the new
make fetch_pypi_downloads target with a daily actions/cache
fallback, mirroring the stars fetch, but non-fatal: the column
degrades to dashes when the fetch fails, unlike stars which the
build requires.
Co-Authored-By: Claude <noreply@anthropic.com>
On category pages desc-rows are always visible. On the index page they
were always hidden. Now they become visible whenever a tag/category
filter is applied, giving filtered results the same richness as category
pages.
Also tightens two related CSS rules: border-bottom suppression only
fires when the adjacent desc-row is actually visible, and the expand-row
description is hidden while the desc-row is already showing to avoid
duplicate text.
Co-Authored-By: Claude <noreply@anthropic.com>
- Extract render_category() helper to deduplicate the three category/group/builtin
rendering blocks in build.py
- Replace synthetic dict literals with synthetic_category() helper
- Rewrite subcategory rendering to avoid O(n²) loop using precomputed dicts
- Pass filter_urls (not just JSON) to templates so Jinja can look up group URLs
directly instead of applying the slugify filter at render time
- Remove slugify from env.filters (no longer used in templates)
- Replace isIndexPage() wrapper with isIndexDocument constant in main.js
- Fix: call applyFilters() on page load when activeFilter is set
- Remove dead else branch in tag click handler (category pages with no URL)
- Switch .hero-category-links from CSS columns to CSS grid for more even layout
- Remove max-width cap on .category-subtitle
Co-Authored-By: Claude <noreply@anthropic.com>
Add search input, filter chips, no-results block, and back-to-top
button to category/group/subcategory pages. Pass filter_urls_json to
all page types so tag-chip navigation works site-wide. Fix JS so
filter-clear and no-results-clear redirect to / on non-index pages
instead of trying to filter a non-existent local table. Remove the
now-redundant .category-results CSS overrides.
Co-Authored-By: Claude <noreply@anthropic.com>
The "All projects" link in the category-page topbar pointed to
/#library-index so the browser would scroll to the library section on
arrival. The hash stayed in the URL, which looked like an internal anchor
state rather than a clean homepage URL.
On homepage load, if the hash is #library-index, scroll to the section
explicitly and use history.replaceState to drop the hash from the URL.
The scrollIntoView call covers the case where the script runs before the
browser's native anchor scroll, since replaceState removes the hash the
browser would have used.
Tag clicks on / pushState a category/group/subcategory path; on static
pages they fully navigate. Search and sort stay in querystring. Built-in
source tag has no data-url and stays as an in-page filter. The
isIndexDocument flag is captured at load time so toggling on the index
keeps working after pushState changes location.pathname.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Hoist reducedMotion and sortHeaders to module scope to avoid repeated
DOM queries on every call
- collapseAll now queries within tbody instead of the full document
- Replace indexOf with includes for tag filtering
- Remove null check on activeSort (always initialized)
- Drop inline section comments that just restate the code
Co-Authored-By: Claude <noreply@anthropic.com>
Only the canonical 'filter' query param is supported. The 'category'
and 'group' aliases were never documented and silently accepted wrong
spellings; removing them prevents hidden coupling to old URL shapes.
Co-Authored-By: Claude <noreply@anthropic.com>
Replace the { type, value } filter object with a plain string value.
Merge data-cats and data-groups row attributes into a single data-tags
attribute. Drop data-type from tag buttons. Consolidate category/group
URL params into a single filter param, keeping backward-compat fallback.
Co-Authored-By: Claude <noreply@anthropic.com>
The same matchMedia query was inlined twice (hero scroll and back-to-top).
Extracted into a shared helper. Also renamed loop variable and reformatted
a chained querySelector call for readability. No behavior change.
Co-Authored-By: Claude <noreply@anthropic.com>
Add a data-scroll-to attribute to the hero 'Browse the List' anchor
and a JS handler that calls scrollIntoView instead of letting the
browser follow the href, so the URL hash is never written.
Respects prefers-reduced-motion.
Co-Authored-By: Claude <noreply@anthropic.com>
Add an expand-commit span inside the expand row that displays the last
commit date. Hidden on desktop, visible only on mobile (max-width: 960px)
via media query, mirroring the commit column that appears in the full
table. Relative time formatting is applied via JS on page load.
Co-Authored-By: Claude <noreply@anthropic.com>
Swap the hardcoded 'smooth' scroll behavior for a runtime check so users
who have enabled reduced-motion in their OS get instant (auto) scrolling
instead of the animated kind.
Co-Authored-By: Claude <noreply@anthropic.com>
Modernize variable declarations and string literals in main.js for
consistency and to signal immutability intent. Pure style change with
no behavioral differences.
Co-Authored-By: Claude <noreply@anthropic.com>
Uses IntersectionObserver to toggle an .offscreen class on the hero
element, which sets animation-play-state: paused on the sheen, noise,
and scroll-cue animations. Avoids unnecessary GPU work while the hero
is not visible.
Co-Authored-By: Claude <noreply@anthropic.com>
- Add aria-sort attributes to table header on sort state changes
- Replace .table-wrap:focus outline:none with focus-visible outline
- Move noscript message above the fold into main, before content
- Upgrade hero-topbar div to nav with aria-label for landmark semantics
- Remove role=button from tr elements (invalid ARIA on native elements)
- Fix back-to-top button label and text (was labelled 'Back to search')
- Switch font-size from 16px to 100% to respect user browser preferences
- Add overflow-wrap and word-break to .col-name and description cells
Co-Authored-By: Claude <noreply@anthropic.com>
When results are visible, clicking the button previously scrolled to
the results section. It now targets the search input instead, scrolls
it into view centered, and focuses it so the user can immediately type
a new query. Button label updated from 'Top' to 'Search' to reflect
the new destination.
Co-Authored-By: Claude <noreply@anthropic.com>
When a search or filter yields no results, the message now includes
an inline button that resets both the search input and the active
filter. Improves discoverability and reduces dead-end frustration.
Co-Authored-By: Claude <noreply@anthropic.com>
Replaces the fixed-position floating button with an inline button
inside the last column of the sticky table header. Removes the hidden
attribute toggle and float positioning in favour of opacity/pointer-
events toggling, keeping the visibility logic intact.
Co-Authored-By: Claude <noreply@anthropic.com>
The smooth scroll behavior felt sluggish on a 505-row page. Instant
jump is more responsive for a utility button.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace hidden attribute on filter-bar with opacity/transform transition
driven by a .visible class, allowing smooth fade-in/slide-down when a
filter is active. Add staggered hero-in keyframe animations to the page
title, subtitle, action buttons, and search box. Polish sortable table
headers with color transition on hover and active states.
Co-Authored-By: Claude <noreply@anthropic.com>
Adds a fixed-position button that fades in after scrolling 600px and
smoothly scrolls back to the top on click. Hidden by default via the
HTML hidden attribute so noscript users never see it.
Co-Authored-By: Claude <noreply@anthropic.com>
Calling getSortValue inside the comparator ran DOM queries on every
comparison (O(n log n) reads). Hoisting into a forEach beforehand reduces
that to O(n). Batching row appends through a DocumentFragment also avoids
repeated reflows during table reorder.
Co-Authored-By: Claude <noreply@anthropic.com>
Entries appearing in more than one category were previously emitted as
separate rows. They are now deduplicated in build.py by URL, collecting
all category and group names into lists.
The template encodes those lists as pipe-delimited data attributes
(data-cats, data-groups) and renders a tag button per category.
The JS filter is updated to split on '||' and check for membership,
so clicking any category tag correctly shows the merged row.
Co-Authored-By: Claude <noreply@anthropic.com>
Switch readme_parser.py from regex-based parsing to markdown-it-py for
more robust and maintainable Markdown AST traversal. Update build pipeline,
templates, styles, and JS to support the new parser output. Refresh GitHub
stars data and update tests to match new parser behavior.
Co-Authored-By: Claude <noreply@anthropic.com>
Replaces MkDocs with a bespoke Python site generator using Jinja2 templates
and Markdown. Adds uv for dependency management, GitHub Actions workflow for
deployment, and Makefile targets for local development (fetch_stars, build,
preview, deploy).
Co-Authored-By: Claude <noreply@anthropic.com>