Developer Handoff · Restyle Change Log
A plain-language summary of every visual change in this restyle pass — the exact file, what changed, and the CSS / Tailwind classes involved. Organised by theme so you can review one concern at a time.
The design system is token-driven: colours, fonts, and sizes live as CSS custom properties in src/design-system/tokens.css (consumed by Tailwind v4 via @theme). Most colour changes happen there once and cascade everywhere.
This log records only the latest final state of each item — the file, what it is now, and the token/class — not the iterations it took to get there.
Two new colours were introduced and two existing ones repurposed.
| Token | Value | Meaning |
|---|---|---|
--color-accent-fill | #cc0230 | Crimson — the new fill for all solid CTA buttons, pills and chips |
--color-accent-fill-hover | oklch(accent-fill − 12% black) | Darker crimson for hover |
--color-accent-fill-ink | #ffffff | White — the text/icon colour that sits on crimson |
--color-highlight | #6eceb2 | Mint — hover/active state for nav tabs and footer links |
--color-accent | #e9c84b (unchanged) | Old warm yellow — now only used as the token behind a few non-button accents |
/* tokens.css — added */
--color-accent-fill: #cc0230;
--color-accent-fill-hover: color-mix(in oklch, var(--color-accent-fill), black 12%);
--color-accent-fill-ink: #ffffff;
--color-highlight: #6eceb2;
Because these are registered in @theme, Tailwind auto-generates the utility classes bg-accent-fill, text-accent-fill-ink, border-accent-fill, hover:bg-accent-fill-hover, and text-highlight / hover:text-highlight.
The two off-white page surfaces were set to pure white. Everything that paints a page background (bg-cream, bg-surface) and the cream-coloured logo (text-cream) followed automatically.
/* before → after */
--color-cream: #f4efe4; → #ffffff;
--color-surface: #faf7f1; → #ffffff;
index.html — the browser theme colour was matched to white:
<meta name="theme-color" content="#ffffff" /> <!-- was #faf7f1 -->
All text now uses Zeitung, self-hosted (no external font CDN). The licensed .woff2 files were mirrored from the production site into src/assets/fonts/ (Regular 400 + Bold 700 — the only two weights that exist; the browser synthesises intermediate weights and there are no italics).
/* tokens.css */
--font-serif: 'Zeitung', ui-sans-serif, system-ui, -apple-system, Arial, sans-serif;
--font-sans: 'Zeitung', ui-sans-serif, system-ui, -apple-system, Arial, sans-serif;
/* styles.css — added @font-face */
@font-face { font-family:'Zeitung'; font-weight:400; font-display:swap;
src:url('./fonts/Zeitung-Regular.woff2') format('woff2'); }
@font-face { font-family:'Zeitung'; font-weight:700; font-display:swap;
src:url('./fonts/Zeitung-Bold.woff2') format('woff2'); }
index.html — the Google Fonts <link> (Fraunces + DM Sans) and its preconnect hints were removed.
Button.vue is the single source of truth for buttons. Variants are picked with <Button variant="…">.
// base string — added `uppercase`
'... font-sans font-semibold uppercase ...'
// sizes — text set to 14px across the board
sm: 'text-[14px] px-[18px] py-[9px] tracking-label',
md: 'text-[14px] px-[26px] py-[13px] tracking-label',
lg: 'text-[14px] px-[34px] py-[17px] tracking-label',
| Variant | Before | After |
|---|---|---|
primary (green button) | green fill, yellow text | green fill, white text — bg-brand text-white border-brand |
accent (main CTA) | yellow fill, green text | crimson fill, white text — bg-accent-fill text-accent-fill-ink border-accent-fill |
secondary | outline | crimson fill, white text (same as accent) |
ghost / danger | unchanged | unchanged |
Note: secondary and accent are currently identical (both crimson). The Hero's "Learn more" secondary CTA (rendered inline, see §5) was matched to the same crimson fill.
The Hero renders its secondary CTA inline (not via <Button>), so it has to be styled directly. On brand-green heroes it is now crimson fill + white text, uppercase, 14px:
class="… rounded-pill border border-accent-fill bg-accent-fill px-[34px] py-[17px]
text-[14px] font-semibold uppercase tracking-label text-accent-fill-ink
transition-colors duration-base hover:bg-accent-fill-hover"
The Hero's primary CTA already uses the accent variant on green surfaces, so it's crimson too.
Every add-to-cart button is now the crimson accent variant.
| File | Change |
|---|---|
ProductCard.vue | ctaVariant prop default 'primary' → 'accent' |
BundleCard.vue | add-to-cart <Button> variant="primary" → "accent" |
ShopPage.vue | product grid :cta-variant="… 'accent' : 'primary'" → cta-variant="accent" (no more alternating) |
ProductPage.vue | main add-to-cart already accent (unchanged) |
BundlePage.vue | add-to-cart already accent (unchanged) |
<!-- checkout button -->
<Button variant="accent" …> <!-- was variant="primary" -->
All previously-yellow solid fills now use the crimson token pair bg-accent-fill + text-accent-fill-ink.
| File | Element |
|---|---|
IconButton.vue | accent variant (cart icon button) |
LanguageSwitcher.vue | active language pill (all 3 tones) |
Navbar.vue | mobile cart CTA pill |
Kaiserhacks.vue | video play chip |
Badge.vue | accent badge variant |
About.vue | "HISTORY & SCIENCE" eyebrow (Badge variant="brand" → "accent") and the "TODAY" timeline pill (.pill-accent CSS now crimson + white) |
/* About.vue — .pill-accent (the "TODAY" pill) */
.pill-accent {
background: var(--color-accent-fill);
color: var(--color-accent-fill-ink);
border-color: var(--color-accent-fill);
}
The hover/active highlight on the brand-green navbar (and the brand footer links) changed from yellow to mint via the new --color-highlight token:
text-accent → text-highlight (active nav tab)
hover:text-accent → hover:text-highlight (nav tab + footer link hover)
Not changed: the mobile cart-count number badge (Navbar.vue, a green circle with a count) is still yellow — it's a count indicator, not a tab highlight. Easy to switch if wanted.
The italic emphasis words inside hero/section headlines, and the small eyebrow labels above them, were yellow. They now match the surrounding white hero text.
| File | Change |
|---|---|
HomePage.vue, CategoryPage.vue, ProductPage.vue, ShopPage.vue | headline <em> emphasis: text-accent-soft → text-cream |
BrandHero.vue, Revitalization.vue | same emphasis swap |
CategoryPage.vue, ShopPage.vue, Kaiserhacks.vue | hero eyebrows: text-accent → text-cream/75 |
(--color-cream is now #ffffff, so these read as white on the green heroes.)
The soft S-curve between coloured sections became a straight diagonal that sits low on the left, high on the right, and the divider band was doubled in height so the slope is roughly twice as steep.
height: h-12 md:h-16 → h-24 md:h-32 (48/64px → 96/128px)
viewBox: 0 0 1440 64 → 0 0 1440 128
path: (cubic-bézier wave) → M0,0 L0,116 L1440,12 L1440,0 Z
The seam-free construction is unchanged: a full-height <rect> paints the lower section colour and the <path> paints the upper section colour.
The same diagonal divider also sits between the Kaiserhacks green header and the white body below it: Kaiserhacks.vue imports WaveDivider and renders <WaveDivider from="brand" to="surface" /> between the bg-brand header and the content <div> (which gains -mt-px to sit flush).
In the search overlay, the price labels on the brand-green tone were yellow. They are now white to match the rest of the green-surface text:
// brand tone
price: 'text-accent' → 'text-cream' // (#ffffff on green)
The paper and cream tones keep text-brand (unchanged).
The home-page intro animation (the in-flow figure entrance — the full-screen SplashIntro overlay was already retired) previously used hand-traced approximations of the woman + waterfall. It now uses the official Kaiser-Natron brand vectors: the Ikone ("Hebe") and the Waterfall (2021 Druckdaten-Final), converted from EPS to SVG.
src/assets/brand/ for provenance; the extracted path data lives in src/components/heroFigures.js (ladyMint, ladyWhite, waterfall).#006648 outline tone is dropped; the figures render as flat mint silhouettes, matching the established splash aesthetic. Mint tones: lady #72c1ad, waterfall #6eceb2; natron handful #ffffff.0 0 2760 3624 viewBox: the lady at the origin (native 1828×3624), the waterfall half-scale (scale(0.5)) to her right and vertically centred against her (translate(1793,1310)).left-m), waterfall from the right (right-m), the white natron fades in once she's landed (mound-m), tagline + SINCE 1881 last; same edge-feather mask and reduced-motion handling.Side effect: the home-page chunk shrank ~214 KB → ~70 KB because the new heroFigures.js (~57 KB) replaces the much larger traced splashPaths.js import. splashPaths.js / SplashIntro.vue remain only as unused legacy.
The 250 g box product shot had a muted, grey/pine green that didn't match the brighter brand green of the other Kaiser-Natron powder packs (e.g. the 50 g sachet public/products/kaiser-natron-pulver-50-g-beutel.webp). It was replaced with an updated box render carrying the correct vivid brand green (the dark-green 3-D side panels and the red bottom band are intact).
public/products/*.webp (served as static assets; the build mirrors them to dist/products/*.webp). Convention: transparent RGBA, ~1200 px tall.quality 92, no scaling needed — already matching the original dimensions), so edges/text stay crisp.The new accent-fill, accent-fill-hover, accent-fill-ink tokens were added to the colour-swatch reference so the in-app design-system page stays accurate.
The brand name is now written Kaiser-Natron® consistently — hyphenated, with the ® mark — on every visible mention. Two problems were fixed:
brand field carried no trademark mark. (The long Kaiserhacks recipe copy and the product title fields already had Kaiser-Natron® and were left as-is.)Kaiser Natron. All standardised to the hyphenated form.| File | What changed |
|---|---|
src/i18n/messages.js | shop.headline, ds.hero.headline.a, home.banner.sub, home.brand.headline.a, home.teaser.cta, and the bundle.*.items.* lists — de and en — now read Kaiser-Natron® |
src/api/products.js | brand: 'Kaiser-Natron' → 'Kaiser-Natron®' (all 11 products; shown in search results via Search.vue) |
src/design-system/components/Logo.vue | default accessible title prop 'Kaiser Natron' → 'Kaiser-Natron®' |
src/design-system/components/Navbar.vue | logo link aria-label 'Kaiser Natron home' → 'Kaiser-Natron home' |
Not touched: generic ingredient references ("Natron", "Natronwasser", "Natron-basierte …") — those mean the substance, not the brand, so they take no ®. Image alt text keeps the plain hyphenated name (not on-screen).
The product-hero headline (ds.hero.headline.*, de + en) was reworded from a generic cleaning-shine line to the brand's own versatility voice (drawn from kaiser-natron.de — "Die Verwendungsmöglichkeiten … sind beinah grenzenlos"). The three-part split (a / emphasised em / b) is unchanged; only the words changed.
DE "Kaiser-Natron® für alles was glänzen soll."
→ "Kaiser-Natron® für fast alles im Alltag."
EN "Kaiser-Natron® for everything that should shine."
→ "Kaiser-Natron® for almost anything at home."
The bundle images are AI-composed. Rather than replace them, each is now marked with a small, faint "AI Edited" caption in the bottom-right of the image, so the AI origin is disclosed. It's data-driven, so it disappears automatically once real photography replaces a given image.
bundles.js — each bundle record gains aiEdited: true.BundleCard.vue — new aiEdited Boolean prop; when true, renders the overlay span inside the media area (both the card-link and plain-media branches).Bundles.vue — passes :ai-edited="bundle.aiEdited" to all four BundleCard instances (mobile + grid + sidebar + carousel).HomePage.vue / BundlePage.vue — carry aiEdited through to the rendered records; BundlePage renders the same overlay on its large hero image (desktop + mobile).<span class="pointer-events-none absolute bottom-0 right-0 z-[1] px-2 py-0.5
text-[10px] font-medium uppercase tracking-label text-white/55
drop-shadow-[0_1px_2px_rgba(0,0,0,0.45)]">AI Edited</span>
Set a bundle's aiEdited: false (or drop it) in bundles.js to remove the badge once its image is a real photo.
The Revitalization section was stripped back. Removed entirely:
revit.notifyCta — "Get early access" / "Early Access sichern").This is done at the usage site, not the component: revitCopy no longer passes features or notifyCta, and the :features / :notify-cta / @notify bindings (and the orphaned onRevitNotify handler) were removed. The section now renders eyebrow + headline + sub only.
Revitalization.vue is unchanged and still reusable — its v-if="features.length" and v-if="notifyCta" guards simply render nothing when those props are absent. The unused revit.feature.* / revit.notifyCta i18n keys are left in place (harmless) in case the section is restored.
On the shop page, the catalogue is split into four use-group sections, each fronted by a full-width colour banner in the brand's own use-group colour (sourced from kaiser-natron.de), with diagonal dividers carrying the colour in and back out to the neutral product grid below. (The home page was left unchanged — it keeps its 3-card ProductTeaser.)
tokens.css — the old "Haushalt" lump was split into Clean (cleaning) and Wash (laundry):
| Token | Hex | Section | Products |
|---|---|---|---|
--color-cat-kitchen | #c6d47d (lime) | Küche / cook | Pulver, Tabletten |
--color-cat-clean | #eb5a61 (grapefruit) | Reinigung / clean | cleaners, sprays, descalers |
--color-cat-wash | #c15a7e (plum) | Wäsche / wash | wash-soda, starch, stain removers |
--color-cat-care | #f1864c (orange) | Pflege / care | bath, foot-bath, sport |
Tailwind v4 auto-emits bg-cat-kitchen / bg-cat-clean / bg-cat-wash / bg-cat-care.
src/api/products.js — USE_CASES is now ['cook','clean','wash','care']; Wäsche maps to the new wash group (was clean). productsByUseCase returns all four buckets.
Hero.vue has kitchen / clean / wash / care tones. Lime keeps dark ink text; the other three take cream (white) text. Each sets an eyebrowColor applied inline (overrides the global .eyebrow { color: muted }). WaveDivider.vue gained matching kitchen / clean / wash / care tones.
ShopPage.vue loops the four use-cases; each renders WaveDivider → <Hero :tone="section.cat"> (hero product + mixed-font heading + CTAs) → WaveDivider → a section title + product grid. CAT_TONE maps cook→kitchen, clean→clean, wash→wash, care→care; CAT_HERO_ID picks the headline product (Pulver / Allzweck-Spray / Daunenwasch / Bad). A per-section title (shop.section.<id>.products.title) now sits above each grid.
Each banner carries two buttons (via the Hero #actions slot): "add to cart" in the brand crimson (Button variant="accent", adds the section's hero product) and "learn more" as a white-outline ghost (border-white/90 text-white, links to the product page).
The white-outline "learn more" reads well on the saturated grapefruit / plum / orange banners but is low-contrast on the light lime (Kitchen) banner — may want a dark-outline variant there.
The green title fold was min-h:calc(100svh − nav) but only holds a compact title band, leaving too much empty green. Reduced to calc(50svh − var(--nav-h)). A diagonal then drops into a thin white band (h-6 md:h-10) before the first colour banner, so the green hero and the lime Kitchen banner don't butt directly together.
Added for parity with /haushalt + /pflege:
router/index.js — new /kueche route → CategoryPage { slug: 'kueche', useCase: 'cook' }.CategoryPage.vue — slug validator allows kueche; useCase allows cook.messages.js — full category.kueche.* copy (de + en), mirroring pflege/haushalt.Resolved (see §21). The three content-complete category pages (/kueche, /haushalt, /pflege) are now wired into the footer. /waesche (wash) is intentionally deferred pending brand copy — the shop's in-page wash section covers laundry in the meantime.
Decision. Of the four use-groups, the three with complete copy (cook/clean/care → /kueche, /haushalt, /pflege) are kept as standalone landing pages alongside the shop's in-page sections, and linked from the footer. The wash group has no standalone page (/waesche) yet — deferred until brand copy is supplied; the shop's wash section covers laundry meanwhile.
Footer links. Footer.vue exploreLinks now lists Shop → Küche → Haushalt → Pflege → Bundles → About (the /kueche link was previously missing — page was reachable only by direct URL). Order follows the shop's use-group order.
Naming aligned to the shop. The footer link labels and the category-page eyebrows now use the shop's plain section names (shop.feature.*) instead of the older descriptive variants, so a section and its landing page read identically:
| Page (route) | use-case | eyebrow + footer label — DE / EN | was |
|---|---|---|---|
/kueche | cook | Küche / Kitchen | "Küche & Backen" / "Kitchen & baking" |
/haushalt | clean | Reinigung / Clean | "Haushalt & Reinigung" / "Home & cleaning" |
/pflege | care | Pflege / Care | "Pflege & Wohlbefinden" / "Personal care & wellbeing" |
Route slugs are unchanged (/haushalt still serves the clean group); only the visible labels/eyebrows moved to the new names.
There is no membership programme, so every trace of one was removed (the join button went earlier in §U2; this completes it). Decision: single retail price — bundles now show only their regular price (e.g. €24,90); the old lower memberPrice was dropped entirely (no discount remains).
bundles.js — memberPrice deleted from all three bundles.BundleCard.vue — removed the memberPrice prop, the memberLabel computed, and the "Mitglieder: €X" line under the price.Bundles.vue — removed all four :member-price bindings, the joinCta prop, the join emit, both "become a member" buttons (stacked + sidebar), and the now-unused Button import. Stale "why join" / "member pitch" comments reworded to "why bundle".BundlePage.vue — removed the memberPriceLabel computed and the member price line in both desktop and mobile hero blocks.HomePage.vue — dropped memberPrice from the localized-bundle mapping.messages.js). Deleted orphaned keys bundle.memberPrice, bundles.joinCta, bundles.card.memberPrefix. bundles.card.priceLabel → "Preis" / "Price" (was "Verkaufspreis" / "Retail price"). The section subtitle + three benefits were rewritten from membership perks to bundle value (no savings claim, since the price is now flat):
ds.bundleCard.description / ds.bundles.description updated to drop the member-price / member-CTA mentions.The headline.em stays "Vorteile / Benefits" — it now reads as the bundles' advantages rather than membership perks.
The cream second-fold banner reads "Ein Pulver, hundert Anwendungen im Haushalt" / "One powder, a hundred uses around the home" but showed the Bad 500 g (bath) product — and its add-to-cart + "learn more" link pointed there too, contradicting the powder message. Repointed the whole banner to powder:
imgBanner → /products/kaiser-natron-pulver-3.490-g-eimer.webp (the bulk bucket — visually reinforces "a hundred uses"; deliberately not the 250 g Großpackung, which is already the first-fold hero).bannerProductId → kaiser-natron-pulver-3490-g-eimer, so the CTA adds the powder and "learn more" links to /shop/kaiser-natron-pulver-3490-g-eimer.image-alt → "Kaiser-Natron® Pulver 3.490 g Eimer".No new asset needed — the bucket image already shipped in public/products/.
The brand owner flagged the "Revitalization Center" name + icons as off-brand. Removed the section from the public site for now:
HomePage.vue — removed the <Revitalization> block and the two diagonal SVG dividers that bracketed it (the cream banner now flows straight into the cream About section), plus the Revitalization import and the revitCopy computed.{ key: 'nav.revitalization', href: '/#revitalize' } entry from every page's nav array (9 files) so nothing links to the missing anchor.Kept in reserve (not deleted): Revitalization.vue, its design-system docs page, the revit.* i18n keys, and the /design/preview/revitalization route — so the section can be reinstated (with a corrected name) by re-adding the import, revitCopy, the render block, and the nav entries.
Brand-owner review: Sport Profi is a laundry product and belongs under Household, not Bathing & Care. In the four-group taxonomy laundry = wash, so USE_CASE_BY_CATEGORY.Sport changed 'care' → 'wash'. The kaiser-natron-sport-profi-250-ml product now appears in the Wäsche / wash (plum) section instead of Pflege / care. Doc comment updated to match.
Brand-owner review: the "Original from Austria" claim is inaccurate. Removed the leading sentence from footer.tagline (DE + EN); the tagline now opens at "Reines Natron für Küche, Haushalt und Pflege …" / "Pure sodium bicarbonate for the kitchen, the home, and personal care …".
Other Austria-flavoured strings remain and are separate decisions, not part of this change: footer.madeIn / product.prop.made-in-austria ("In Österreich abgefüllt" / "Bottled in Austria") and the Impressum/Datenschutz Vienna address. Flag to the user if the origin correction should extend to these.
Jump-buttons. Under the shop hero's title + sub, a row of four skewed parallelogram buttons (one per use-group, each filled with its own category colour) smooth-scrolls to the matching section:
sections; label is section.feature.-skew-x-12 on the button with a counter-skew skew-x-12 on the inner <span> so the text stays upright — echoes the angled (Soulmates) CI.CAT_BTN: bg-cat-kitchen text-brand (lime is light → dark ink), the other three bg-cat-* text-white.scrollToSection(id) calls el.scrollIntoView({ behavior: 'smooth' }); each section's existing scroll-mt keeps the landing just below the sticky nav.White gap removed. The thin white band (-mt-px h-6 md:h-10 bg-cream) and its preceding WaveDivider from="brand" to="cream" — added in §20 — are gone. The green hero now flows straight into the first colour banner via a single diagonal: the section loop is (section, i) and the first divider is :from="i === 0 ? 'brand' : 'cream'".
The /design style guide still described the old design (Fraunces + DM Sans, "pine green", warm-yellow CTAs, three Hero tones) and omitted the new tokens. Audited all 26 sections and corrected every drift:
ColorsSection now shows the mint highlight, the four category colours (cat-kitchen/clean/wash/care), plus brand-float and cream-dark. New group titles ds.colors.group.highlight / .categories; ds.colors.description rewritten.text-display / text-headline-lg / text-headline-md). Descriptions rewritten to the single Zeitung family.HeroPreview validator; ds.hero.description "three surface tones" → seven.ds.buttons.description fixed — accent is crimson (was "warm-yellow"), "pine-green" → "brand-green".tokens.css header rewritten; checkout font in the Stripe / Express mounts changed from hardcoded DM Sans → the --font-sans stack; "Fraunces" / "pine green" comments corrected.The floating mobile menu (hamburger) button in the bottom-right cluster was the green float variant (bg-brand-float), so it didn't read as an action and clashed with the crimson cart button sitting next to it. It now uses the accent variant — crimson fill, white icon — matching the cart and the rest of the CTAs:
| Was | Now |
|---|---|
variant="float" (green, shadow → lg) | variant="accent" + shadow="md" (crimson) |
Icon size (24) and stroke width (2) are unchanged. The two floating buttons are now a matched crimson pair.
View on site — on a narrow viewport (< 1100px), bottom-right.
Crimson #cc0230 — all buttons / CTAs / solid pills (with #ffffff text)
Mint #6eceb2 — nav tab + footer link hover/active highlight
White #ffffff — page backgrounds (cream + surface) and button text on green