Changelog 937e350
rotating cannabis altnames on homapge
3 files of code, design or docs +60 −15 lines
Code, design and documents 3 files
source-archive.json +13 −13
Not shown: a picture, a generated file, or past the length shown per change.
src/components/HomePage.astro +35 −2
@@ -14,5 +14,5 @@ import { SHOW_PRICES } from "../lib/config.js"; import { homeSeo } from "../lib/seo.js"; import { viewByMetric } from "../lib/map-views.js"; -import { cultureOf } from "../lib/culture.js"; +import { cultureOf, cultureEntries } from "../lib/culture.js"; import { panelRows, PANEL_FROM } from "../lib/exports.js"; @@ -31,4 +31,13 @@ const { view = null } = Astro.props; const base = import.meta.env.BASE_URL; +// The hero's "cannabis" cycles through other names for the plant from the atlas, each with the +// language it comes from and a link to its entry. Only names for the plant itself, not for a +// joint, a strain or a deal. +const HERO_WORDS = ["cannabis", "ganja", "marijuana", "dagga", "kif", "maconha", "esrar", "mota", "anasha", "chamba", "wiet", "zatla", "pakalolo", "mbanje", "hashish", "matekoane", "zamal", "bangi", "diamba", "charas", "bhang", "grass", "pot", "dope"]; +const heroWords = HERO_WORDS.map((slug) => { + const e = cultureEntries.find((x) => x.url === `culture/language/${slug}/`); + if (!e) throw new Error(`HomePage: no atlas entry "language/${slug}" for the hero`); + return { word: e.title.toLowerCase(), note: e.origin ? e.origin.split(",")[0] : e.countries[0].name, href: `${base}${e.url}` }; +}); // where each map option lives: its own page, or the home page for the overall index const viewHref = (metric) => `${base}map/${viewByMetric[metric].slug ? viewByMetric[metric].slug + "/" : ""}`; @@ -165,5 +174,5 @@ const columnGroups = [["Shown by default", BASE_COLUMNS], ...["Score segments", )} <p class="eyebrow">Cannabis Freedom Index <span>{DATA_SNAPSHOT} edition</span></p> - <h1>How free is cannabis <em>in every country?</em></h1> + <h1>How free is <span class="visually-hidden">cannabis</span><a class="hero-word" id="heroWord" href={heroWords[0].href} aria-hidden="true" tabindex="-1" data-words={JSON.stringify(heroWords)}><span class="hw-w">{heroWords[0].word}</span><small class="hw-note">{heroWords[0].note}</small></a> <em>in every country?</em></h1> <div class="front-grid"> <div class="front-intro"> @@ -943,2 +952,26 @@ const columnGroups = [["Shown by default", BASE_COLUMNS], ...["Score segments", </script> </Base> + +<script> + // The hero's word for cannabis: a new one every few seconds, held while the pointer is on it, + // and still for anyone who prefers less motion. + const hw = document.getElementById("heroWord"); + if (hw && !matchMedia("(prefers-reduced-motion: reduce)").matches) { + const words = JSON.parse(hw.dataset.words); + const word = hw.querySelector(".hw-w"), note = hw.querySelector(".hw-note"); + let i = 0, held = false; + hw.addEventListener("mouseenter", () => (held = true)); + hw.addEventListener("mouseleave", () => (held = false)); + setInterval(() => { + if (held || document.hidden) return; + i = (i + 1) % words.length; + hw.classList.add("out"); + setTimeout(() => { + word.textContent = words[i].word; + note.textContent = words[i].note; + hw.href = words[i].href; + hw.classList.remove("out"); + }, 380); + }, 2800); + } +</script>
src/styles/global.css +12 −0
@@ -2037,2 +2037,14 @@ button.sv-row .sv-chip { font: 400 10.5px var(--mono); text-transform: uppercase .legend-poster { margin-left: auto; color: var(--text-2); text-decoration: underline; text-decoration-color: var(--muted); text-underline-offset: 3px; } .legend-poster:hover { color: var(--text); } + +/* The hero's word for cannabis, cycling through the atlas's names for it (HomePage.astro), with + the language each comes from set small beside it. The question's second half keeps its line. */ +.front h1 em { display: block; } +.hero-word { position: relative; display: inline-block; color: inherit; } +.hw-w { display: inline-block; transition: opacity .38s ease, transform .38s ease, filter .38s ease; } +.hero-word.out .hw-w { opacity: 0; transform: translateY(.14em); filter: blur(6px); } +.hero-word:hover .hw-w { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: .1em; text-decoration-color: var(--muted); } +.hw-note { position: absolute; left: 100%; top: .32em; margin-left: .45em; font: 400 11.5px var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--muted); white-space: nowrap; transition: opacity .38s ease; } +.hero-word.out .hw-note { opacity: 0; } +@media (max-width: 700px) { .hw-note { left: auto; right: 0; top: auto; bottom: 100%; margin: 0 0 2px; font-size: 10px; } } +@media (prefers-reduced-motion: reduce) { .hw-w, .hw-note { transition: none; } }