Examples search: maintainer’s guide
Architecture overview
The control flow is as follows:
- Page Load
- Fetch Collections
- Filter & Validate Data
- Build Fuse.js Index
- Render Filters
- Apply URL State
- Listen for Events
Key files
| File | Purpose |
|---|---|
../js/examples.js |
Core logic |
index.md |
HTML structure |
../../themes/flourish/source/_styles/components/examples.scss |
Styling |
Data flow
- All collections are fetched from
https://public.flourish.studio/visualisation/{id}/visualisation-object.json - Cards filtered: only
Include === "y"are kept - Industry/Purpose values validated against
VALID_*allowlists (invalid values silently stripped) Featured === "y"prepends “Featured” to Industry field- Sorted by Priority first, then stable pseudo-random order
Configuration points
All configuration is at the top of examples.js:
| Constant | What it does |
|---|---|
COLLECTION_IDS |
Which collections to fetch |
TEXT_SEARCH_FIELDS |
Searchable fields + weights |
VISIBLE_FILTER_FIELDS |
Which filters show as checkboxes (currently Industry, Purpose) |
VALID_INDUSTRIES / VALID_PURPOSES |
Allowlists for validation |
FUSE_OPTIONS |
Search parameters |
MOBILE_BREAKPOINT |
820px |
Important behaviours
- Featured is auto-unchecked when user types a search query
- Featured and Purpose are mutually exclusive - selecting one unchecks the other
- Radio buttons toggle - clicking same one twice unchecks it
- URL uses
replaceState- no browser history entries for searches - Card thumbnails are lazy loaded - card images and gif’s are lazy loaded with an extensive margin
Potential pitfalls
- Missing
Include: "y"- Card silently excluded - Misspelled Industry/Purpose - Value silently stripped (but you can check console for warnings)
- Missing
Linkfield - Preview/Make own buttons won’t work - Same card in multiple collections - Shows as duplicates (no deduplication)
- Modal has no timeout - If iframe fails to load, stays invisible
- No pagination - Rendering 1000+ results could slow UI
Debug mode
Add ?debug to URL to see:
- Data load counts
- Filter options extracted
- Fuse.js search scores and matched fields
- Lazy image loading
Common tasks
Add new collection
Add ID to COLLECTION_IDS array in examples.js
Add new filter
- Add field to
VISIBLE_FILTER_FIELDS - Create corresponding
VALID_*array for validation
Adjust search fuzziness
Change FUSE_OPTIONS.threshold (lower = stricter, 0 = exact match, 1 = match anything)
Fuse search explained
Fuse.js scores each potential match between 0.0 (perfect) and 1.0 (complete mismatch). Any result scoring above threshold is excluded.
Score formula
score = (errors / pattern.length) * fieldNorm |
- errors — character mismatches (typos) between query and candidate
- pattern.length — length of the search term
- fieldNorm — a multiplier derived from the total length of the field being searched
Without the flags below, two additional factors affect the score: the position of the match inside the field, and the ratio of the query length to the field length. Both are undesirable for our use case, so we disable them.
Our configuration and why
| Option | Value | Effect |
|---|---|---|
threshold |
0.15 |
Fairly strict — allows minor typos but not wild mismatches |
ignoreLocation |
true |
Match anywhere in the field; without this, "bump" at position 5 in "Lake Bumbunga" would be penalised by 5 / distance and likely excluded |
ignoreFieldNorm |
true |
Don’t penalise short queries in long fields; without this, "race" scores ~0.17–0.2 against "Line chart race" (15 chars) and gets excluded at threshold 0.15, while matching "Race" (4 chars) scores ~0.001 |
Multi-word queries
Fuse.js treats queries as a single character sequence, not as separate words. "area chart" matches the literal string "area chart" but "chart area" does not match "Area chart" — order matters. Searching for "area chart" also won’t match a field containing only "Area".
Comma-separated field values
With ignoreLocation: true, commas are just characters. A field like "Area, Bump" will match searches for "area" and "bump" equally well.
Troubleshoot missing example
Check:
Includefield is set to “y”- Industry/Purpose values match
VALID_*arrays exactly Linkfield exists with formatvisualisation/IDorstory/ID