Skip to content

feat(ux): UX review batch — 31 P0/P1/P2 fixes - #11

Merged
Bissbert merged 9 commits into
mainfrom
feature/ux-fix-batch
May 4, 2026
Merged

Bissbert merged 9 commits into
mainfrom
feature/ux-fix-batch

Conversation

@Bissbert

@Bissbert Bissbert commented May 4, 2026

Copy link
Copy Markdown
Contributor

Summary

End-to-end implementation of every finding from ~/Downloads/gemmology-dev-ux-review-2026-05-04/FIX-PLAN.md — 31 fixes across accessibility, performance, SEO, content, and polish.

P0 (7 fixes — accessibility & correctness)

  • Hero copy rewritten goal-led (study/identify) with proof number from getSiteStats() and a single primary CTA
  • WebGL fallback for HeroCrystal3D — static SVG octahedron when WebGL unavailable, respects prefers-reduced-motion, swaps sql.js call for direct GLTFLoader.load(/models/gltf/<slug>.glb) (eliminates ~600KB DB load on home)
  • Contrast palette pass — crystal-600 → crystal-700 site-wide (WCAG AA at 16px regular)
  • 46 missing crystal SVGs — 18 rendered via crystal-renderer Python toolchain, 28 origin-aware placeholders for amorphous/composite/simulant materials; new npm run check-svgs audit gates the build
  • Heading hierarchy — learn property/comparison h4 → h3
  • Mineral count single source of truth — src/lib/stats.ts reads live from minerals.db
  • /admin out of sitemap — sitemap filter + robots Disallow + page-level noindex

P1 (9 fixes — performance, SEO, IA)

  • Self-hosted fonts via @fontsource-variable/inter and @fontsource-variable/jetbrains-mono (eliminates Google Fonts hop + CLS)
  • sql.js + minerals.db lazy-load behind client:visible
  • Active nav state with aria-current=\"page\" + visual underline marker
  • Mobile mineral CTA — PlaygroundLink duplicated for <lg directly under QuickFacts
  • JSON-LD upgrades — combined LearningResource+Article, Wikidata Q43533 additionalType for Mineral, generic StructuredData component for breadcrumbs+WebPage
  • Sitemap — lastmod on every URL, tier-based priority (home/gallery/learn=1.0, minerals=0.8, docs=0.5)
  • About page + author/reviewedBy/reviewedAt fields on learn collection schema, byline + reviewed-date in article header
  • Legacy redirects — 12 /learn/<flat-slug> 301s + gallery/docs old URL redirects
  • Security headers — CSP with frame-ancestors 'none', HSTS preload, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, per-path Cache-Control

P2 (15 fixes — polish)

  • Learn hub Start here curated foundations + client-side filter
  • Category descriptions surface total count ("plus N more")
  • DifficultyBadge — distinct hue + leading dot per level
  • Mineral page CDL dedup, <main>→<article>, acronym title-casing (CVD/HPHT/FTIR/UV/IR/YAG/CZ…), breadcrumb above H1
  • Gallery Groups → <select> when >6 entries
  • Footer Packages column hidden outside /docs
  • Tools-hub redundant blurb removed
  • <title> brand-first ("gemmology.dev — X") for SERP recall
  • /llms.txt build-time endpoint with all learn + mineral URLs
  • IndexNow GitHub Action — diff-based submission to Bing/Yandex/Naver
  • Quiz setup 2-column layout at lg

Test plan

  • npm run build — 910 pages built clean in ~7s
  • dist/llms.txt generated, lists 138 learn entries + 303 mineral surfaces
  • dist/sitemap-0.xml includes <lastmod> and tiered <priority>
  • dist/index.html <title> is brand-first
  • dist/_headers and dist/_redirects deployed to Cloudflare Pages
  • dist/robots.txt Disallows /admin/
  • npm run check-svgs — 0 missing
  • Post-deploy: securityheaders.com grade ≥ A
  • Post-deploy: Lighthouse on /, /learn, /gallery, /playground confirms a11y ≥ 95, color-contrast 0 failures
  • Post-deploy: curl -I https://gemmology.dev/learn/crystal-systems → 301 to /learn/fundamentals/crystal-systems
  • Post-deploy: configure INDEXNOW_KEY repo secret + place key file in public/

See ~/Downloads/gemmology-dev-ux-review-2026-05-04/REPORT.md and FIX-PLAN.md for the full audit and acceptance criteria.

Bissbert added 8 commits May 4, 2026 18:13
The /admin/ route was leaking into the public sitemap-0.xml and was
indexable. This:

- Adds a sitemap filter so /admin/* URLs are excluded
- Adds Disallow: /admin/ to robots.txt
- Adds a noindex prop to BaseLayout and uses it on the admin page

Resolves P0-5 from the 2026-05-04 UX review.
Sections in /learn/* skipped h3 entirely — h2 (e.g. "Symmetry Elements")
went straight to h4 children rendered by PropertyList and ComparisonBlock.
That violates WCAG 1.3.1 and breaks screen-reader/LLM passage attribution.

Both components now accept a headingLevel prop (default 3). SectionRenderer
passes headingLevel=4 only when the components are nested inside a
subsection (which already uses h3), preserving correct structure at every
depth.

Resolves P0-6 from the 2026-05-04 UX review.
Three pages displayed three different (and all stale) mineral counts:
- / claimed "50+"
- /gallery claimed "90+"
- /404 claimed "94+"

The live database has 159 families. New src/lib/stats.ts queries
mineral_families and mineral_expressions at build time and exposes the
counts via getSiteStats(); the three pages now consume that helper.

Resolves P0-7 from the 2026-05-04 UX review.
Replace crystal-500/600 in text and button backgrounds with crystal-700
across all components. crystal-600 (#0284c7) failed AA on white at 4.5:1;
crystal-700 (#0369a1) passes ~5.7:1 for body text and UI controls.

- text-crystal-600 → text-crystal-700 (links, headings, hover states)
- bg-crystal-500/600 with text-white → bg-crystal-700 (buttons, badges, active states)
- from-crystal-500 to-crystal-600 → from-crystal-600 to-crystal-700 (gradients)
- hover:bg-crystal-600 → hover:bg-crystal-800
- prose-a defaults: add underline + decoration colour for link-in-text-block rule
- Decorative progress bars left at crystal-500 (3:1 UI contrast holds)
…chy', 'fix/mineral-count-source-of-truth' and 'fix/contrast-palette' into feature/ux-fix-batch
- Rewrite hero to outcome-led headline ("Identify gems with confidence"),
  shift primary CTA to /gallery and secondary to /learn, append open-source
  reassurance line.
- Add WebGL availability check + prefers-reduced-motion handling to
  HeroCrystal3D; fall back to a static SVG illustration with proper
  role=img and aria-label for browsers without WebGL.
- Generate SVGs for the 46 mineral families that previously 404'd:
  18 rendered via crystal-renderer pipeline, 28 origin-aware placeholders
  for amorphous, simulant, composite, and synthetic-only entries.
- Add scripts/audit-crystal-svgs.mjs and wire it into prebuild via the new
  npm run check-svgs target so future drift fails CI before deploy.
… sitemap, about page, redirects, security headers

P1-1: Self-host Inter & JetBrains Mono via @fontsource-variable to eliminate CLS and third-party hop
P1-2: Hero respects prefers-reduced-motion (handled in P0 commit, verified)
P1-3: Header active state with aria-current="page" + visual underline marker
P1-4: Mineral detail mobile CTA — duplicate PlaygroundLink for sub-lg viewports
P1-5: Schema.org JSON-LD upgrades — LearningResource+Article, Wikidata Mineral additionalType, generic StructuredData component
P1-6: Sitemap lastmod + tiered priority (home/gallery/learn=1.0, minerals=0.8, docs=0.5)
P1-7: About page with editorial standards + author/reviewedBy/reviewedAt schema for learn entries
P1-8: _redirects with 12 legacy /learn flat-slug 301s
P1-9: _headers with CSP, HSTS preload, frame-ancestors none, per-path Cache-Control
…llms.txt, indexnow, quiz

P2-1: Learn hub "Start here" curated foundations section above the 90-card wall
P2-2: Category descriptions surface total count + remainder ("plus N more")
P2-3: DifficultyBadge — distinct hue + leading dot per level for at-a-glance scannability
P2-4: Learn hub client-side filter (data-search hashes; collapses empty groups)
P2-5: Removed duplicate CDL block from PlaygroundLink (kept canonical copy in CrystalStructureCard)
P2-6: Mineral page nested <main> → <article> (BaseLayout already provides <main>)
P2-7: Acronym title-casing helper (CVD/HPHT/FTIR/UV/IR/YAG/CZ…) for origin + growth method
P2-8: Gallery Groups filter collapses to <select> when >6 entries
P2-9: Footer "Packages" column hidden outside /docs
P2-10: <title> brand-first ("gemmology.dev — X") for SERP recall
P2-11: /llms.txt build-time endpoint with all learn + mineral surfaces
P2-12: IndexNow GitHub Action — diff-based URL submission to Bing/Yandex/Naver
P2-13: Removed redundant "Professional Gemmological Tools" section from tools hub
P2-14: Quiz setup uses 2-column layout at lg (categories on left, controls on right)
P2-15: Mineral page breadcrumb moved above H1 (semantic + UX win)
@github-actions

github-actions Bot commented May 4, 2026

Copy link
Copy Markdown

🚀 Preview deployed to: https://1ffdaf7b.gemmology-dev.pages.dev

Required by .github/workflows/indexnow.yml — IndexNow endpoints fetch
https://gemmology.dev/<KEY>.txt to verify host ownership before accepting
URL submissions. Key value matches the INDEXNOW_KEY GitHub Actions secret.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented May 4, 2026 •

Copy link
Copy Markdown

Deploying gemmology-dev with  Cloudflare Pages  Cloudflare Pages

Latest commit: 5d0bf14
Status:🚫  Build failed.

View logs

@github-actions

github-actions Bot commented May 4, 2026

Copy link
Copy Markdown

🚀 Preview deployed to: https://0a236c2d.gemmology-dev.pages.dev

@Bissbert
Bissbert merged commit bd5fbe4 into main May 4, 2026
3 of 4 checks passed
Bissbert added a commit that referenced this pull request May 11, 2026
* fix(seo): exclude /admin/ from sitemap, robots, and indexing

The /admin/ route was leaking into the public sitemap-0.xml and was
indexable. This:

- Adds a sitemap filter so /admin/* URLs are excluded
- Adds Disallow: /admin/ to robots.txt
- Adds a noindex prop to BaseLayout and uses it on the admin page

Resolves P0-5 from the 2026-05-04 UX review.

* fix(a11y): promote learn property/comparison headings from h4 to h3

Sections in /learn/* skipped h3 entirely — h2 (e.g. "Symmetry Elements")
went straight to h4 children rendered by PropertyList and ComparisonBlock.
That violates WCAG 1.3.1 and breaks screen-reader/LLM passage attribution.

Both components now accept a headingLevel prop (default 3). SectionRenderer
passes headingLevel=4 only when the components are nested inside a
subsection (which already uses h3), preserving correct structure at every
depth.

Resolves P0-6 from the 2026-05-04 UX review.

* fix(content): single source of truth for mineral counts

Three pages displayed three different (and all stale) mineral counts:
- / claimed "50+"
- /gallery claimed "90+"
- /404 claimed "94+"

The live database has 159 families. New src/lib/stats.ts queries
mineral_families and mineral_expressions at build time and exposes the
counts via getSiteStats(); the three pages now consume that helper.

Resolves P0-7 from the 2026-05-04 UX review.

* fix(a11y): contrast palette pass for WCAG AA compliance

Replace crystal-500/600 in text and button backgrounds with crystal-700
across all components. crystal-600 (#0284c7) failed AA on white at 4.5:1;
crystal-700 (#0369a1) passes ~5.7:1 for body text and UI controls.

- text-crystal-600 → text-crystal-700 (links, headings, hover states)
- bg-crystal-500/600 with text-white → bg-crystal-700 (buttons, badges, active states)
- from-crystal-500 to-crystal-600 → from-crystal-600 to-crystal-700 (gradients)
- hover:bg-crystal-600 → hover:bg-crystal-800
- prose-a defaults: add underline + decoration colour for link-in-text-block rule
- Decorative progress bars left at crystal-500 (3:1 UI contrast holds)

* feat(ux): P0 fixes — hero copy, WebGL fallback, missing SVGs

- Rewrite hero to outcome-led headline ("Identify gems with confidence"),
  shift primary CTA to /gallery and secondary to /learn, append open-source
  reassurance line.
- Add WebGL availability check + prefers-reduced-motion handling to
  HeroCrystal3D; fall back to a static SVG illustration with proper
  role=img and aria-label for browsers without WebGL.
- Generate SVGs for the 46 mineral families that previously 404'd:
  18 rendered via crystal-renderer pipeline, 28 origin-aware placeholders
  for amorphous, simulant, composite, and synthetic-only entries.
- Add scripts/audit-crystal-svgs.mjs and wire it into prebuild via the new
  npm run check-svgs target so future drift fails CI before deploy.

* feat(ux): P1 fixes — fonts, hero lazy-load, nav, mobile CTA, JSON-LD, sitemap, about page, redirects, security headers

P1-1: Self-host Inter & JetBrains Mono via @fontsource-variable to eliminate CLS and third-party hop
P1-2: Hero respects prefers-reduced-motion (handled in P0 commit, verified)
P1-3: Header active state with aria-current="page" + visual underline marker
P1-4: Mineral detail mobile CTA — duplicate PlaygroundLink for sub-lg viewports
P1-5: Schema.org JSON-LD upgrades — LearningResource+Article, Wikidata Mineral additionalType, generic StructuredData component
P1-6: Sitemap lastmod + tiered priority (home/gallery/learn=1.0, minerals=0.8, docs=0.5)
P1-7: About page with editorial standards + author/reviewedBy/reviewedAt schema for learn entries
P1-8: _redirects with 12 legacy /learn flat-slug 301s
P1-9: _headers with CSP, HSTS preload, frame-ancestors none, per-path Cache-Control

* feat(ux): P2 polish batch — mineral page, footer, learn hub, titles, llms.txt, indexnow, quiz

P2-1: Learn hub "Start here" curated foundations section above the 90-card wall
P2-2: Category descriptions surface total count + remainder ("plus N more")
P2-3: DifficultyBadge — distinct hue + leading dot per level for at-a-glance scannability
P2-4: Learn hub client-side filter (data-search hashes; collapses empty groups)
P2-5: Removed duplicate CDL block from PlaygroundLink (kept canonical copy in CrystalStructureCard)
P2-6: Mineral page nested <main> → <article> (BaseLayout already provides <main>)
P2-7: Acronym title-casing helper (CVD/HPHT/FTIR/UV/IR/YAG/CZ…) for origin + growth method
P2-8: Gallery Groups filter collapses to <select> when >6 entries
P2-9: Footer "Packages" column hidden outside /docs
P2-10: <title> brand-first ("gemmology.dev — X") for SERP recall
P2-11: /llms.txt build-time endpoint with all learn + mineral surfaces
P2-12: IndexNow GitHub Action — diff-based URL submission to Bing/Yandex/Naver
P2-13: Removed redundant "Professional Gemmological Tools" section from tools hub
P2-14: Quiz setup uses 2-column layout at lg (categories on left, controls on right)
P2-15: Mineral page breadcrumb moved above H1 (semantic + UX win)

* chore(seo): add IndexNow verification key file

Required by .github/workflows/indexnow.yml — IndexNow endpoints fetch
https://gemmology.dev/<KEY>.txt to verify host ownership before accepting
URL submissions. Key value matches the INDEXNOW_KEY GitHub Actions secret.

This branch was successfully deployed

1 active deployment
gemmology-dev (Preview) — 5d0bf14d Deployed May 4, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant