Grammar Page Organization Dropdown
1. How the page works today
The grammar index (grammar.md) is pure Liquid. It collects every published explanation page — currently 98 files with layout: explanation under _grammar/ — and groups them by a section value. That value is not written in front matter: the folder_categorizer.rb plugin derives section and subsection from the folder path (_grammar/Necessity/〜べき/… → section "Necessity"). Sections render alphabetically, and within each section the grammar-subsection.md include emits a flat link list sorted by japanese, then title.
Two facts shape this spec:
- The site is static. There is no server to re-sort a list on request, so every ordering the dropdown offers must either be pre-rendered into the page or assembled client-side from embedded data.
- There is an established pattern for client-side preferences.
js/layout-switcher.jsalready implements a<select>+localStoragepreference (for themes), andfurigana-toggle.jsdoes the same for furigana. The organization dropdown should copy this pattern, not invent a new one.
2. Recommended architecture: pre-render all views, toggle with JS
Render all four organizations into /grammar at build time, each inside its own container. Exactly one container is visible; a dropdown at the top of the page switches between them. This is the simplest option that keeps the page working without JavaScript and requires no client-side templating.
Page structure
<div class="grammar-arrange">
<label for="grammar-arrange-select">Arrange by</label>
<select id="grammar-arrange-select">
<option value="category">Category</option>
<option value="recommended">Recommended reading order</option>
<option value="jlpt">JLPT level</option>
<option value="genki">Genki chapter</option>
</select>
</div>
<div data-arrangement="category"> …current output, unchanged… </div>
<div data-arrangement="recommended" hidden> … </div>
<div data-arrangement="jlpt" hidden> … </div>
<div data-arrangement="genki" hidden> … </div>New script: js/grammar-arrangement.js
- Mirrors
layout-switcher.js: read preference fromlocalStorage(key such asgrammar-arrangement), apply it on load, save on change. - Also honor a URL query parameter (e.g.
/grammar?arrange=jlpt) so a specific view can be linked from other pages or shared; the parameter wins over the stored preference, andhistory.replaceStatekeeps the URL in sync when the user changes the dropdown. - Loaded only on the grammar page (conditional include in
head/assets.html, or referenced from the page's layout), not sitewide.
Why not the alternatives
| Alternative | Why not |
|---|---|
Four separate pages (/grammar/by-jlpt etc.) | Splits inbound links and search results across four URLs, quadruples near-duplicate content for crawlers, and a "preference" can't persist across them without redirects. |
| One list re-sorted by JavaScript | The four views aren't re-sorts of one flat list — each has different groupings (sections vs. levels vs. chapters vs. stages) with different headings and annotations. Client-side regrouping means shipping a JSON blob plus a templating layer; the Liquid build already does this for free. |
| Rendering only the selected view at build time | Not possible — static builds can't respond to a per-visitor choice. |
Cost check: 98 links × 4 views is roughly 400 list items, well under anything that affects page weight or the ~2.2s serve loop. Duplicated links inside hidden containers are ignored by Pagefind's default indexing of visible text and are harmless for SEO as long as the default (category) view is the one visible without JS.
3. Data model: where each ordering's data lives
The four orderings need three kinds of data, and they should not all live in the same place. The dividing line: facts about a grammar pattern belong in that page's front matter; facts about a curriculum belong in a central data file.
| View | Data needed | Where it lives | New work required |
|---|---|---|---|
| Category | section / subsection | Already derived from folder paths | None |
| JLPT | One level per page | Front matter: jlpt: N4 | Tag all 98 pages |
| Textbook | Chapter number per page, per book | Front matter: genki: 12 (one flat key per textbook) | Map pages to chapters; many pages will map to nothing |
| Recommended | An ordered curriculum | Central file: _data/grammar_order.yaml | Author the curriculum |
3.1 JLPT: jlpt: N4 in front matter
---
layout: explanation
title: Should be with 〜べきだ
japanese: 〜べきだ
jlpt: N3
---- One level per page, as a string
N5–N1. Sorting in Liquid is then justgroup_by: "jlpt"plus a fixed iteration order (N5, N4, N3, N2, N1, none) — don't rely on alphabetical sort, which would put N1 first. - Pages with no sensible level get no key. A big strength of this site is colloquial, manga-specific grammar (contractions, sentence-ending particles, rough speech) that the JLPT never formally lists. Render these in a final group titled something like "Beyond the JLPT" rather than "Unassigned" — for a manga-reading audience this is a feature, not missing data.
- Levels are approximations and should say so. The JLPT has published no official grammar list since the 2010 revision; every site (Bunpro, JLPT Sensei, etc.) assigns levels by convention. A one-line note above the JLPT view should state that levels are approximate. When a pattern spans levels (e.g. a basic use at N4 and an extended use at N2), tag the level at which a reader first needs it.
- Grouping within a level: keep it a flat list sorted by
japanese, same as current subsections. 98 pages across 6 groups is ~16 links per group; sub-headings inside levels would add noise.
3.2 Textbooks: one flat front-matter key per book
---
japanese: 〜てもいい
jlpt: N5
genki: 6 # Genki I, Lesson 6
genki-note: Introduced as 〜てもいいですか
---- Use a flat integer key per textbook (
genki: 6) rather than a nested map (textbooks: {genki: 6}). Flat keys make the Liquid trivial:where_exp: "p", "p.genki"→group_by: "genki"→ numeric sort. Nested structures fight Liquid's sort filters for no benefit. - Number chapters continuously across volumes. Genki I is Lessons 1–12, Genki II is 13–23, so a single integer works. Render headings as "Lesson 6 (Genki I)" from the number. If a future textbook restarts numbering per volume (e.g. Minna no Nihongo), that book gets its own convention decided then.
- Each textbook is its own dropdown option ("Genki chapter", later perhaps "Tobira chapter"), not one merged "textbook" view. Start with Genki only; the pattern extends by adding a key and a container.
- Expect sparse coverage, and present it honestly. Genki covers perhaps half of these 98 pages. After the last chapter group, render a section titled "Not covered in Genki" listing the rest (sorted by
japanese). This is genuinely useful: a Genki learner sees exactly what the textbook hasn't given them that manga will demand. An optionalgenki-notekey handles cases where the textbook's framing differs from the site's (as in the example above). - Chapter mappings are facts, not content — listing "〜てもいい appears in Genki Lesson 6" raises no licensing concern. Don't reproduce Genki's explanations or example sentences.
3.3 Recommended order: a central curriculum file, not per-page numbers
A per-page order: 37 key would be a maintenance trap: inserting one new page mid-sequence means renumbering dozens of files, and the curriculum as a whole would be invisible — readable only by grepping every file. Instead, define the order in one file that reads top-to-bottom as the curriculum itself:
# _data/grammar_order.yaml
- stage: The sentence skeleton
blurb: >-
Enough structure to tell who is doing what to whom in a
speech bubble.
pages:
- /grammar/marking-the-subject-with-が
- /grammar/marking-the-topic-with-は-and-も
- /grammar/marking-the-direct-object-with-を
# …
- stage: Verb endings you meet on page one
blurb: >-
Past, negative, and て-form — the inflections that appear in
nearly every panel.
pages:
- /grammar/…- Reference pages by permalink — the one identifier that is already stable and unique (titles get edited; file paths move between category folders).
- Group into named stages rather than a strict 1–98 ranking. Stages survive insertions, give the learner a sense of progress, and admit an honest truth: within a stage, order barely matters. Each stage's
blurbrenders under its heading and can say why this comes now — something no other view offers. - Not every page needs a slot. Pages absent from the file render in a final "Reference — read when you meet them" stage. Some patterns (say, 〜んばかり) aren't worth sequencing; they're lookup material.
- The order should reflect the site's actual goal. This is the view where "reading manga, no production, no listening" changes the answer. A textbook or JLPT order front-loads polite production forms (ます/です conjugation drills) and defers colloquial speech for years. A manga-reading order is nearly the reverse:
- Plain forms before (or at least alongside) polite forms — manga dialogue is mostly plain.
- Colloquial contractions (〜ちゃう, 〜てる, じゃ, 〜なきゃ) belong in an early-middle stage, not the end — they appear on page one of most shounen manga.
- Recognition-only treatment throughout: the learner never needs to conjugate, only to de-conjugate. Sequencing can therefore be more aggressive than a production course.
- Conjecture/hearsay (〜そう, 〜らしい, 〜みたい) and explanatory の/んだ come early — they're everywhere in dialogue.
- Formal written N2/N1 patterns go late or in the reference stage regardless of their JLPT level.
- The existing
_docs/grammar-page-recommendations.mdis about which pages to write next; this file is about which existing pages to read first. They're related but should stay separate documents.
3.4 Rendering the curriculum in Liquid
For each permalink in the data file, find the matching document: site.grammar | where: "permalink", slug | first. That's an O(stages × pages × 98) scan — a few thousand comparisons, negligible against the current build. If it ever shows up in profiling, extend build_indexes.rb to expose a permalink→doc hash, but don't build that up front.
Validation: a typo'd permalink in the data file would silently drop a page. Add a small check — either in the existing plugin chain or as a _scripts/ lint run by hand — that warns when (a) a permalink in grammar_order.yaml matches no published page, or (b) a published page appears in no stage and is also absent from the intended-reference list. The same script can report JLPT/Genki tagging coverage while it's there.
4. Template changes
grammar.md— becomes four blocks. The category block is the current loop, unchanged. New blocks: JLPT (group_bywith fixed level order + "Beyond the JLPT" tail), Genki (numericgroup_by+ "Not covered" tail), recommended (loop oversite.data.grammar_order+ reference tail). The page is getting long enough that each view belongs in its own include (grammar-by-jlpt.md, etc.) withgrammar.mdreduced to the dropdown markup and four include calls.grammar-subsection.md— reusable as-is for the flat link lists inside every grouping; no changes needed.js/grammar-arrangement.js— new, per §2.- CSS — a small
_sassaddition styling the dropdown row consistently with the theme selector. - Optional, worth doing once data exists: the explanation layout can render the same metadata on each grammar page itself — a small line like JLPT N3 Genki L14 near the title. The data is per-page front matter precisely so this becomes free.
Accessibility and no-JS behavior
- Category view is visible by default; the other three containers carry the
hiddenattribute in the built HTML. Without JavaScript the page is exactly today's page, plus an inert dropdown (or hide the dropdown itself until JS reveals it). - The
<select>gets a real<label>. Switching views should move no focus and announce nothing — it's a filter, not a navigation. - In-page anchors (e.g.
#necessity) only resolve in the category view. Acceptable for now; if section deep-links matter later, the script can detect a hash targeting a hidden container and switch views to match.
5. Open questions to settle before building
| Question | Recommendation |
|---|---|
| Which JLPT reference to follow when sources disagree? | Pick one (Bunpro's list is the most complete) and note it once on the page; consistency matters more than the choice. |
| Genki 2nd or 3rd edition chapter numbers? | 3rd edition; lesson boundaries barely moved, and it's what current learners own. |
| Default view for first-time visitors? | Category for now (matches inbound links and expectations). Revisit once the recommended order exists — it arguably serves a new visitor better, and the default is a one-line change. |
| Show JLPT pills in every view's lists? | No — keep lists clean; the pill belongs on the explanation page itself. |
| Should dropdown options appear before their data is complete? | No. Ship each option only when its view renders something respectable (see phasing below). A "JLPT" view where half the pages sit in a fallback bucket undermines trust. |
6. Suggested phasing
- Infrastructure + recommended order. Dropdown, script, includes, and
_data/grammar_order.yaml. This phase touches no grammar pages at all (the curriculum file is central), yet delivers the option with the most unique value — no other site can offer a manga-reading order over this exact content. Ship with two options: Category, Recommended. - JLPT tagging. Add
jlpt:to the 98 pages (mechanical; a script can propose levels from a reference list for human review). Enable the JLPT option and, optionally, the pill on explanation pages. - Genki mapping. Add
genki:where applicable, enable the Genki option with its "Not covered in Genki" tail. Later textbooks repeat this phase. - Ongoing: the coverage lint from §3.4 keeps new pages from silently missing tags or curriculum slots; a line in the page-creation workflow (or the create-page skill) should prompt for
jlpt:on every new grammar page.
jlpt:, genki:); one central YAML file for the curriculum. The recommended-order view is both the cheapest to ship (zero page edits) and the truest to the site's manga-reading mission — build it first.