diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c7bcc8a..d8935ef3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- **Docs: navigation and SEO polish.** A stable/latest version switcher in the navbar + (`docs/_static/switcher.json`); a custom `robots.txt` that keeps the ~60 thin + `_modules/` source-view pages out of crawlers (documentation pages allowed as + before); "Tutorial N:" prefixes dropped from 8 notebook H1s so page titles and + search-result snippets lead with the topic (page URLs unchanged - only the H1's + own fragment id changes, and an H1 anchor resolves to the page top that the bare + URL already opens; sidebar labels were already short-form); the 5 section landing + pages are now linked from `diff_diff/guides/llms.txt`. - **Docs: search-result excerpts fixed and search-as-you-type enabled.** Notebook section anchors containing CSS-special characters (digit-leading `#3.-Fit-Event-Study`, apostrophes, parentheses, colons) crashed Sphinx's search-excerpt renderer diff --git a/diff_diff/guides/llms.txt b/diff_diff/guides/llms.txt index 1cd5c2e8..cc9ae69f 100644 --- a/diff_diff/guides/llms.txt +++ b/diff_diff/guides/llms.txt @@ -31,6 +31,13 @@ Full practitioner guide: call `diff_diff.get_llm_guide("practitioner")` ## Documentation +The site is organized into 5 sections, each with a landing page: +[Getting Started](https://diff-diff.readthedocs.io/en/stable/getting_started.html) · +[Practitioner Guide](https://diff-diff.readthedocs.io/en/stable/practitioners.html) · +[Tutorials](https://diff-diff.readthedocs.io/en/stable/tutorials/index.html) · +[User Guide](https://diff-diff.readthedocs.io/en/stable/user_guide.html) · +[API Reference](https://diff-diff.readthedocs.io/en/stable/api/index.html) + ### Getting Started - **Practitioner Guide** (call `diff_diff.get_llm_guide("practitioner")`): 8-step workflow for rigorous DiD analysis (Baker et al. 2025) — **start here** diff --git a/docs/_static/switcher.json b/docs/_static/switcher.json new file mode 100644 index 00000000..ab9bb376 --- /dev/null +++ b/docs/_static/switcher.json @@ -0,0 +1,13 @@ +[ + { + "name": "stable", + "version": "stable", + "url": "https://diff-diff.readthedocs.io/en/stable/", + "preferred": true + }, + { + "name": "latest", + "version": "latest", + "url": "https://diff-diff.readthedocs.io/en/latest/" + } +] diff --git a/docs/conf.py b/docs/conf.py index 12148728..5633878f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -110,6 +110,9 @@ "../diff_diff/guides/llms-full.txt", "../diff_diff/guides/llms-practitioner.txt", "../diff_diff/guides/llms-autonomous.txt", + # Overrides RTD's allow-everything default at the domain root: keeps the + # ~60 thin _modules/ source-view pages out of crawlers. + "robots.txt", ] sitemap_url_scheme = "{link}" @@ -129,7 +132,21 @@ "icon": "fa-brands fa-python", }, ], - "navbar_end": ["theme-switcher", "navbar-icon-links"], + "navbar_end": ["version-switcher", "theme-switcher", "navbar-icon-links"], + # Version dropdown (stable/latest). json_url points at the latest build + # so new entries propagate to every published version without rebuilds. + # ONE-SELECTOR POLICY: this navbar switcher replaces the RTD flyout + # (Settings -> Addons -> Flyout menu is disabled in the RTD dashboard, + # decided 2026-07-20) - re-enabling the flyout would put two version + # controls with different version lists on every page. + # check_switcher=False: the build-time URL probe would fail -W on CI and + # on the first RTD build (the URL only exists after this change ships); + # the switcher itself is fetched client-side at page load. + "switcher": { + "json_url": "https://diff-diff.readthedocs.io/en/latest/_static/switcher.json", + "version_match": os.environ.get("READTHEDOCS_VERSION", "latest"), + }, + "check_switcher": False, "navigation_depth": 3, "show_toc_level": 2, # Live-filtering search overlay. Safe to enable now that search-result diff --git a/docs/robots.txt b/docs/robots.txt new file mode 100644 index 00000000..fb523b10 --- /dev/null +++ b/docs/robots.txt @@ -0,0 +1,10 @@ +# Served at the domain root via conf.py html_extra_path (overrides the +# Read the Docs autogenerated robots.txt, which allows everything). +# The ~60 autogenerated _modules/ source-view pages are thin duplicates of +# the API reference - keep them out of crawlers so search engines surface +# the documented pages instead. + +User-agent: * +Disallow: /en/*/_modules/ + +Sitemap: https://diff-diff.readthedocs.io/sitemap.xml diff --git a/docs/tutorials/08_triple_diff.ipynb b/docs/tutorials/08_triple_diff.ipynb index 64ff2a79..20f71808 100644 --- a/docs/tutorials/08_triple_diff.ipynb +++ b/docs/tutorials/08_triple_diff.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Tutorial 8: Triple Difference (DDD) Estimation\n", + "# Triple Difference (DDD) Estimation\n", "\n", "This tutorial covers the Triple Difference (DDD) estimator, which extends standard Difference-in-Differences to settings where treatment requires satisfying two criteria.\n", "\n", diff --git a/docs/tutorials/18_geo_experiments.ipynb b/docs/tutorials/18_geo_experiments.ipynb index 9fb5a9c1..4f5fd526 100644 --- a/docs/tutorials/18_geo_experiments.ipynb +++ b/docs/tutorials/18_geo_experiments.ipynb @@ -5,7 +5,7 @@ "id": "t18-cell-001", "metadata": {}, "source": [ - "# Tutorial 18: Geo-Experiment Analysis with SyntheticDiD\n", + "# Geo-Experiment Analysis with SyntheticDiD\n", "\n", "A practitioner walkthrough for marketing analytics teams measuring lift from a campaign that ran in a subset of geographic markets. The tutorial uses `SyntheticDiD` (Arkhangelsky et al. 2021) on a simulated DMA panel and walks through the fit, the diagnostics, and the stakeholder summary you need to ship the result." ] diff --git a/docs/tutorials/19_dcdh_marketing_pulse.ipynb b/docs/tutorials/19_dcdh_marketing_pulse.ipynb index 9f16d725..7a5f260f 100644 --- a/docs/tutorials/19_dcdh_marketing_pulse.ipynb +++ b/docs/tutorials/19_dcdh_marketing_pulse.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "id": "t19-cell-001", "metadata": {}, - "source": "# Tutorial 19: dCDH for Marketing Pulse Campaigns\n\nA practitioner walkthrough for measuring lift from promotional campaigns that turn on AND off across markets at staggered times. The tutorial uses the `ChaisemartinDHaultfoeuille` estimator (alias `DCDH`) - diff-diff's estimator purpose-built for reversible (non-absorbing) treatment: the most general non-absorbing option in the library, with explicit joiner/leaver decomposition. (`LPDiD` and `TROP` also support non-absorbing treatment via their `non_absorbing` parameters, under stronger assumptions.)" + "source": "# dCDH for Marketing Pulse Campaigns\n\nA practitioner walkthrough for measuring lift from promotional campaigns that turn on AND off across markets at staggered times. The tutorial uses the `ChaisemartinDHaultfoeuille` estimator (alias `DCDH`) - diff-diff's estimator purpose-built for reversible (non-absorbing) treatment: the most general non-absorbing option in the library, with explicit joiner/leaver decomposition. (`LPDiD` and `TROP` also support non-absorbing treatment via their `non_absorbing` parameters, under stronger assumptions.)" }, { "cell_type": "markdown", diff --git a/docs/tutorials/20_had_brand_campaign.ipynb b/docs/tutorials/20_had_brand_campaign.ipynb index 9c9706e6..953c7340 100644 --- a/docs/tutorials/20_had_brand_campaign.ipynb +++ b/docs/tutorials/20_had_brand_campaign.ipynb @@ -5,7 +5,7 @@ "id": "t20-cell-001", "metadata": {}, "source": [ - "# Tutorial 20: HAD for a National Brand Campaign with Regional Spend Intensity\n", + "# HAD for a National Brand Campaign with Regional Spend Intensity\n", "\n", "A practitioner walkthrough for measuring per-dollar lift when every market is treated at a different dose level - treatment varies in intensity, not in status, and no never-treated unit exists. Comparison comes from the dose variation across markets, not from an untreated holdout. The tutorial uses the `HeterogeneousAdoptionDiD` estimator (alias `HAD`), built for this case." ] diff --git a/docs/tutorials/21_had_pretest_workflow.ipynb b/docs/tutorials/21_had_pretest_workflow.ipynb index 24ca51fc..edfb3567 100644 --- a/docs/tutorials/21_had_pretest_workflow.ipynb +++ b/docs/tutorials/21_had_pretest_workflow.ipynb @@ -5,7 +5,7 @@ "id": "d4e3e374", "metadata": {}, "source": [ - "# Tutorial 21: HAD Pre-test Workflow - Running the Pre-test Diagnostics on the Brand Campaign Panel\n", + "# HAD Pre-test Workflow - Running the Pre-test Diagnostics on the Brand Campaign Panel\n", "\n", "[Tutorial 20](20_had_brand_campaign.ipynb) fit `HeterogeneousAdoptionDiD` (HAD) on a regional brand-campaign panel and reported a per-dollar lift, with a brief visual placebo check at the end. We deliberately deferred the **formal pre-test workflow** to this tutorial, with a forward pointer in T20's \"Extensions\" section.\n", "\n", diff --git a/docs/tutorials/22_had_survey_design.ipynb b/docs/tutorials/22_had_survey_design.ipynb index 4203b270..aadfd1b0 100644 --- a/docs/tutorials/22_had_survey_design.ipynb +++ b/docs/tutorials/22_had_survey_design.ipynb @@ -5,7 +5,7 @@ "id": "bd4f9026", "metadata": {}, "source": [ - "# Tutorial 22: Survey-Weighted HAD - The BRFSS-Shape Rollout\n", + "# Survey-Weighted HAD - The BRFSS-Shape Rollout\n", "\n", "The HAD series so far ran on simple iid panels. T20\n", "(`docs/tutorials/20_had_brand_campaign.ipynb`) walked the headline workflow;\n", diff --git a/docs/tutorials/24_staggered_vs_collapsed_power.ipynb b/docs/tutorials/24_staggered_vs_collapsed_power.ipynb index f79fc93f..de10b0c3 100644 --- a/docs/tutorials/24_staggered_vs_collapsed_power.ipynb +++ b/docs/tutorials/24_staggered_vs_collapsed_power.ipynb @@ -5,7 +5,7 @@ "id": "269c802e", "metadata": {}, "source": [ - "# Tutorial 24: Staggered Rollout or a Simple 2×2? A Power-Analysis Decision Guide\n", + "# Staggered Rollout or a Simple 2×2? A Power-Analysis Decision Guide\n", "\n", "You're running a geo experiment — a feature rolled out across the **50 US states**. The\n", "rollout is *staggered*: a clean pre-period, then states switch on in waves over several\n", diff --git a/docs/tutorials/27_cic_distributional_effects.ipynb b/docs/tutorials/27_cic_distributional_effects.ipynb index 3ab75f5f..a530e8d4 100644 --- a/docs/tutorials/27_cic_distributional_effects.ipynb +++ b/docs/tutorials/27_cic_distributional_effects.ipynb @@ -5,7 +5,7 @@ "id": "f9b19c35", "metadata": {}, "source": [ - "# Tutorial 27: When the Average Hides the Action - Distributional DiD with Changes-in-Changes\n", + "# When the Average Hides the Action - Distributional DiD with Changes-in-Changes\n", "\n", "A loyalty program was rolled out to a lower-spend customer segment. Finance ran the\n", "standard difference-in-differences on monthly spend and got a flat zero - the program\n",