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
18 changes: 8 additions & 10 deletions .github/ISSUE_TEMPLATE/suggest-method.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,15 @@ body:
validations:
required: true
- type: dropdown
id: kingdom
id: taxon
attributes:
label: Kingdom*
multiple: true
options: [Animalia, Plantae, Fungi, Chromista, Bacteria, Archaea]
validations:
required: true
- type: dropdown
id: phylum
attributes:
label: Phylum / Division*
label: Taxon — phylum / division*
description: >-
Pick the phylum/division(s). Grouped by kingdom for reference (Animalia,
Plantae, Fungi). Choose "Other" if yours isn't listed and describe it below.
multiple: true
options:
# Animalia
- Chordata
- Arthropoda
- Mollusca
Expand All @@ -47,8 +43,10 @@ body:
- Nematoda
- Platyhelminthes
- Porifera
# Plantae
- Tracheophyta
- Bryophyta
# Fungi
- Ascomycota
- Basidiomycota
- "Other (describe in detail box)"
Expand Down
11 changes: 7 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ Add one Markdown file per entry, then open a PR. CI validates it automatically.
```yaml
---
title: "Genome skimming from a 40-year-old pinned beetle"
kingdom: [Animalia] # one or more, from _data/tags.yml
phylum: [Arthropoda] # "Phylum / Division", from _data/tags.yml
taxon: [Arthropoda] # phylum/division, from _data/tags.yml (kingdom is implied)
preservation: [dry-pinned]
extraction: [silica-column]
library: [tagmentation]
Expand All @@ -37,10 +36,14 @@ Full detail in Markdown. Renders when the card is expanded: protocol notes,
yields, caveats, links to SOPs in the bibliography, etc.
```

Required: `title`, `kingdom`, `phylum`, `preservation`, `extraction`,
`library`, `sequencing`, `summary`, `contributor`, `date`.
Required: `title`, `taxon`, `preservation`, `extraction`, `library`,
`sequencing`, `summary`, `contributor`, `date`.
Optional: `outcome`, `doi`, body.

`taxon` values are phyla/divisions, grouped under their kingdom in
`_data/tags.yml`; the kingdom is inferred from that grouping (we represent
animals, plants and fungi).

### Bibliography entry — `_bibliography/<short-slug>.md`

```yaml
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ types, backed by a curated bibliography of supporting papers and SOPs.

🌐 **Site:** https://museomics.github.io/methods/

- **Methods** (`/`) — filter working methods by kingdom, phylum/division,
preservation, extraction, library prep, and sequencing. Cards expand to full
protocol notes.
- **Methods** (`/`) — filter working methods by taxon (phylum/division grouped
under kingdom), preservation, extraction, library prep, and sequencing. Cards
expand to full protocol notes.
- **Bibliography** (`/bibliography/`) — papers and SOPs, filterable by type and
method tag.
- **Contribute** (`/contribute/`) — suggest an entry via issue form, or by PR.
Expand Down
25 changes: 14 additions & 11 deletions SPEC.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# museomics/methods — Build Spec & Implementation Plan (revised)

> Revision note: this supersedes the original draft. Changes from the review:
> two-level taxonomy (Kingdom + Phylum/Division) replacing a single taxon
> facet; GitHub Actions build instead of stock GitHub Pages; a single
> a single Taxon facet whose values are phyla/divisions grouped under kingdom
> (Animalia / Plantae / Fungi), presented as one dropdown with per-kingdom
> select-all — replacing the original flat taxon tag; GitHub Actions build
> instead of stock GitHub Pages; a single
> canonical controlled vocabulary enforced by CI; free-text search; sort
> control; empty state; accessible chips (not colour-alone); optional DOI;
> optional outcome signal; separate content licence (CC BY 4.0).
Expand Down Expand Up @@ -53,16 +55,18 @@ Nav bar on every page: **Methods | Bibliography | Contribute**

## 1. Methods page (`/`)

Let visitors find and filter working methods by **kingdom**, **phylum/division**,
preservation, extraction, library prep, and sequencing/downstream method.
Multi-tagging per entry is supported on every facet.
Let visitors find and filter working methods by **taxon** (phylum/division,
grouped under kingdom — Animalia / Plantae / Fungi), preservation, extraction,
library prep, and sequencing/downstream method. Multi-tagging per entry is
supported on every facet.

### Layout (top to bottom)
1. Header — title + one-line purpose.
2. Tag legend — collapsible explainer of the six facets and their chip
2. Tag legend — collapsible explainer of the five facets and their chip
colours/badges.
3. Controls — free-text search box + sort control (newest / oldest / A–Z), then
a filter bar of six collapsible multi-select facets, populated from
a filter bar of five collapsible multi-select facets (Taxon is one grouped
dropdown), populated from
`_data/tags.yml`. OR within a facet, AND across facets.
4. Results count (live) + "Clear all filters".
5. Card grid.
Expand All @@ -80,8 +84,7 @@ Multi-tagging per entry is supported on every facet.
```yaml
---
title: "Genome skimming from a 40-year-old pinned beetle"
kingdom: [Animalia]
phylum: [Arthropoda]
taxon: [Arthropoda] # phylum/division; kingdom implied by grouping
preservation: [dry-pinned]
extraction: [silica-column]
library: [tagmentation]
Expand All @@ -94,7 +97,7 @@ doi: "10.xxxx/xxxxx" # OPTIONAL — omit for unpublished methods
---
Full detail body as Markdown (renders on expand).
```
Required: title, kingdom, phylum, preservation, extraction, library,
Required: title, taxon, preservation, extraction, library,
sequencing, summary, contributor, date. DOI is optional so unpublished /
in-house methods are not excluded.

Expand All @@ -108,7 +111,7 @@ in-house methods are not excluded.
(`window.SITE`) so JS never guesses the baseurl and filter options come from
the config, not just from values present in the data.
- Active filters, search text and non-default sort are reflected in the URL
query string (`?kingdom=Animalia&preservation=dry-pinned&q=beetle`) and
query string (`?taxon=Arthropoda&preservation=dry-pinned&q=beetle`) and
restored on load.

---
Expand Down
13 changes: 4 additions & 9 deletions _data/facets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,13 @@
# `abbr` is shown as a small badge on each chip so facets are distinguishable
# without relying on colour alone (accessibility).
# `methods` / `bibliography` flag which page a facet applies to.
- key: kingdom
label: Kingdom
abbr: K
- key: taxon
label: Taxon
abbr: Tx
color: green
methods: true
bibliography: false
- key: phylum
label: Phylum / Division
abbr: P
color: teal
methods: true
bibliography: false
grouped: true # values come from a kingdom -> [phyla] map in tags.yml
- key: preservation
label: Preservation
abbr: Pr
Expand Down
48 changes: 23 additions & 25 deletions _data/tags.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,29 @@
# except taxonomy, which uses conventional capitalised names.
# -----------------------------------------------------------------------------

# Taxonomy is split into two levels so visitors can filter broadly (Kingdom) or
# more specifically (Phylum / Division). Curated by the maintainer; extend as
# real contributions arrive rather than pre-populating exhaustively.
kingdom:
- Animalia
- Plantae
- Fungi
- Chromista
- Bacteria
- Archaea

phylum: # "Phylum / Division" — divisions are the botanical equivalent
- Chordata
- Arthropoda
- Mollusca
- Annelida
- Echinodermata
- Cnidaria
- Nematoda
- Platyhelminthes
- Porifera
- Tracheophyta # vascular plants
- Bryophyta # mosses
- Ascomycota
- Basidiomycota
# Taxonomy: a single facet whose values are phyla / divisions, grouped under
# their kingdom. Entries are tagged at phylum/division level (e.g. taxon:
# [Arthropoda]); the kingdom is implied by the grouping below. In the filter UI
# these render as one dropdown with kingdom sub-headings, and each kingdom
# heading is a "select all" toggle. We only represent animals, plants and fungi.
# Curated by the maintainer; extend as real contributions arrive.
taxon:
Animalia:
- Chordata
- Arthropoda
- Mollusca
- Annelida
- Echinodermata
- Cnidaria
- Nematoda
- Platyhelminthes
- Porifera
Plantae:
- Tracheophyta # vascular plants
- Bryophyta # mosses
Fungi:
- Ascomycota
- Basidiomycota

preservation:
- dry
Expand Down
3 changes: 1 addition & 2 deletions _methods/genome-skimming-pinned-beetle.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
title: "Genome skimming from a 40-year-old pinned beetle"
kingdom: [Animalia]
phylum: [Arthropoda]
taxon: [Arthropoda]
preservation: [dry]
extraction: [silica-column]
library: [diy-approach]
Expand Down
3 changes: 1 addition & 2 deletions _methods/herbarium-plant-target-capture.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
title: "Target capture from herbarium sheets using CTAB extraction"
kingdom: [Plantae]
phylum: [Tracheophyta]
taxon: [Tracheophyta]
preservation: [dry]
extraction: [ctab]
library: [single-stranded]
Expand Down
3 changes: 1 addition & 2 deletions _methods/study-skin-bird-mitogenome.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
title: "Mitogenome from a historical bird study skin toe pad"
kingdom: [Animalia]
phylum: [Chordata]
taxon: [Chordata]
preservation: [dry]
extraction: [magnetic-bead]
library: [single-stranded]
Expand Down
4 changes: 4 additions & 0 deletions assets/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ main.container { padding-top: 1.6rem; padding-bottom: 3rem; }
.facet-opts { padding: .5rem .7rem; max-height: 240px; overflow-y: auto; }
.opt { display: flex; align-items: center; gap: .45rem; padding: .15rem 0; cursor: pointer; }
.opt input { margin: 0; }
.facet-subgroup { margin-bottom: .4rem; }
.facet-subgroup + .facet-subgroup { border-top: 1px solid var(--border); padding-top: .4rem; }
.opt--group { font-size: .95rem; }
.opt--child { padding-left: 1.3rem; }

.controls-foot { display: flex; align-items: center; gap: 1rem; margin-top: .9rem; }
.results-count { margin: 0; font-weight: 600; }
Expand Down
43 changes: 43 additions & 0 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,56 @@
}

CONFIG.facets.forEach(function (f) {
if (f.grouped) {
bar.appendChild(groupedFacet(f));
return;
}
var vocab = SITE.vocab[f.key] || [];
bar.appendChild(facetGroup(f, vocab, filters[f.key], function (val, on) {
toggleIn(filters[f.key], val, on); update();
}));
});
}

// A grouped facet (e.g. Taxon): one dropdown whose options are nested under
// group headings (kingdoms). Each heading is a select-all toggle for its
// children (phyla/divisions). Only the children (leaf values) are stored in
// the filter state.
function groupedFacet(facet) {
var map = SITE.vocab[facet.key] || {};
var selectedRef = filters[facet.key];

var groups = Object.keys(map).map(function (groupName) {
var children = map[groupName];
var selectedKids = children.filter(function (c) { return selectedRef.indexOf(c) !== -1; });

var head = el("input", { type: "checkbox" });
head.checked = selectedKids.length === children.length && children.length > 0;
head.indeterminate = selectedKids.length > 0 && selectedKids.length < children.length;
head.addEventListener("change", function () {
children.forEach(function (c) { toggleIn(selectedRef, c, head.checked); });
update();
});
var heading = el("label", { class: "opt opt--group" }, [head, el("strong", { text: groupName })]);

var kids = children.map(function (c) {
var box = el("input", { type: "checkbox", value: c });
if (selectedRef.indexOf(c) !== -1) box.checked = true;
box.addEventListener("change", function () { toggleIn(selectedRef, c, box.checked); update(); });
return el("label", { class: "opt opt--child" }, [box, el("span", { text: c })]);
});

return el("div", { class: "facet-subgroup" }, [heading].concat(kids));
});

var details = el("details", { class: "facet facet--" + facet.color });
if (selectedRef.length) details.open = true;
var count = selectedRef.length ? " (" + selectedRef.length + ")" : "";
details.appendChild(el("summary", { text: facet.label + count }));
details.appendChild(el("div", { class: "facet-opts" }, groups));
return details;
}

function facetGroup(facet, values, selectedRef, onToggle, labelFn) {
var options = values.map(function (v) {
var checked = selectedRef.indexOf(v) !== -1;
Expand Down
3 changes: 1 addition & 2 deletions methods.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ sitemap: false
"contributor": {{ m.contributor | jsonify }},
"date": {{ m.date | date: "%Y-%m-%d" | jsonify }},
"outcome": {{ m.outcome | jsonify }},
"kingdom": {{ m.kingdom | jsonify }},
"phylum": {{ m.phylum | jsonify }},
"taxon": {{ m.taxon | jsonify }},
"preservation": {{ m.preservation | jsonify }},
"extraction": {{ m.extraction | jsonify }},
"library": {{ m.library | jsonify }},
Expand Down
18 changes: 13 additions & 5 deletions scripts/validate_entries.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,18 @@

errors = []

# Front-matter fields on method entries that must be drawn from a vocab list.
METHOD_FACETS = %w[kingdom phylum preservation extraction library sequencing].freeze
METHOD_REQUIRED = (METHOD_FACETS + %w[title summary contributor date]).freeze
# `taxon` is a kingdom -> [phyla] map; entries are tagged at phylum level, so
# the allowed values are the flattened list of phyla/divisions.
TAXON_VALUES = TAGS["taxon"].values.flatten.freeze

# Flat-list facets on method entries that must be drawn from a vocab list.
METHOD_FLAT_FACETS = %w[preservation extraction library sequencing].freeze
METHOD_REQUIRED = (%w[taxon] + METHOD_FLAT_FACETS + %w[title summary contributor date]).freeze

BIB_REQUIRED = %w[type title authors_or_institution year_or_date link].freeze
# Any tag on a bibliography entry must exist in one of these facet lists.
BIB_TAG_VOCAB = (TAGS["extraction"] + TAGS["library"] + TAGS["sequencing"] +
TAGS["preservation"] + TAGS["kingdom"] + TAGS["phylum"]).uniq.freeze
TAGS["preservation"] + TAXON_VALUES).uniq.freeze

def front_matter(path)
raw = File.read(path, encoding: "UTF-8")
Expand Down Expand Up @@ -53,7 +57,11 @@ def as_array(v)
errors << "#{rel}: missing required field `#{field}`" if val.nil? || (val.respond_to?(:empty?) && val.empty?)
end

METHOD_FACETS.each do |facet|
as_array(fm["taxon"]).each do |v|
errors << "#{rel}: `taxon` value \"#{v}\" not in _data/tags.yml" unless TAXON_VALUES.include?(v)
end

METHOD_FLAT_FACETS.each do |facet|
allowed = TAGS[facet] || []
as_array(fm[facet]).each do |v|
errors << "#{rel}: `#{facet}` value \"#{v}\" not in _data/tags.yml" unless allowed.include?(v)
Expand Down
Loading