- Run record
- loading
Loading this Run…
Loading the Revisions of this Run…
A published source file of Revision 0 of Fjora — Taste the North 1.2.0, shown as text in the browser.
Source is browser-viewable only. There is no download or archive endpoint, and no additional license is granted beyond inspection and research.
import { useState, type CSSProperties } from "react";
import { createRoot } from "react-dom/client";
import brandData from "../public/data/brand.json";
import productData from "../public/data/products.json";
import "./styles.css";
type Product = (typeof productData.products)[number];
const flavorCopy: Record<
Product["id"],
{ note: string; accent: string; accentSoft: string }
> = {
"glacier-lime": {
note: "Lime peel leads with a bright edge. Juniper brings a cool, resinous line, softened by arctic thyme.",
accent: "#b8cf8d",
accentSoft: "#e3ead7",
},
cloudberry: {
note: "Cloudberry opens rounded and gently tart. Chamomile turns floral, with rosehip at the finish.",
accent: "#e0a15c",
accentSoft: "#f1dfc7",
},
"spruce-tip": {
note: "Fresh spruce tip opens green and forested. Angelica adds earthy depth, with black currant leaf at the close.",
accent: "#729b82",
accentSoft: "#ccddd2",
},
};
const faqs = [
{
question: "Is Taste the North sweetened?",
answer:
"No. Every flavor is unsweetened, with no sugar and no sweeteners.",
},
{
question: "Does Fjora contain caffeine?",
answer: "No. All three seasonal flavors are caffeine-free.",
},
{
question: "Can I recycle the bottle?",
answer:
"The bottle is single-material glass and the cap is aluminium. Both are widely recyclable.",
},
{
question: "Is Taste the North available year-round?",
answer:
"No. It is a seasonal winter line, bottled once per season and available while stock lasts.",
},
];
function FjoraMark() {
return (
<span className="wordmark">
<svg
className="wordmark__symbol"
viewBox="0 0 42 36"
aria-hidden="true"
>
<path d="M2 21.5 12.6 9l7.1 8.1L27.6 4 40 21.5" />
<path d="M3 27.5c5.9-3 11.9-3 17.8 0s11.9 3 18 0" />
<path d="M7 32c4.6-1.8 9.2-1.8 13.8 0s9.2 1.8 13.8 0" />
</svg>
<span className="wordmark__name">FJORA</span>
</span>
);
}
function ArrowIcon() {
return (
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M5 12h13M13 6l6 6-6 6" />
</svg>
);
}
function App() {
const [selectedId, setSelectedId] = useState<Product["id"]>(
productData.products[0].id,
);
const selectedIndex = productData.products.findIndex(
(product) => product.id === selectedId,
);
const selected = productData.products[selectedIndex];
const selectedCopy = flavorCopy[selected.id];
const detailStyle = {
"--flavor-accent": selectedCopy.accent,
"--flavor-accent-soft": selectedCopy.accentSoft,
} as CSSProperties;
return (
<>
<a className="skip-link" href="#main-content">
Skip to content
</a>
<main id="main-content">
<section className="hero" id="top" aria-labelledby="campaign-title">
<img
className="hero__image"
src="/assets/hero-fjord.webp"
alt="Snow-covered mountains reflected in a still northern fjord"
fetchPriority="high"
/>
<div className="hero__wash" aria-hidden="true" />
<header className="site-header shell">
<a className="site-header__home" href="#top" aria-label="Fjora home">
<FjoraMark />
</a>
<nav className="site-nav" aria-label="Campaign sections">
<a href="#flavors">Flavors</a>
<a href="#story">Story</a>
<a href="#faq">FAQ</a>
</nav>
</header>
<div className="hero__content shell">
<div className="hero__copy">
<div className="eyebrow eyebrow--light">
<span>Seasonal winter line</span>
<span aria-hidden="true">01 — 03</span>
</div>
<h1 id="campaign-title">
<span>Taste</span>
<em>the North</em>
</h1>
<p className="hero__intro">
Glacial meltwater. Cold-steeped botanicals. Three flavors,
bottled once for winter.
</p>
<a className="primary-cta" href="#flavors">
<span>Meet the winter flavors</span>
<span className="primary-cta__icon">
<ArrowIcon />
</span>
</a>
</div>
<div className="hero__edition" aria-label="Campaign edition">
<span>FJ / 01</span>
<span className="hero__edition-line" aria-hidden="true" />
<span>Winter bottling</span>
</div>
</div>
<a className="scroll-cue" href="#flavors" aria-label="Scroll to flavors">
<span>Explore</span>
<span className="scroll-cue__line" aria-hidden="true" />
</a>
</section>
<section className="flavors section" id="flavors" aria-labelledby="flavors-title">
<div className="shell">
<div className="section-heading section-heading--split">
<div>
<p className="eyebrow eyebrow--dark">01 / The winter collection</p>
<h2 id="flavors-title">Choose your way north.</h2>
</div>
<p className="section-heading__aside">
One seasonal line. Three botanical compositions. Select a
bottle to discover its character.
</p>
</div>
<div className="flavor-picker" aria-label="Choose a flavor">
{productData.products.map((product, index) => {
const isSelected = product.id === selectedId;
return (
<button
className="flavor-option"
type="button"
key={product.id}
aria-pressed={isSelected}
onClick={() => setSelectedId(product.id)}
>
<span className="flavor-option__image-wrap">
<img
src={product.image}
alt={`${product.name} bottle`}
/>
</span>
<span className="flavor-option__label">
<span className="flavor-option__number">
0{index + 1}
</span>
<span className="flavor-option__name">{product.name}</span>
<span className="flavor-option__state" aria-hidden="true">
{isSelected ? "Viewing" : "View"}
</span>
</span>
</button>
);
})}
</div>
<article
className="flavor-detail"
style={detailStyle}
aria-live="polite"
aria-label={`${selected.name} details`}
>
<div className="flavor-detail__media">
<img
key={selected.id}
src={selected.image}
alt={`${selected.name} botanical sparkling water bottle`}
/>
<div className="flavor-detail__media-label" aria-hidden="true">
<span>Seasonal / Winter</span>
<span>FJ — 0{selectedIndex + 1}</span>
</div>
</div>
<div className="flavor-detail__content" key={`${selected.id}-copy`}>
<div className="flavor-detail__topline">
<span>Selected flavor</span>
<span>0{selectedIndex + 1} / 03</span>
</div>
<h3>{selected.name}</h3>
<div className="tasting-note">
<p className="detail-label">Tasting note</p>
<p>{selectedCopy.note}</p>
</div>
<div className="botanicals">
<p className="detail-label">Botanicals</p>
<ul>
{selected.botanicals.map((botanical) => (
<li key={botanical}>{botanical}</li>
))}
</ul>
</div>
<div className="flavor-detail__facts">
<span>Unsweetened</span>
<span>Caffeine-free</span>
</div>
</div>
</article>
</div>
</section>
<section className="story section" id="story" aria-labelledby="story-title">
<div className="shell story__grid">
<div className="story__image-wrap">
<img
src="/assets/story-botanicals.webp"
alt="Cloudberries, lime peel and evergreen botanicals resting on ice"
className="story__image"
loading="lazy"
/>
<div className="story__stamp" aria-hidden="true">
<span>Cold</span>
<span>steeped</span>
</div>
<span className="story__image-index" aria-hidden="true">
02
</span>
</div>
<div className="story__content">
<p className="eyebrow eyebrow--dark">02 / Our northern story</p>
<h2 id="story-title">Made in the measure of winter.</h2>
<div className="story__body">
<p>
Fjora is a Nordic botanical sparkling water. Its story begins
with glacial meltwater and botanicals left to steep in the
cold.
</p>
<p>
Taste the North is the winter chapter. Three flavors. One
bottling each season. A brief collection, available while it
lasts.
</p>
</div>
<dl className="story__facts">
<div>
<dt>Water</dt>
<dd>Glacial meltwater</dd>
</div>
<div>
<dt>Method</dt>
<dd>Cold-steeped botanicals</dd>
</div>
<div>
<dt>Release</dt>
<dd>Once each winter season</dd>
</div>
</dl>
</div>
</div>
</section>
<section className="faq section" id="faq" aria-labelledby="faq-title">
<div className="shell faq__grid">
<div className="faq__intro">
<p className="eyebrow eyebrow--dark">03 / The clear details</p>
<h2 id="faq-title">Questions, answered.</h2>
<p>Everything essential about the winter collection.</p>
</div>
<div className="faq__list">
{faqs.map((faq, index) => (
<details key={faq.question} className="faq-item">
<summary>
<span className="faq-item__number">0{index + 1}</span>
<span>{faq.question}</span>
<span className="faq-item__toggle" aria-hidden="true" />
</summary>
<div className="faq-item__answer">
<p>{faq.answer}</p>
</div>
</details>
))}
</div>
</div>
</section>
</main>
<footer className="footer">
<div className="shell">
<div className="footer__top">
<a href="#top" aria-label="Back to the top">
<FjoraMark />
</a>
<p>Taste the North</p>
</div>
<div className="footer__rule" aria-hidden="true" />
<div className="footer__bottom">
<p className="footer__contact">{brandData.footer_contact}</p>
<p className="footer__notice">{brandData.fictional_notice}</p>
<a className="footer__back" href="#top">
Back to top <span aria-hidden="true">↑</span>
</a>
</div>
</div>
</footer>
</>
);
}
createRoot(document.getElementById("root")!).render(<App />);