feat(search): tone-aware product search with mobile + desktop triggers

Introduces:
- src/api/fixtures/products.js + src/api/index.js — placeholder catalog
  (24 products across Kaiser-Natron / Holste / Gazelle / Grüne Tante /
  Linda) so search has real data. Same shape the backend will return.
- Search.vue — Teleported dialog. Token-scored, diacritics-folded, ß→ss
  normalized so German terms still match when users type ASCII. Keyboard
  navigation (↑/↓/Enter/Esc), autofocus on open, scroll-lock. Full-screen
  on mobile, centered command-palette modal on md+. Tone prop defaults
  to 'brand' so the overlay reads as the site's primary affordance
  (green), with 'paper' and 'cream' variants available.
- SearchSection.vue + ds-search route + sidebar entry.
- DE + EN strings for the overlay and the showcase.

Navbar wires the triggers: desktop search button sits left of the
LanguageSwitcher in the right cluster; mobile gets a matching bottom-
left floating button (cream surface, safe-area padded) that mirrors the
existing bottom-right cart/menu cluster. The Search overlay is mounted
inside Navbar with the default catalog, so every page that renders a
Navbar gets search for free. Selected products bubble up as a
@select event for navigation handling.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-04-21 13:33:18 +01:00
parent 45402733f7
commit af4e03a155
52 changed files with 1235 additions and 33 deletions

View File

@@ -1 +1 @@
import{L as e,c as t,k as n,m as r,p as i,s as a,tt as o,x as s}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as c}from"./i18n-B16vzzU0.js";import{t as l}from"./Badge-CHfkTK9K.js";import{t as u}from"./SectionShell-kXxofFIK.js";import{t as d}from"./Card-DRIe4MlQ.js";var f={class:`eyebrow mb-5`},p={class:`flex flex-wrap gap-3`},m={class:`eyebrow mb-5`},h={class:`flex flex-wrap gap-3`},g={__name:`BadgesSection`,setup(g){let{t:_}=c();return(c,g)=>(s(),t(u,{eyebrow:e(_)(`ds.eyebrow.components`),title:e(_)(`ds.badges.title`),description:e(_)(`ds.badges.description`)},{default:n(()=>[a(`section`,null,[a(`h2`,f,o(e(_)(`ds.heading.variants`)),1),r(d,{tone:`paper`},{default:n(()=>[a(`div`,p,[r(l,{variant:`neutral`},{default:n(()=>[i(o(e(_)(`ds.badges.neutral`)),1)]),_:1}),r(l,{variant:`brand`},{default:n(()=>[i(o(e(_)(`ds.badges.brand`)),1)]),_:1}),r(l,{variant:`accent`},{default:n(()=>[i(o(e(_)(`ds.badges.accent`)),1)]),_:1}),r(l,{variant:`subtle`},{default:n(()=>[i(o(e(_)(`ds.badges.subtle`)),1)]),_:1}),r(l,{variant:`success`},{default:n(()=>[i(o(e(_)(`ds.badges.success`)),1)]),_:1}),r(l,{variant:`warning`},{default:n(()=>[i(o(e(_)(`ds.badges.warning`)),1)]),_:1}),r(l,{variant:`danger`},{default:n(()=>[i(o(e(_)(`ds.badges.danger`)),1)]),_:1})])]),_:1})]),a(`section`,null,[a(`h2`,m,o(e(_)(`ds.heading.nonUppercase`)),1),r(d,{tone:`paper`},{default:n(()=>[a(`div`,h,[r(l,{variant:`brand`,uppercase:!1},{default:n(()=>[i(o(e(_)(`ds.badges.newRelease`)),1)]),_:1}),r(l,{variant:`accent`,uppercase:!1},{default:n(()=>[i(o(e(_)(`ds.badges.featured`)),1)]),_:1}),r(l,{variant:`subtle`,uppercase:!1},{default:n(()=>[...g[0]||=[i(`v2.1.0`,-1)]]),_:1})])]),_:1})])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{g as default}; import{L as e,c as t,k as n,m as r,p as i,s as a,tt as o,x as s}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as c}from"./i18n-DUHlbRTU.js";import{t as l}from"./Badge-BPajj5yh.js";import{t as u}from"./SectionShell-DTyMkRQY.js";import{t as d}from"./Card-ailigDjs.js";var f={class:`eyebrow mb-5`},p={class:`flex flex-wrap gap-3`},m={class:`eyebrow mb-5`},h={class:`flex flex-wrap gap-3`},g={__name:`BadgesSection`,setup(g){let{t:_}=c();return(c,g)=>(s(),t(u,{eyebrow:e(_)(`ds.eyebrow.components`),title:e(_)(`ds.badges.title`),description:e(_)(`ds.badges.description`)},{default:n(()=>[a(`section`,null,[a(`h2`,f,o(e(_)(`ds.heading.variants`)),1),r(d,{tone:`paper`},{default:n(()=>[a(`div`,p,[r(l,{variant:`neutral`},{default:n(()=>[i(o(e(_)(`ds.badges.neutral`)),1)]),_:1}),r(l,{variant:`brand`},{default:n(()=>[i(o(e(_)(`ds.badges.brand`)),1)]),_:1}),r(l,{variant:`accent`},{default:n(()=>[i(o(e(_)(`ds.badges.accent`)),1)]),_:1}),r(l,{variant:`subtle`},{default:n(()=>[i(o(e(_)(`ds.badges.subtle`)),1)]),_:1}),r(l,{variant:`success`},{default:n(()=>[i(o(e(_)(`ds.badges.success`)),1)]),_:1}),r(l,{variant:`warning`},{default:n(()=>[i(o(e(_)(`ds.badges.warning`)),1)]),_:1}),r(l,{variant:`danger`},{default:n(()=>[i(o(e(_)(`ds.badges.danger`)),1)]),_:1})])]),_:1})]),a(`section`,null,[a(`h2`,m,o(e(_)(`ds.heading.nonUppercase`)),1),r(d,{tone:`paper`},{default:n(()=>[a(`div`,h,[r(l,{variant:`brand`,uppercase:!1},{default:n(()=>[i(o(e(_)(`ds.badges.newRelease`)),1)]),_:1}),r(l,{variant:`accent`,uppercase:!1},{default:n(()=>[i(o(e(_)(`ds.badges.featured`)),1)]),_:1}),r(l,{variant:`subtle`,uppercase:!1},{default:n(()=>[...g[0]||=[i(`v2.1.0`,-1)]]),_:1})])]),_:1})])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{g as default};

View File

@@ -1,4 +1,4 @@
import{L as e,c as t,k as n,m as r,p as i,s as a,tt as o,x as s}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as c}from"./Icon-BaDkTwYL.js";import{t as l}from"./i18n-B16vzzU0.js";import{t as u}from"./Button-DOTlaJtk.js";import{t as d}from"./SectionShell-kXxofFIK.js";import{t as f}from"./Card-DRIe4MlQ.js";var p={class:`eyebrow mb-5`},m={class:`flex flex-wrap gap-3`},h={class:`eyebrow mb-5`},g={class:`grid md:grid-cols-3 gap-4`},_={class:`eyebrow mb-4`},v={class:`flex flex-wrap gap-3`},y={class:`eyebrow mb-4`},b={class:`flex flex-wrap gap-3`},x={class:`eyebrow mb-4 !text-cream opacity-80`},S={class:`flex flex-wrap gap-3`},C={class:`eyebrow mb-5`},w={class:`flex flex-wrap items-center gap-3`},T={class:`eyebrow mb-5`},E={class:`flex flex-wrap items-center gap-3`},D={class:`eyebrow mb-5`},O={class:`flex flex-wrap gap-3 items-center`},k={class:`eyebrow mb-5`},A={class:`max-w-md space-y-3`},j={class:`eyebrow mb-5`},M={class:`rounded-md border border-line bg-paper p-6 font-mono text-[12px] text-ink`},N={class:`whitespace-pre-wrap`},P={__name:`ButtonsSection`,setup(P){let{t:F}=l();return(l,P)=>(s(),t(d,{eyebrow:e(F)(`ds.eyebrow.components`),title:e(F)(`ds.buttons.title`),description:e(F)(`ds.buttons.description`)},{default:n(()=>[a(`section`,null,[a(`h2`,p,o(e(F)(`ds.heading.variants`)),1),r(f,{tone:`paper`},{default:n(()=>[a(`div`,m,[r(u,{variant:`primary`},{default:n(()=>[i(o(e(F)(`ds.buttons.primary`)),1)]),_:1}),r(u,{variant:`accent`},{default:n(()=>[i(o(e(F)(`ds.buttons.accent`)),1)]),_:1}),r(u,{variant:`secondary`},{default:n(()=>[i(o(e(F)(`ds.buttons.secondary`)),1)]),_:1}),r(u,{variant:`ghost`},{default:n(()=>[i(o(e(F)(`ds.buttons.ghost`)),1)]),_:1}),r(u,{variant:`danger`},{default:n(()=>[i(o(e(F)(`ds.buttons.danger`)),1)]),_:1})])]),_:1})]),a(`section`,null,[a(`h2`,h,o(e(F)(`ds.heading.onDifferentSurfaces`)),1),a(`div`,g,[r(f,{tone:`paper`},{default:n(()=>[a(`p`,_,o(e(F)(`ds.cards.paper`)),1),a(`div`,v,[r(u,{variant:`primary`},{before:n(()=>[r(c,{name:`cart`,size:18})]),default:n(()=>[i(` `+o(e(F)(`ds.buttons.addToCart`)),1)]),_:1}),r(u,{variant:`ghost`},{default:n(()=>[i(o(e(F)(`ds.buttons.learnMore`)),1)]),_:1})])]),_:1}),r(f,{tone:`cream`},{default:n(()=>[a(`p`,y,o(e(F)(`ds.cards.cream`)),1),a(`div`,b,[r(u,{variant:`primary`},{before:n(()=>[r(c,{name:`cart`,size:18})]),default:n(()=>[i(` `+o(e(F)(`ds.buttons.addToCart`)),1)]),_:1}),r(u,{variant:`ghost`},{default:n(()=>[i(o(e(F)(`ds.buttons.learnMore`)),1)]),_:1})])]),_:1}),r(f,{tone:`brand`},{default:n(()=>[a(`p`,x,o(e(F)(`ds.cards.brand`)),1),a(`div`,S,[r(u,{variant:`accent`},{before:n(()=>[r(c,{name:`cart`,size:18})]),default:n(()=>[i(` `+o(e(F)(`ds.buttons.addToCart`)),1)]),_:1}),r(u,{variant:`ghost`,class:`!text-cream hover:!bg-cream-wash`},{default:n(()=>[i(o(e(F)(`ds.buttons.learnMore`)),1)]),_:1})])]),_:1})])]),a(`section`,null,[a(`h2`,C,o(e(F)(`ds.heading.sizes`)),1),r(f,{tone:`paper`},{default:n(()=>[a(`div`,w,[r(u,{size:`sm`},{default:n(()=>[i(o(e(F)(`ds.buttons.small`)),1)]),_:1}),r(u,{size:`md`},{default:n(()=>[i(o(e(F)(`ds.buttons.medium`)),1)]),_:1}),r(u,{size:`lg`},{default:n(()=>[i(o(e(F)(`ds.buttons.large`)),1)]),_:1})])]),_:1})]),a(`section`,null,[a(`h2`,T,o(e(F)(`ds.heading.withIcons`)),1),r(f,{tone:`paper`},{default:n(()=>[a(`div`,E,[r(u,{variant:`primary`},{before:n(()=>[r(c,{name:`cart`,size:18})]),default:n(()=>[i(` `+o(e(F)(`ds.buttons.addToCart`)),1)]),_:1}),r(u,{variant:`secondary`},{after:n(()=>[r(c,{name:`arrow-right`,size:18})]),default:n(()=>[i(o(e(F)(`ds.buttons.learnMore`))+` `,1)]),_:1}),r(u,{variant:`ghost`},{before:n(()=>[r(c,{name:`heart`,size:18})]),default:n(()=>[i(` `+o(e(F)(`ds.buttons.save`)),1)]),_:1}),r(u,{variant:`accent`,size:`sm`},{before:n(()=>[r(c,{name:`check`,size:16})]),default:n(()=>[i(` `+o(e(F)(`ds.buttons.confirm`)),1)]),_:1})])]),_:1})]),a(`section`,null,[a(`h2`,D,o(e(F)(`ds.heading.states`)),1),r(f,{tone:`paper`},{default:n(()=>[a(`div`,O,[r(u,null,{default:n(()=>[i(o(e(F)(`ds.heading.default`)),1)]),_:1}),r(u,{disabled:``},{default:n(()=>[i(o(e(F)(`ds.buttons.disabled`)),1)]),_:1}),r(u,{loading:``},{default:n(()=>[i(o(e(F)(`ds.buttons.loading`)),1)]),_:1})])]),_:1})]),a(`section`,null,[a(`h2`,k,o(e(F)(`ds.heading.block`)),1),r(f,{tone:`paper`},{default:n(()=>[a(`div`,A,[r(u,{block:``,variant:`primary`},{before:n(()=>[r(c,{name:`cart`,size:18})]),default:n(()=>[i(` `+o(e(F)(`ds.buttons.addToCart`)),1)]),_:1}),r(u,{block:``,variant:`secondary`},{default:n(()=>[i(o(e(F)(`ds.buttons.continueShopping`)),1)]),_:1})])]),_:1})]),a(`section`,null,[a(`h2`,j,o(e(F)(`ds.heading.usage`)),1),a(`div`,M,[a(`pre`,N,`<Button variant="primary" size="md"> import{L as e,c as t,k as n,m as r,p as i,s as a,tt as o,x as s}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as c}from"./Icon-BaDkTwYL.js";import{t as l}from"./i18n-DUHlbRTU.js";import{t as u}from"./Button-De2-980w.js";import{t as d}from"./SectionShell-DTyMkRQY.js";import{t as f}from"./Card-ailigDjs.js";var p={class:`eyebrow mb-5`},m={class:`flex flex-wrap gap-3`},h={class:`eyebrow mb-5`},g={class:`grid md:grid-cols-3 gap-4`},_={class:`eyebrow mb-4`},v={class:`flex flex-wrap gap-3`},y={class:`eyebrow mb-4`},b={class:`flex flex-wrap gap-3`},x={class:`eyebrow mb-4 !text-cream opacity-80`},S={class:`flex flex-wrap gap-3`},C={class:`eyebrow mb-5`},w={class:`flex flex-wrap items-center gap-3`},T={class:`eyebrow mb-5`},E={class:`flex flex-wrap items-center gap-3`},D={class:`eyebrow mb-5`},O={class:`flex flex-wrap gap-3 items-center`},k={class:`eyebrow mb-5`},A={class:`max-w-md space-y-3`},j={class:`eyebrow mb-5`},M={class:`rounded-md border border-line bg-paper p-6 font-mono text-[12px] text-ink`},N={class:`whitespace-pre-wrap`},P={__name:`ButtonsSection`,setup(P){let{t:F}=l();return(l,P)=>(s(),t(d,{eyebrow:e(F)(`ds.eyebrow.components`),title:e(F)(`ds.buttons.title`),description:e(F)(`ds.buttons.description`)},{default:n(()=>[a(`section`,null,[a(`h2`,p,o(e(F)(`ds.heading.variants`)),1),r(f,{tone:`paper`},{default:n(()=>[a(`div`,m,[r(u,{variant:`primary`},{default:n(()=>[i(o(e(F)(`ds.buttons.primary`)),1)]),_:1}),r(u,{variant:`accent`},{default:n(()=>[i(o(e(F)(`ds.buttons.accent`)),1)]),_:1}),r(u,{variant:`secondary`},{default:n(()=>[i(o(e(F)(`ds.buttons.secondary`)),1)]),_:1}),r(u,{variant:`ghost`},{default:n(()=>[i(o(e(F)(`ds.buttons.ghost`)),1)]),_:1}),r(u,{variant:`danger`},{default:n(()=>[i(o(e(F)(`ds.buttons.danger`)),1)]),_:1})])]),_:1})]),a(`section`,null,[a(`h2`,h,o(e(F)(`ds.heading.onDifferentSurfaces`)),1),a(`div`,g,[r(f,{tone:`paper`},{default:n(()=>[a(`p`,_,o(e(F)(`ds.cards.paper`)),1),a(`div`,v,[r(u,{variant:`primary`},{before:n(()=>[r(c,{name:`cart`,size:18})]),default:n(()=>[i(` `+o(e(F)(`ds.buttons.addToCart`)),1)]),_:1}),r(u,{variant:`ghost`},{default:n(()=>[i(o(e(F)(`ds.buttons.learnMore`)),1)]),_:1})])]),_:1}),r(f,{tone:`cream`},{default:n(()=>[a(`p`,y,o(e(F)(`ds.cards.cream`)),1),a(`div`,b,[r(u,{variant:`primary`},{before:n(()=>[r(c,{name:`cart`,size:18})]),default:n(()=>[i(` `+o(e(F)(`ds.buttons.addToCart`)),1)]),_:1}),r(u,{variant:`ghost`},{default:n(()=>[i(o(e(F)(`ds.buttons.learnMore`)),1)]),_:1})])]),_:1}),r(f,{tone:`brand`},{default:n(()=>[a(`p`,x,o(e(F)(`ds.cards.brand`)),1),a(`div`,S,[r(u,{variant:`accent`},{before:n(()=>[r(c,{name:`cart`,size:18})]),default:n(()=>[i(` `+o(e(F)(`ds.buttons.addToCart`)),1)]),_:1}),r(u,{variant:`ghost`,class:`!text-cream hover:!bg-cream-wash`},{default:n(()=>[i(o(e(F)(`ds.buttons.learnMore`)),1)]),_:1})])]),_:1})])]),a(`section`,null,[a(`h2`,C,o(e(F)(`ds.heading.sizes`)),1),r(f,{tone:`paper`},{default:n(()=>[a(`div`,w,[r(u,{size:`sm`},{default:n(()=>[i(o(e(F)(`ds.buttons.small`)),1)]),_:1}),r(u,{size:`md`},{default:n(()=>[i(o(e(F)(`ds.buttons.medium`)),1)]),_:1}),r(u,{size:`lg`},{default:n(()=>[i(o(e(F)(`ds.buttons.large`)),1)]),_:1})])]),_:1})]),a(`section`,null,[a(`h2`,T,o(e(F)(`ds.heading.withIcons`)),1),r(f,{tone:`paper`},{default:n(()=>[a(`div`,E,[r(u,{variant:`primary`},{before:n(()=>[r(c,{name:`cart`,size:18})]),default:n(()=>[i(` `+o(e(F)(`ds.buttons.addToCart`)),1)]),_:1}),r(u,{variant:`secondary`},{after:n(()=>[r(c,{name:`arrow-right`,size:18})]),default:n(()=>[i(o(e(F)(`ds.buttons.learnMore`))+` `,1)]),_:1}),r(u,{variant:`ghost`},{before:n(()=>[r(c,{name:`heart`,size:18})]),default:n(()=>[i(` `+o(e(F)(`ds.buttons.save`)),1)]),_:1}),r(u,{variant:`accent`,size:`sm`},{before:n(()=>[r(c,{name:`check`,size:16})]),default:n(()=>[i(` `+o(e(F)(`ds.buttons.confirm`)),1)]),_:1})])]),_:1})]),a(`section`,null,[a(`h2`,D,o(e(F)(`ds.heading.states`)),1),r(f,{tone:`paper`},{default:n(()=>[a(`div`,O,[r(u,null,{default:n(()=>[i(o(e(F)(`ds.heading.default`)),1)]),_:1}),r(u,{disabled:``},{default:n(()=>[i(o(e(F)(`ds.buttons.disabled`)),1)]),_:1}),r(u,{loading:``},{default:n(()=>[i(o(e(F)(`ds.buttons.loading`)),1)]),_:1})])]),_:1})]),a(`section`,null,[a(`h2`,k,o(e(F)(`ds.heading.block`)),1),r(f,{tone:`paper`},{default:n(()=>[a(`div`,A,[r(u,{block:``,variant:`primary`},{before:n(()=>[r(c,{name:`cart`,size:18})]),default:n(()=>[i(` `+o(e(F)(`ds.buttons.addToCart`)),1)]),_:1}),r(u,{block:``,variant:`secondary`},{default:n(()=>[i(o(e(F)(`ds.buttons.continueShopping`)),1)]),_:1})])]),_:1})]),a(`section`,null,[a(`h2`,j,o(e(F)(`ds.heading.usage`)),1),a(`div`,M,[a(`pre`,N,`<Button variant="primary" size="md">
<template #before><Icon name="cart" :size="18" /></template> <template #before><Icon name="cart" :size="18" /></template>
`+o(e(F)(`ds.buttons.addToCart`))+` `+o(e(F)(`ds.buttons.addToCart`))+`
</Button>`,1)])])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{P as default}; </Button>`,1)])])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{P as default};

View File

@@ -1 +1 @@
import{L as e,c as t,k as n,m as r,p as i,s as a,tt as o,x as s}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as c}from"./i18n-B16vzzU0.js";import{t as l}from"./Badge-CHfkTK9K.js";import{t as u}from"./SectionShell-kXxofFIK.js";import{t as d}from"./Card-DRIe4MlQ.js";var f={class:`eyebrow mb-5`},p={class:`grid md:grid-cols-3 gap-6`},m={class:`font-display text-2xl font-normal mb-2`},h={class:`text-[14px] text-muted leading-relaxed`},g={class:`font-display text-2xl font-normal mb-2`},_={class:`text-[14px] text-muted leading-relaxed`},v={class:`font-display text-2xl font-normal mb-2`},y={class:`text-[14px] opacity-80 leading-relaxed`},b={class:`eyebrow mb-5`},x={class:`grid md:grid-cols-2 gap-6`},S={class:`font-display text-2xl font-normal mb-2`},C={class:`text-[14px] text-muted leading-relaxed`},w={class:`font-display text-2xl font-normal mb-2`},T={class:`text-[14px] text-muted leading-relaxed`},E={class:`eyebrow mb-5`},D={class:`p-7`},O={class:`font-display text-2xl font-normal mb-2`},k={class:`text-[14px] text-muted leading-relaxed`},A={__name:`CardsSection`,setup(A){let{t:j}=c();return(c,A)=>(s(),t(u,{eyebrow:e(j)(`ds.eyebrow.components`),title:e(j)(`ds.cards.title`),description:e(j)(`ds.cards.description`)},{default:n(()=>[a(`section`,null,[a(`h2`,f,o(e(j)(`ds.heading.tones`)),1),a(`div`,p,[r(d,{tone:`paper`},{default:n(()=>[r(l,{variant:`subtle`,class:`mb-4`},{default:n(()=>[i(o(e(j)(`ds.cards.paper`)),1)]),_:1}),a(`h3`,m,o(e(j)(`ds.cards.paperTitle`)),1),a(`p`,h,o(e(j)(`ds.cards.paperBody`)),1)]),_:1}),r(d,{tone:`cream`},{default:n(()=>[r(l,{variant:`subtle`,class:`mb-4`},{default:n(()=>[i(o(e(j)(`ds.cards.cream`)),1)]),_:1}),a(`h3`,g,o(e(j)(`ds.cards.creamTitle`)),1),a(`p`,_,o(e(j)(`ds.cards.creamBody`)),1)]),_:1}),r(d,{tone:`brand`},{default:n(()=>[r(l,{variant:`accent`,class:`mb-4`},{default:n(()=>[i(o(e(j)(`ds.cards.brand`)),1)]),_:1}),a(`h3`,v,o(e(j)(`ds.cards.brandTitle`)),1),a(`p`,y,o(e(j)(`ds.cards.brandBody`)),1)]),_:1})])]),a(`section`,null,[a(`h2`,b,o(e(j)(`ds.heading.interactive`)),1),a(`div`,x,[r(d,{tone:`paper`,interactive:``},{default:n(()=>[a(`h3`,S,o(e(j)(`ds.cards.hoverMe`)),1),a(`p`,C,o(e(j)(`ds.cards.hoverBody`)),1)]),_:1}),r(d,{tone:`cream`,interactive:``},{default:n(()=>[a(`h3`,w,o(e(j)(`ds.cards.hoverMeToo`)),1),a(`p`,T,o(e(j)(`ds.cards.hoverBodyAlt`)),1)]),_:1})])]),a(`section`,null,[a(`h2`,E,o(e(j)(`ds.heading.withoutPadding`)),1),r(d,{tone:`paper`,padded:!1},{default:n(()=>[A[2]||=a(`div`,{class:`h-40 bg-cream rounded-t-md`},null,-1),a(`div`,D,[a(`h3`,O,o(e(j)(`ds.cards.mediaTitle`)),1),a(`p`,k,[i(o(e(j)(`ds.cards.mediaBody`))+` `,1),A[0]||=a(`code`,{class:`font-mono text-[12px]`},`:padded="false"`,-1),A[1]||=i(`. `,-1)])])]),_:1})])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{A as default}; import{L as e,c as t,k as n,m as r,p as i,s as a,tt as o,x as s}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as c}from"./i18n-DUHlbRTU.js";import{t as l}from"./Badge-BPajj5yh.js";import{t as u}from"./SectionShell-DTyMkRQY.js";import{t as d}from"./Card-ailigDjs.js";var f={class:`eyebrow mb-5`},p={class:`grid md:grid-cols-3 gap-6`},m={class:`font-display text-2xl font-normal mb-2`},h={class:`text-[14px] text-muted leading-relaxed`},g={class:`font-display text-2xl font-normal mb-2`},_={class:`text-[14px] text-muted leading-relaxed`},v={class:`font-display text-2xl font-normal mb-2`},y={class:`text-[14px] opacity-80 leading-relaxed`},b={class:`eyebrow mb-5`},x={class:`grid md:grid-cols-2 gap-6`},S={class:`font-display text-2xl font-normal mb-2`},C={class:`text-[14px] text-muted leading-relaxed`},w={class:`font-display text-2xl font-normal mb-2`},T={class:`text-[14px] text-muted leading-relaxed`},E={class:`eyebrow mb-5`},D={class:`p-7`},O={class:`font-display text-2xl font-normal mb-2`},k={class:`text-[14px] text-muted leading-relaxed`},A={__name:`CardsSection`,setup(A){let{t:j}=c();return(c,A)=>(s(),t(u,{eyebrow:e(j)(`ds.eyebrow.components`),title:e(j)(`ds.cards.title`),description:e(j)(`ds.cards.description`)},{default:n(()=>[a(`section`,null,[a(`h2`,f,o(e(j)(`ds.heading.tones`)),1),a(`div`,p,[r(d,{tone:`paper`},{default:n(()=>[r(l,{variant:`subtle`,class:`mb-4`},{default:n(()=>[i(o(e(j)(`ds.cards.paper`)),1)]),_:1}),a(`h3`,m,o(e(j)(`ds.cards.paperTitle`)),1),a(`p`,h,o(e(j)(`ds.cards.paperBody`)),1)]),_:1}),r(d,{tone:`cream`},{default:n(()=>[r(l,{variant:`subtle`,class:`mb-4`},{default:n(()=>[i(o(e(j)(`ds.cards.cream`)),1)]),_:1}),a(`h3`,g,o(e(j)(`ds.cards.creamTitle`)),1),a(`p`,_,o(e(j)(`ds.cards.creamBody`)),1)]),_:1}),r(d,{tone:`brand`},{default:n(()=>[r(l,{variant:`accent`,class:`mb-4`},{default:n(()=>[i(o(e(j)(`ds.cards.brand`)),1)]),_:1}),a(`h3`,v,o(e(j)(`ds.cards.brandTitle`)),1),a(`p`,y,o(e(j)(`ds.cards.brandBody`)),1)]),_:1})])]),a(`section`,null,[a(`h2`,b,o(e(j)(`ds.heading.interactive`)),1),a(`div`,x,[r(d,{tone:`paper`,interactive:``},{default:n(()=>[a(`h3`,S,o(e(j)(`ds.cards.hoverMe`)),1),a(`p`,C,o(e(j)(`ds.cards.hoverBody`)),1)]),_:1}),r(d,{tone:`cream`,interactive:``},{default:n(()=>[a(`h3`,w,o(e(j)(`ds.cards.hoverMeToo`)),1),a(`p`,T,o(e(j)(`ds.cards.hoverBodyAlt`)),1)]),_:1})])]),a(`section`,null,[a(`h2`,E,o(e(j)(`ds.heading.withoutPadding`)),1),r(d,{tone:`paper`,padded:!1},{default:n(()=>[A[2]||=a(`div`,{class:`h-40 bg-cream rounded-t-md`},null,-1),a(`div`,D,[a(`h3`,O,o(e(j)(`ds.cards.mediaTitle`)),1),a(`p`,k,[i(o(e(j)(`ds.cards.mediaBody`))+` `,1),A[0]||=a(`code`,{class:`font-mono text-[12px]`},`:padded="false"`,-1),A[1]||=i(`. `,-1)])])]),_:1})])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{A as default};

View File

@@ -1 +1 @@
import{C as e,L as t,c as n,et as r,k as i,o as a,r as o,s,tt as c,u as l,x as u}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as d}from"./i18n-B16vzzU0.js";import{t as f}from"./SectionShell-kXxofFIK.js";var p={class:`eyebrow mb-5`},m={class:`grid grid-cols-2 sm:grid-cols-3 gap-4`},h={class:`px-4 py-3`},g={class:`font-mono text-[12px] text-ink block`},_={__name:`ColorsSection`,setup(_){let{t:v}=d(),y=a(()=>[{title:v(`ds.colors.group.brand`),names:[`brand`,`brand-hover`,`brand-soft`]},{title:v(`ds.colors.group.accent`),names:[`accent`,`accent-soft`,`accent-ink`]},{title:v(`ds.colors.group.surface`),names:[`surface`,`paper`,`cream`]},{title:v(`ds.colors.group.ink`),names:[`ink`,`muted`]},{title:v(`ds.colors.group.line`),names:[`line`,`line-strong`]},{title:v(`ds.colors.group.semantic`),names:[`success`,`warning`,`danger`]}]);return(a,d)=>(u(),n(f,{eyebrow:t(v)(`ds.eyebrow.tokens`),title:t(v)(`ds.colors.title`),description:t(v)(`ds.colors.description`)},{default:i(()=>[(u(!0),l(o,null,e(y.value,t=>(u(),l(`section`,{key:t.title},[s(`h2`,p,c(t.title),1),s(`div`,m,[(u(!0),l(o,null,e(t.names,e=>(u(),l(`div`,{key:e,class:`rounded-md border border-line overflow-hidden bg-paper`},[s(`div`,{class:`h-28 border-b border-line`,style:r({background:`var(--color-${e})`})},null,4),s(`div`,h,[s(`code`,g,`--color-`+c(e),1)])]))),128))])]))),128))]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{_ as default}; import{C as e,L as t,c as n,et as r,k as i,o as a,r as o,s,tt as c,u as l,x as u}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as d}from"./i18n-DUHlbRTU.js";import{t as f}from"./SectionShell-DTyMkRQY.js";var p={class:`eyebrow mb-5`},m={class:`grid grid-cols-2 sm:grid-cols-3 gap-4`},h={class:`px-4 py-3`},g={class:`font-mono text-[12px] text-ink block`},_={__name:`ColorsSection`,setup(_){let{t:v}=d(),y=a(()=>[{title:v(`ds.colors.group.brand`),names:[`brand`,`brand-hover`,`brand-soft`]},{title:v(`ds.colors.group.accent`),names:[`accent`,`accent-soft`,`accent-ink`]},{title:v(`ds.colors.group.surface`),names:[`surface`,`paper`,`cream`]},{title:v(`ds.colors.group.ink`),names:[`ink`,`muted`]},{title:v(`ds.colors.group.line`),names:[`line`,`line-strong`]},{title:v(`ds.colors.group.semantic`),names:[`success`,`warning`,`danger`]}]);return(a,d)=>(u(),n(f,{eyebrow:t(v)(`ds.eyebrow.tokens`),title:t(v)(`ds.colors.title`),description:t(v)(`ds.colors.description`)},{default:i(()=>[(u(!0),l(o,null,e(y.value,t=>(u(),l(`section`,{key:t.title},[s(`h2`,p,c(t.title),1),s(`div`,m,[(u(!0),l(o,null,e(t.names,e=>(u(),l(`div`,{key:e,class:`rounded-md border border-line overflow-hidden bg-paper`},[s(`div`,{class:`h-28 border-b border-line`,style:r({background:`var(--color-${e})`})},null,4),s(`div`,h,[s(`code`,g,`--color-`+c(e),1)])]))),128))])]))),128))]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{_ as default};

View File

@@ -1 +0,0 @@
.ds-sheet-enter-active[data-v-564ce72b],.ds-sheet-leave-active[data-v-564ce72b]{transition:transform .26s}.ds-sheet-enter-from[data-v-564ce72b],.ds-sheet-leave-to[data-v-564ce72b]{transform:translateY(100%)}.ds-backdrop-enter-active[data-v-564ce72b],.ds-backdrop-leave-active[data-v-564ce72b]{transition:opacity .2s}.ds-backdrop-enter-from[data-v-564ce72b],.ds-backdrop-leave-to[data-v-564ce72b]{opacity:0}

1
dist/assets/DesignLayout-COYFKSYJ.css vendored Normal file
View File

@@ -0,0 +1 @@
.ds-sheet-enter-active[data-v-8f281349],.ds-sheet-leave-active[data-v-8f281349]{transition:transform .26s}.ds-sheet-enter-from[data-v-8f281349],.ds-sheet-leave-to[data-v-8f281349]{transform:translateY(100%)}.ds-backdrop-enter-active[data-v-8f281349],.ds-backdrop-leave-active[data-v-8f281349]{transition:opacity .2s}.ds-backdrop-enter-from[data-v-8f281349],.ds-backdrop-leave-to[data-v-8f281349]{opacity:0}

1
dist/assets/DesignLayout-Dj2IRTg_.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
import{$ as e,L as t,k as n,m as r,o as i,p as a,s as o,tt as s,u as c,x as l}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{a as u}from"./vue-router-BrNWkU63.js";import{t as d}from"./i18n-B16vzzU0.js";import{t as f}from"./Navbar-DZE5noW0.js";import{t as p}from"./Hero-DXU6A0Jy.js";var m={class:`italic font-light text-brand-soft`},h=`/products/cutouts/kaiser-natron-pulver-250-g-gro%C3%9Fpackung-removebg-preview.png`,g={__name:`HeroPreview`,setup(g){let _=u(),{t:v}=d(),y=i(()=>[`split`,`centered`].includes(_.query.variant)?_.query.variant:`split`),b=i(()=>[`cream`,`paper`,`brand`].includes(_.query.tone)?_.query.tone:`cream`),x=i(()=>b.value===`brand`?`brand`:b.value);return(i,u)=>(l(),c(`div`,{class:e([`min-h-screen`,b.value===`brand`?`bg-brand`:`bg-surface`])},[r(f,{variant:x.value,"cart-count":0},null,8,[`variant`]),r(p,{variant:y.value,tone:b.value,eyebrow:t(v)(`ds.hero.eyebrow`),subheadline:t(v)(`ds.hero.sub`),image:h,"image-alt":`Kaiser-Natron Pulver 250 g Großpackung`,badge:t(v)(`ds.badges.featured`),"cta-label":t(v)(`ds.buttons.addToCart`),"secondary-label":t(v)(`ds.buttons.learnMore`)},{headline:n(()=>[a(s(t(v)(`ds.hero.headline.a`))+` `,1),o(`em`,m,s(t(v)(`ds.hero.headline.em`)),1),a(` `+s(t(v)(`ds.hero.headline.b`)),1)]),_:1},8,[`variant`,`tone`,`eyebrow`,`subheadline`,`badge`,`cta-label`,`secondary-label`])],2))}};export{g as default}; import{$ as e,L as t,k as n,m as r,o as i,p as a,s as o,tt as s,u as c,x as l}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{a as u}from"./vue-router-BrNWkU63.js";import{t as d}from"./i18n-DUHlbRTU.js";import{t as f}from"./Navbar-ie3UVUXi.js";import{t as p}from"./Hero-CLAu1aAp.js";var m={class:`italic font-light text-brand-soft`},h=`/products/cutouts/kaiser-natron-pulver-250-g-gro%C3%9Fpackung-removebg-preview.png`,g={__name:`HeroPreview`,setup(g){let _=u(),{t:v}=d(),y=i(()=>[`split`,`centered`].includes(_.query.variant)?_.query.variant:`split`),b=i(()=>[`cream`,`paper`,`brand`].includes(_.query.tone)?_.query.tone:`cream`),x=i(()=>b.value===`brand`?`brand`:b.value);return(i,u)=>(l(),c(`div`,{class:e([`min-h-screen`,b.value===`brand`?`bg-brand`:`bg-surface`])},[r(f,{variant:x.value,"cart-count":0},null,8,[`variant`]),r(p,{variant:y.value,tone:b.value,eyebrow:t(v)(`ds.hero.eyebrow`),subheadline:t(v)(`ds.hero.sub`),image:h,"image-alt":`Kaiser-Natron Pulver 250 g Großpackung`,badge:t(v)(`ds.badges.featured`),"cta-label":t(v)(`ds.buttons.addToCart`),"secondary-label":t(v)(`ds.buttons.learnMore`)},{headline:n(()=>[a(s(t(v)(`ds.hero.headline.a`))+` `,1),o(`em`,m,s(t(v)(`ds.hero.headline.em`)),1),a(` `+s(t(v)(`ds.hero.headline.b`)),1)]),_:1},8,[`variant`,`tone`,`eyebrow`,`subheadline`,`badge`,`cta-label`,`secondary-label`])],2))}};export{g as default};

View File

@@ -1,4 +1,4 @@
import{$ as e,C as t,L as n,P as r,c as i,et as a,k as o,m as s,o as c,p as l,r as u,s as d,tt as f,u as p,x as m}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as h}from"./i18n-B16vzzU0.js";import{t as g}from"./SectionShell-kXxofFIK.js";import{t as _}from"./DevicePreview-CdOTgn6S.js";var v=[`aria-label`],y=[`aria-selected`,`onClick`],b=[`aria-label`],x=[`aria-selected`,`onClick`],S={class:`eyebrow mb-5`},C={__name:`HeroSection`,setup(C){let{t:w}=h(),T=c(()=>[{id:`split`,label:w(`ds.hero.variant.split`)},{id:`centered`,label:w(`ds.hero.variant.centered`)}]),E=c(()=>[{id:`cream`,label:w(`ds.navbar.tone.cream`),swatch:`var(--color-cream)`},{id:`paper`,label:w(`ds.navbar.tone.paper`),swatch:`#ffffff`},{id:`brand`,label:w(`ds.navbar.tone.brand`),swatch:`var(--color-brand)`}]),D=r(`split`),O=r(`cream`),k=c(()=>`/design/preview/hero?variant=${D.value}&tone=${O.value}`);return(r,c)=>(m(),i(g,{eyebrow:n(w)(`ds.eyebrow.components`),title:n(w)(`ds.hero.title`),description:n(w)(`ds.hero.description`),wide:``},{default:o(()=>[d(`section`,null,[s(_,{src:k.value,initial:`desktop`,height:760},{controls:o(()=>[d(`div`,{role:`tablist`,"aria-label":n(w)(`ds.hero.variant.label`),class:`inline-flex items-center p-1 gap-0.5 rounded-pill border border-line bg-paper`},[(m(!0),p(u,null,t(T.value,t=>(m(),p(`button`,{key:t.id,type:`button`,role:`tab`,"aria-selected":D.value===t.id,class:e([`px-3 py-1.5 text-[12px] font-semibold tracking-label rounded-pill transition-colors duration-base`,D.value===t.id?`bg-brand text-accent`:`text-muted hover:text-brand`]),onClick:e=>D.value=t.id},f(t.label),11,y))),128))],8,v),d(`div`,{role:`tablist`,"aria-label":n(w)(`ds.navbar.tone`),class:`inline-flex items-center p-1 gap-0.5 rounded-pill border border-line bg-paper`},[(m(!0),p(u,null,t(E.value,t=>(m(),p(`button`,{key:t.id,type:`button`,role:`tab`,"aria-selected":O.value===t.id,class:e([`inline-flex items-center gap-2 px-3 py-1.5 text-[12px] font-semibold tracking-label rounded-pill transition-colors duration-base`,O.value===t.id?`bg-brand text-accent`:`text-muted hover:text-brand`]),onClick:e=>O.value=t.id},[d(`span`,{class:`w-2.5 h-2.5 rounded-full border border-line-strong`,style:a({backgroundColor:t.swatch})},null,4),l(` `+f(t.label),1)],10,x))),128))],8,b)]),_:1},8,[`src`])]),d(`section`,null,[d(`h2`,S,f(n(w)(`ds.heading.usage`)),1),c[0]||=d(`div`,{class:`rounded-md border border-line bg-paper p-6 font-mono text-[12px] text-ink`},[d(`pre`,{class:`whitespace-pre-wrap`},`<Hero import{$ as e,C as t,L as n,P as r,c as i,et as a,k as o,m as s,o as c,p as l,r as u,s as d,tt as f,u as p,x as m}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as h}from"./i18n-DUHlbRTU.js";import{t as g}from"./SectionShell-DTyMkRQY.js";import{t as _}from"./DevicePreview-BgxBx9-q.js";var v=[`aria-label`],y=[`aria-selected`,`onClick`],b=[`aria-label`],x=[`aria-selected`,`onClick`],S={class:`eyebrow mb-5`},C={__name:`HeroSection`,setup(C){let{t:w}=h(),T=c(()=>[{id:`split`,label:w(`ds.hero.variant.split`)},{id:`centered`,label:w(`ds.hero.variant.centered`)}]),E=c(()=>[{id:`cream`,label:w(`ds.navbar.tone.cream`),swatch:`var(--color-cream)`},{id:`paper`,label:w(`ds.navbar.tone.paper`),swatch:`#ffffff`},{id:`brand`,label:w(`ds.navbar.tone.brand`),swatch:`var(--color-brand)`}]),D=r(`split`),O=r(`cream`),k=c(()=>`/design/preview/hero?variant=${D.value}&tone=${O.value}`);return(r,c)=>(m(),i(g,{eyebrow:n(w)(`ds.eyebrow.components`),title:n(w)(`ds.hero.title`),description:n(w)(`ds.hero.description`),wide:``},{default:o(()=>[d(`section`,null,[s(_,{src:k.value,initial:`desktop`,height:760},{controls:o(()=>[d(`div`,{role:`tablist`,"aria-label":n(w)(`ds.hero.variant.label`),class:`inline-flex items-center p-1 gap-0.5 rounded-pill border border-line bg-paper`},[(m(!0),p(u,null,t(T.value,t=>(m(),p(`button`,{key:t.id,type:`button`,role:`tab`,"aria-selected":D.value===t.id,class:e([`px-3 py-1.5 text-[12px] font-semibold tracking-label rounded-pill transition-colors duration-base`,D.value===t.id?`bg-brand text-accent`:`text-muted hover:text-brand`]),onClick:e=>D.value=t.id},f(t.label),11,y))),128))],8,v),d(`div`,{role:`tablist`,"aria-label":n(w)(`ds.navbar.tone`),class:`inline-flex items-center p-1 gap-0.5 rounded-pill border border-line bg-paper`},[(m(!0),p(u,null,t(E.value,t=>(m(),p(`button`,{key:t.id,type:`button`,role:`tab`,"aria-selected":O.value===t.id,class:e([`inline-flex items-center gap-2 px-3 py-1.5 text-[12px] font-semibold tracking-label rounded-pill transition-colors duration-base`,O.value===t.id?`bg-brand text-accent`:`text-muted hover:text-brand`]),onClick:e=>O.value=t.id},[d(`span`,{class:`w-2.5 h-2.5 rounded-full border border-line-strong`,style:a({backgroundColor:t.swatch})},null,4),l(` `+f(t.label),1)],10,x))),128))],8,b)]),_:1},8,[`src`])]),d(`section`,null,[d(`h2`,S,f(n(w)(`ds.heading.usage`)),1),c[0]||=d(`div`,{class:`rounded-md border border-line bg-paper p-6 font-mono text-[12px] text-ink`},[d(`pre`,{class:`whitespace-pre-wrap`},`<Hero
variant="split" variant="split"
tone="cream" tone="cream"
eyebrow="Neu" eyebrow="Neu"

1
dist/assets/HomePage-BzDuqA9T.js vendored Normal file
View File

@@ -0,0 +1 @@
import{L as e,k as t,m as n,p as r,s as i,tt as a,u as o,x as s}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as c}from"./i18n-DUHlbRTU.js";import{t as l}from"./Navbar-ie3UVUXi.js";import{t as u}from"./Hero-CLAu1aAp.js";import{t as d}from"./products-BJebky8H.js";var f={class:`h-svh flex flex-col bg-brand overflow-hidden`},p={class:`flex-1 flex items-center`},m={class:`italic font-light text-accent-soft`},h=`/products/cutouts/kaiser-natron-pulver-250-g-gro%C3%9Fpackung-removebg-preview.png`,g={__name:`HomePage`,setup(g){let{t:_}=c();return(c,g)=>(s(),o(`div`,f,[n(l,{variant:`brand`,layout:`standard`,"cart-count":0,products:e(d)},null,8,[`products`]),i(`div`,p,[n(u,{class:`w-full`,variant:`split`,tone:`brand`,eyebrow:e(_)(`ds.hero.eyebrow`),subheadline:e(_)(`ds.hero.sub`),image:h,"image-alt":`Kaiser-Natron Pulver 250 g Großpackung`,"cta-label":e(_)(`ds.buttons.addToCart`),"secondary-label":e(_)(`ds.buttons.learnMore`),"cta-href":`/shop`,"secondary-href":`/anwendungen`},{headline:t(()=>[r(a(e(_)(`ds.hero.headline.a`))+` `,1),i(`em`,m,a(e(_)(`ds.hero.headline.em`)),1),r(` `+a(e(_)(`ds.hero.headline.b`)),1)]),_:1},8,[`eyebrow`,`subheadline`,`cta-label`,`secondary-label`])])]))}};export{g as default};

View File

@@ -1 +0,0 @@
import{L as e,k as t,m as n,p as r,s as i,tt as a,u as o,x as s}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as c}from"./i18n-B16vzzU0.js";import{t as l}from"./Navbar-DZE5noW0.js";import{t as u}from"./Hero-DXU6A0Jy.js";var d={class:`h-svh flex flex-col bg-brand overflow-hidden`},f={class:`flex-1 flex items-center`},p={class:`italic font-light text-accent-soft`},m=`/products/cutouts/kaiser-natron-pulver-250-g-gro%C3%9Fpackung-removebg-preview.png`,h={__name:`HomePage`,setup(h){let{t:g}=c();return(c,h)=>(s(),o(`div`,d,[n(l,{variant:`brand`,layout:`standard`,"cart-count":0}),i(`div`,f,[n(u,{class:`w-full`,variant:`split`,tone:`brand`,eyebrow:e(g)(`ds.hero.eyebrow`),subheadline:e(g)(`ds.hero.sub`),image:m,"image-alt":`Kaiser-Natron Pulver 250 g Großpackung`,"cta-label":e(g)(`ds.buttons.addToCart`),"secondary-label":e(g)(`ds.buttons.learnMore`),"cta-href":`/shop`,"secondary-href":`/anwendungen`},{headline:t(()=>[r(a(e(g)(`ds.hero.headline.a`))+` `,1),i(`em`,p,a(e(g)(`ds.hero.headline.em`)),1),r(` `+a(e(g)(`ds.hero.headline.b`)),1)]),_:1},8,[`eyebrow`,`subheadline`,`cta-label`,`secondary-label`])])]))}};export{h as default};

View File

@@ -1,2 +1,2 @@
import{A as e,C as t,L as n,P as r,c as i,k as a,l as o,m as s,o as c,r as l,s as u,tt as d,u as f,x as p}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{r as m}from"./runtime-dom.esm-bundler-DWTDSpp2.js";import{n as h,t as g}from"./Icon-BaDkTwYL.js";import{t as _}from"./i18n-B16vzzU0.js";import{t as v}from"./SectionShell-kXxofFIK.js";var y={class:`mb-5 flex items-center gap-3 flex-wrap`},b={class:`relative inline-flex items-center`},x={class:`absolute left-3 text-muted pointer-events-none`},S=[`placeholder`],C={class:`text-[12px] text-muted`},w={key:0,class:`text-[14px] text-muted`},T={class:`eyebrow mb-4`},E={class:`grid grid-cols-[repeat(auto-fill,minmax(120px,1fr))] gap-3`},D=[`onClick`],O={class:`w-10 h-10 rounded-sm bg-cream text-brand flex items-center justify-center`},k={class:`font-mono text-[11px] text-muted group-hover:text-brand transition-colors`},A={class:`eyebrow mb-5`},j={class:`grid md:grid-cols-3 gap-4`},M={class:`rounded-md border border-line bg-paper text-brand p-8 flex items-center justify-center gap-6`},N={class:`rounded-md border border-line bg-cream text-brand p-8 flex items-center justify-center gap-6`},P={class:`rounded-md bg-brand text-accent p-8 flex items-center justify-center gap-6`},F={class:`eyebrow mb-5`},I={class:`rounded-md border border-line bg-paper p-8 flex items-end gap-8 text-brand`},L={class:`flex flex-col items-center gap-3`},R={class:`flex flex-col items-center gap-3`},z={class:`font-mono text-[11px] text-muted`},B={class:`flex flex-col items-center gap-3`},V={class:`flex flex-col items-center gap-3`},H={class:`flex flex-col items-center gap-3`},U={class:`eyebrow mb-5`},W={__name:`IconsSection`,setup(W){let{t:G}=_(),K=c(()=>[{title:G(`ds.icons.group.commerce`),names:[`cart`,`bag`,`heart`,`user`,`search`]},{title:G(`ds.icons.group.navigation`),names:[`menu`,`close`,`chevron-left`,`chevron-right`,`chevron-down`,`chevron-up`,`arrow-left`,`arrow-right`]},{title:G(`ds.icons.group.actions`),names:[`plus`,`minus`,`check`]},{title:G(`ds.icons.group.contact`),names:[`mail`,`phone`,`map-pin`,`external-link`]},{title:G(`ds.icons.group.feedback`),names:[`info`,`star`]},{title:G(`ds.icons.group.devices`),names:[`mobile`,`tablet`,`desktop`]}]),q=r(``),J=c(()=>{let e=q.value.trim().toLowerCase();return e?K.value.map(t=>({...t,names:t.names.filter(t=>t.includes(e)||(h[t]?.label||``).toLowerCase().includes(e))})).filter(e=>e.names.length>0):K.value}),Y=r(``);async function X(e){let t=`<Icon name="${e}" />`;try{await navigator.clipboard.writeText(t),Y.value=e,setTimeout(()=>{Y.value===e&&(Y.value=``)},1200)}catch{}}return(r,c)=>(p(),i(v,{eyebrow:n(G)(`ds.eyebrow.components`),title:n(G)(`ds.icons.title`),description:n(G)(`ds.icons.description`)},{default:a(()=>[u(`section`,null,[u(`div`,y,[u(`label`,b,[u(`span`,x,[s(g,{name:`search`,size:16})]),e(u(`input`,{"onUpdate:modelValue":c[0]||=e=>q.value=e,type:`search`,placeholder:n(G)(`ds.icons.search`),class:`pl-9 pr-4 py-2 rounded-pill border border-line bg-paper text-[14px] text-ink placeholder:text-muted focus:outline-none focus:border-brand w-[260px]`},null,8,S),[[m,q.value]])]),u(`span`,C,d(n(G)(`ds.icons.copyHint`)),1)]),J.value.length===0?(p(),f(`div`,w,d(n(G)(`ds.icons.noMatch`))+` "`+d(q.value)+`". `,1)):o(``,!0),(p(!0),f(l,null,t(J.value,e=>(p(),f(`div`,{key:e.title,class:`mb-10 last:mb-0`},[u(`h2`,T,d(e.title),1),u(`div`,E,[(p(!0),f(l,null,t(e.names,e=>(p(),f(`button`,{key:e,type:`button`,class:`group flex flex-col items-center justify-center gap-2 p-4 rounded-md border border-line bg-paper text-ink hover:border-brand-soft hover:-translate-y-0.5 hover:shadow-sm transition-all duration-base ease-out`,onClick:t=>X(e)},[u(`span`,O,[s(g,{name:e,size:22},null,8,[`name`])]),u(`span`,k,d(Y.value===e?n(G)(`ds.icons.copied`):e),1)],8,D))),128))])]))),128))]),u(`section`,null,[u(`h2`,A,d(n(G)(`ds.heading.onDifferentSurfaces`)),1),u(`div`,j,[u(`div`,M,[s(g,{name:`cart`,size:28}),s(g,{name:`heart`,size:28}),s(g,{name:`user`,size:28})]),u(`div`,N,[s(g,{name:`cart`,size:28}),s(g,{name:`heart`,size:28}),s(g,{name:`user`,size:28})]),u(`div`,P,[s(g,{name:`cart`,size:28}),s(g,{name:`heart`,size:28}),s(g,{name:`user`,size:28})])])]),u(`section`,null,[u(`h2`,F,d(n(G)(`ds.heading.sizes`)),1),u(`div`,I,[u(`div`,L,[s(g,{name:`cart`,size:16}),c[1]||=u(`code`,{class:`font-mono text-[11px] text-muted`},`16`,-1)]),u(`div`,R,[s(g,{name:`cart`,size:20}),u(`code`,z,d(n(G)(`ds.icons.sizeDefault`)),1)]),u(`div`,B,[s(g,{name:`cart`,size:24}),c[2]||=u(`code`,{class:`font-mono text-[11px] text-muted`},`24`,-1)]),u(`div`,V,[s(g,{name:`cart`,size:32}),c[3]||=u(`code`,{class:`font-mono text-[11px] text-muted`},`32`,-1)]),u(`div`,H,[s(g,{name:`cart`,size:48}),c[4]||=u(`code`,{class:`font-mono text-[11px] text-muted`},`48`,-1)])])]),u(`section`,null,[u(`h2`,U,d(n(G)(`ds.heading.usage`)),1),c[5]||=u(`div`,{class:`rounded-md border border-line bg-paper p-6 font-mono text-[12px] text-ink`},[u(`pre`,{class:`whitespace-pre-wrap`},`<Icon name="cart" :size="20" /> import{A as e,C as t,L as n,P as r,c as i,k as a,l as o,m as s,o as c,r as l,s as u,tt as d,u as f,x as p}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{r as m}from"./runtime-dom.esm-bundler-CDZvOKZN.js";import{n as h,t as g}from"./Icon-BaDkTwYL.js";import{t as _}from"./i18n-DUHlbRTU.js";import{t as v}from"./SectionShell-DTyMkRQY.js";var y={class:`mb-5 flex items-center gap-3 flex-wrap`},b={class:`relative inline-flex items-center`},x={class:`absolute left-3 text-muted pointer-events-none`},S=[`placeholder`],C={class:`text-[12px] text-muted`},w={key:0,class:`text-[14px] text-muted`},T={class:`eyebrow mb-4`},E={class:`grid grid-cols-[repeat(auto-fill,minmax(120px,1fr))] gap-3`},D=[`onClick`],O={class:`w-10 h-10 rounded-sm bg-cream text-brand flex items-center justify-center`},k={class:`font-mono text-[11px] text-muted group-hover:text-brand transition-colors`},A={class:`eyebrow mb-5`},j={class:`grid md:grid-cols-3 gap-4`},M={class:`rounded-md border border-line bg-paper text-brand p-8 flex items-center justify-center gap-6`},N={class:`rounded-md border border-line bg-cream text-brand p-8 flex items-center justify-center gap-6`},P={class:`rounded-md bg-brand text-accent p-8 flex items-center justify-center gap-6`},F={class:`eyebrow mb-5`},I={class:`rounded-md border border-line bg-paper p-8 flex items-end gap-8 text-brand`},L={class:`flex flex-col items-center gap-3`},R={class:`flex flex-col items-center gap-3`},z={class:`font-mono text-[11px] text-muted`},B={class:`flex flex-col items-center gap-3`},V={class:`flex flex-col items-center gap-3`},H={class:`flex flex-col items-center gap-3`},U={class:`eyebrow mb-5`},W={__name:`IconsSection`,setup(W){let{t:G}=_(),K=c(()=>[{title:G(`ds.icons.group.commerce`),names:[`cart`,`bag`,`heart`,`user`,`search`]},{title:G(`ds.icons.group.navigation`),names:[`menu`,`close`,`chevron-left`,`chevron-right`,`chevron-down`,`chevron-up`,`arrow-left`,`arrow-right`]},{title:G(`ds.icons.group.actions`),names:[`plus`,`minus`,`check`]},{title:G(`ds.icons.group.contact`),names:[`mail`,`phone`,`map-pin`,`external-link`]},{title:G(`ds.icons.group.feedback`),names:[`info`,`star`]},{title:G(`ds.icons.group.devices`),names:[`mobile`,`tablet`,`desktop`]}]),q=r(``),J=c(()=>{let e=q.value.trim().toLowerCase();return e?K.value.map(t=>({...t,names:t.names.filter(t=>t.includes(e)||(h[t]?.label||``).toLowerCase().includes(e))})).filter(e=>e.names.length>0):K.value}),Y=r(``);async function X(e){let t=`<Icon name="${e}" />`;try{await navigator.clipboard.writeText(t),Y.value=e,setTimeout(()=>{Y.value===e&&(Y.value=``)},1200)}catch{}}return(r,c)=>(p(),i(v,{eyebrow:n(G)(`ds.eyebrow.components`),title:n(G)(`ds.icons.title`),description:n(G)(`ds.icons.description`)},{default:a(()=>[u(`section`,null,[u(`div`,y,[u(`label`,b,[u(`span`,x,[s(g,{name:`search`,size:16})]),e(u(`input`,{"onUpdate:modelValue":c[0]||=e=>q.value=e,type:`search`,placeholder:n(G)(`ds.icons.search`),class:`pl-9 pr-4 py-2 rounded-pill border border-line bg-paper text-[14px] text-ink placeholder:text-muted focus:outline-none focus:border-brand w-[260px]`},null,8,S),[[m,q.value]])]),u(`span`,C,d(n(G)(`ds.icons.copyHint`)),1)]),J.value.length===0?(p(),f(`div`,w,d(n(G)(`ds.icons.noMatch`))+` "`+d(q.value)+`". `,1)):o(``,!0),(p(!0),f(l,null,t(J.value,e=>(p(),f(`div`,{key:e.title,class:`mb-10 last:mb-0`},[u(`h2`,T,d(e.title),1),u(`div`,E,[(p(!0),f(l,null,t(e.names,e=>(p(),f(`button`,{key:e,type:`button`,class:`group flex flex-col items-center justify-center gap-2 p-4 rounded-md border border-line bg-paper text-ink hover:border-brand-soft hover:-translate-y-0.5 hover:shadow-sm transition-all duration-base ease-out`,onClick:t=>X(e)},[u(`span`,O,[s(g,{name:e,size:22},null,8,[`name`])]),u(`span`,k,d(Y.value===e?n(G)(`ds.icons.copied`):e),1)],8,D))),128))])]))),128))]),u(`section`,null,[u(`h2`,A,d(n(G)(`ds.heading.onDifferentSurfaces`)),1),u(`div`,j,[u(`div`,M,[s(g,{name:`cart`,size:28}),s(g,{name:`heart`,size:28}),s(g,{name:`user`,size:28})]),u(`div`,N,[s(g,{name:`cart`,size:28}),s(g,{name:`heart`,size:28}),s(g,{name:`user`,size:28})]),u(`div`,P,[s(g,{name:`cart`,size:28}),s(g,{name:`heart`,size:28}),s(g,{name:`user`,size:28})])])]),u(`section`,null,[u(`h2`,F,d(n(G)(`ds.heading.sizes`)),1),u(`div`,I,[u(`div`,L,[s(g,{name:`cart`,size:16}),c[1]||=u(`code`,{class:`font-mono text-[11px] text-muted`},`16`,-1)]),u(`div`,R,[s(g,{name:`cart`,size:20}),u(`code`,z,d(n(G)(`ds.icons.sizeDefault`)),1)]),u(`div`,B,[s(g,{name:`cart`,size:24}),c[2]||=u(`code`,{class:`font-mono text-[11px] text-muted`},`24`,-1)]),u(`div`,V,[s(g,{name:`cart`,size:32}),c[3]||=u(`code`,{class:`font-mono text-[11px] text-muted`},`32`,-1)]),u(`div`,H,[s(g,{name:`cart`,size:48}),c[4]||=u(`code`,{class:`font-mono text-[11px] text-muted`},`48`,-1)])])]),u(`section`,null,[u(`h2`,U,d(n(G)(`ds.heading.usage`)),1),c[5]||=u(`div`,{class:`rounded-md border border-line bg-paper p-6 font-mono text-[12px] text-ink`},[u(`pre`,{class:`whitespace-pre-wrap`},`<Icon name="cart" :size="20" />
<Icon name="arrow-right" :size="16" label="Next slide" />`)],-1)])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{W as default}; <Icon name="arrow-right" :size="16" label="Next slide" />`)],-1)])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{W as default};

View File

@@ -1 +1 @@
import{$ as e,D as t,L as n,P as r,c as i,k as a,l as o,m as s,o as c,p as l,s as u,tt as d,u as f,x as p}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as m}from"./i18n-B16vzzU0.js";import{t as h}from"./SectionShell-kXxofFIK.js";import{t as g}from"./Card-DRIe4MlQ.js";var _={class:`flex flex-col gap-2`},v=[`for`],y={key:0,class:`text-danger`},b=[`id`,`type`,`value`,`placeholder`,`disabled`,`required`,`aria-invalid`,`aria-describedby`],x=[`id`],S=[`id`],C={__name:`Input`,props:{modelValue:{type:[String,Number],default:``},label:{type:String,default:``},hint:{type:String,default:``},error:{type:String,default:``},type:{type:String,default:`text`},placeholder:{type:String,default:``},disabled:{type:Boolean,default:!1},required:{type:Boolean,default:!1}},emits:[`update:modelValue`],setup(n){let r=n,i=t(),a=c(()=>`in-${i}`),s=c(()=>r.hint?`hint-${i}`:void 0),m=c(()=>r.error?`err-${i}`:void 0),h=c(()=>[s.value,m.value].filter(Boolean).join(` `)||void 0);return(t,r)=>(p(),f(`div`,_,[n.label?(p(),f(`label`,{key:0,for:a.value,class:`text-[11px] font-bold uppercase tracking-eyebrow text-muted`},[l(d(n.label),1),n.required?(p(),f(`span`,y,` *`)):o(``,!0)],8,v)):o(``,!0),u(`input`,{id:a.value,type:n.type,value:n.modelValue,placeholder:n.placeholder,disabled:n.disabled,required:n.required,"aria-invalid":!!n.error,"aria-describedby":h.value,class:e([`w-full rounded-sm border bg-paper px-4 py-3 text-[15px] text-ink placeholder:text-ink-placeholder transition-colors duration-base focus:outline-none focus:border-brand disabled:opacity-50 disabled:cursor-not-allowed`,n.error?`border-danger`:`border-line`]),onInput:r[0]||=e=>t.$emit(`update:modelValue`,e.target.value)},null,42,b),n.hint&&!n.error?(p(),f(`p`,{key:1,id:s.value,class:`text-[13px] text-muted`},d(n.hint),9,x)):o(``,!0),n.error?(p(),f(`p`,{key:2,id:m.value,class:`text-[13px] text-danger`},d(n.error),9,S)):o(``,!0)]))}},w={class:`eyebrow mb-5`},T={class:`grid md:grid-cols-2 gap-6 max-w-3xl`},E={class:`eyebrow mb-5`},D={class:`grid md:grid-cols-2 gap-6 max-w-3xl`},O={__name:`InputsSection`,setup(e){let{t}=m(),o=r(``),c=r(``),l=r(``);return(e,r)=>(p(),i(h,{eyebrow:n(t)(`ds.eyebrow.components`),title:n(t)(`ds.inputs.title`),description:n(t)(`ds.inputs.description`)},{default:a(()=>[u(`section`,null,[u(`h2`,w,d(n(t)(`ds.heading.default`)),1),s(g,{tone:`paper`},{default:a(()=>[u(`div`,T,[s(C,{modelValue:o.value,"onUpdate:modelValue":r[0]||=e=>o.value=e,label:n(t)(`ds.inputs.email`),type:`email`,placeholder:`you@example.com`,hint:n(t)(`ds.inputs.emailHint`)},null,8,[`modelValue`,`label`,`hint`]),s(C,{modelValue:c.value,"onUpdate:modelValue":r[1]||=e=>c.value=e,label:n(t)(`ds.inputs.password`),type:`password`,placeholder:`••••••••`},null,8,[`modelValue`,`label`])])]),_:1})]),u(`section`,null,[u(`h2`,E,d(n(t)(`ds.heading.states`)),1),s(g,{tone:`paper`},{default:a(()=>[u(`div`,D,[s(C,{modelValue:l.value,"onUpdate:modelValue":r[2]||=e=>l.value=e,label:n(t)(`ds.inputs.required`),required:``,error:n(t)(`ds.inputs.requiredError`)},null,8,[`modelValue`,`label`,`error`]),s(C,{label:n(t)(`ds.inputs.disabled`),placeholder:n(t)(`ds.inputs.disabledPlaceholder`),disabled:``},null,8,[`label`,`placeholder`])])]),_:1})])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{O as default}; import{$ as e,D as t,L as n,P as r,c as i,k as a,l as o,m as s,o as c,p as l,s as u,tt as d,u as f,x as p}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as m}from"./i18n-DUHlbRTU.js";import{t as h}from"./SectionShell-DTyMkRQY.js";import{t as g}from"./Card-ailigDjs.js";var _={class:`flex flex-col gap-2`},v=[`for`],y={key:0,class:`text-danger`},b=[`id`,`type`,`value`,`placeholder`,`disabled`,`required`,`aria-invalid`,`aria-describedby`],x=[`id`],S=[`id`],C={__name:`Input`,props:{modelValue:{type:[String,Number],default:``},label:{type:String,default:``},hint:{type:String,default:``},error:{type:String,default:``},type:{type:String,default:`text`},placeholder:{type:String,default:``},disabled:{type:Boolean,default:!1},required:{type:Boolean,default:!1}},emits:[`update:modelValue`],setup(n){let r=n,i=t(),a=c(()=>`in-${i}`),s=c(()=>r.hint?`hint-${i}`:void 0),m=c(()=>r.error?`err-${i}`:void 0),h=c(()=>[s.value,m.value].filter(Boolean).join(` `)||void 0);return(t,r)=>(p(),f(`div`,_,[n.label?(p(),f(`label`,{key:0,for:a.value,class:`text-[11px] font-bold uppercase tracking-eyebrow text-muted`},[l(d(n.label),1),n.required?(p(),f(`span`,y,` *`)):o(``,!0)],8,v)):o(``,!0),u(`input`,{id:a.value,type:n.type,value:n.modelValue,placeholder:n.placeholder,disabled:n.disabled,required:n.required,"aria-invalid":!!n.error,"aria-describedby":h.value,class:e([`w-full rounded-sm border bg-paper px-4 py-3 text-[15px] text-ink placeholder:text-ink-placeholder transition-colors duration-base focus:outline-none focus:border-brand disabled:opacity-50 disabled:cursor-not-allowed`,n.error?`border-danger`:`border-line`]),onInput:r[0]||=e=>t.$emit(`update:modelValue`,e.target.value)},null,42,b),n.hint&&!n.error?(p(),f(`p`,{key:1,id:s.value,class:`text-[13px] text-muted`},d(n.hint),9,x)):o(``,!0),n.error?(p(),f(`p`,{key:2,id:m.value,class:`text-[13px] text-danger`},d(n.error),9,S)):o(``,!0)]))}},w={class:`eyebrow mb-5`},T={class:`grid md:grid-cols-2 gap-6 max-w-3xl`},E={class:`eyebrow mb-5`},D={class:`grid md:grid-cols-2 gap-6 max-w-3xl`},O={__name:`InputsSection`,setup(e){let{t}=m(),o=r(``),c=r(``),l=r(``);return(e,r)=>(p(),i(h,{eyebrow:n(t)(`ds.eyebrow.components`),title:n(t)(`ds.inputs.title`),description:n(t)(`ds.inputs.description`)},{default:a(()=>[u(`section`,null,[u(`h2`,w,d(n(t)(`ds.heading.default`)),1),s(g,{tone:`paper`},{default:a(()=>[u(`div`,T,[s(C,{modelValue:o.value,"onUpdate:modelValue":r[0]||=e=>o.value=e,label:n(t)(`ds.inputs.email`),type:`email`,placeholder:`you@example.com`,hint:n(t)(`ds.inputs.emailHint`)},null,8,[`modelValue`,`label`,`hint`]),s(C,{modelValue:c.value,"onUpdate:modelValue":r[1]||=e=>c.value=e,label:n(t)(`ds.inputs.password`),type:`password`,placeholder:`••••••••`},null,8,[`modelValue`,`label`])])]),_:1})]),u(`section`,null,[u(`h2`,E,d(n(t)(`ds.heading.states`)),1),s(g,{tone:`paper`},{default:a(()=>[u(`div`,D,[s(C,{modelValue:l.value,"onUpdate:modelValue":r[2]||=e=>l.value=e,label:n(t)(`ds.inputs.required`),required:``,error:n(t)(`ds.inputs.requiredError`)},null,8,[`modelValue`,`label`,`error`]),s(C,{label:n(t)(`ds.inputs.disabled`),placeholder:n(t)(`ds.inputs.disabledPlaceholder`),disabled:``},null,8,[`label`,`placeholder`])])]),_:1})])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{O as default};

View File

@@ -1 +1 @@
import{$ as e,C as t,L as n,o as r,r as i,tt as a,u as o,x as s}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as c}from"./i18n-B16vzzU0.js";var l=[`aria-label`,`aria-pressed`,`onClick`],u={__name:`LanguageSwitcher`,props:{floating:{type:Boolean,default:!1},tone:{type:String,default:`paper`,validator:e=>[`paper`,`cream`,`brand`].includes(e)}},setup(u){let d=u,{locale:f,setLocale:p,availableLocales:m}=c(),h={paper:{container:`border border-line bg-paper`,active:`bg-brand text-accent`,inactive:`text-muted hover:text-brand`},cream:{container:`border border-line-strong bg-paper`,active:`bg-brand text-accent`,inactive:`text-muted hover:text-brand`},brand:{container:`border border-cream-line bg-cream-wash`,active:`bg-accent text-brand`,inactive:`text-cream hover:text-accent`}},g=r(()=>h[d.tone]);return(r,c)=>(s(),o(`div`,{role:`group`,"aria-label":`Language`,class:e([`inline-flex items-center p-1 gap-0.5 rounded-pill font-sans`,g.value.container,u.floating?`fixed top-6 right-6 z-[60] shadow-sm`:``])},[(s(!0),o(i,null,t(n(m),t=>(s(),o(`button`,{key:t.code,type:`button`,"aria-label":t.name,"aria-pressed":n(f)===t.code,class:e([`px-2.5 py-1 text-[11px] font-bold tracking-eyebrow rounded-pill transition-colors duration-base`,n(f)===t.code?g.value.active:g.value.inactive]),onClick:e=>n(p)(t.code)},a(t.label),11,l))),128))],2))}};export{u as t}; import{$ as e,C as t,L as n,o as r,r as i,tt as a,u as o,x as s}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as c}from"./i18n-DUHlbRTU.js";var l=[`aria-label`,`aria-pressed`,`onClick`],u={__name:`LanguageSwitcher`,props:{floating:{type:Boolean,default:!1},tone:{type:String,default:`paper`,validator:e=>[`paper`,`cream`,`brand`].includes(e)}},setup(u){let d=u,{locale:f,setLocale:p,availableLocales:m}=c(),h={paper:{container:`border border-line bg-paper`,active:`bg-brand text-accent`,inactive:`text-muted hover:text-brand`},cream:{container:`border border-line-strong bg-paper`,active:`bg-brand text-accent`,inactive:`text-muted hover:text-brand`},brand:{container:`border border-cream-line bg-cream-wash`,active:`bg-accent text-brand`,inactive:`text-cream hover:text-accent`}},g=r(()=>h[d.tone]);return(r,c)=>(s(),o(`div`,{role:`group`,"aria-label":`Language`,class:e([`inline-flex items-center p-1 gap-0.5 rounded-pill font-sans`,g.value.container,u.floating?`fixed top-6 right-6 z-[60] shadow-sm`:``])},[(s(!0),o(i,null,t(n(m),t=>(s(),o(`button`,{key:t.code,type:`button`,"aria-label":t.name,"aria-pressed":n(f)===t.code,class:e([`px-2.5 py-1 text-[11px] font-bold tracking-eyebrow rounded-pill transition-colors duration-base`,n(f)===t.code?g.value.active:g.value.inactive]),onClick:e=>n(p)(t.code)},a(t.label),11,l))),128))],2))}};export{u as t};

View File

@@ -1,3 +1,3 @@
import{L as e,c as t,k as n,m as r,s as i,tt as a,x as o}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as s}from"./i18n-B16vzzU0.js";import{t as c}from"./LanguageSwitcher-tvBvRm5H.js";import{t as l}from"./SectionShell-kXxofFIK.js";import{t as u}from"./Card-DRIe4MlQ.js";var d={class:`eyebrow mb-5`},f={class:`grid md:grid-cols-3 gap-4`},p={class:`eyebrow mb-4`},m={class:`text-[13px] text-muted mt-4 leading-relaxed`},h={class:`eyebrow mb-4`},g={class:`text-[13px] text-muted mt-4 leading-relaxed`},_={class:`eyebrow mb-4`},v={class:`text-[13px] opacity-80 mt-4 leading-relaxed`},y={class:`eyebrow mb-5`},b={class:`text-[13px] text-muted max-w-2xl leading-relaxed`},x={class:`eyebrow mb-5`},S={__name:`LanguageSwitcherSection`,setup(S){let{t:C}=s();return(s,S)=>(o(),t(l,{eyebrow:e(C)(`ds.eyebrow.components`),title:e(C)(`ds.language.title`),description:e(C)(`ds.language.description`)},{default:n(()=>[i(`section`,null,[i(`h2`,d,a(e(C)(`ds.heading.onDifferentSurfaces`)),1),i(`div`,f,[r(u,{tone:`paper`},{default:n(()=>[i(`p`,p,a(e(C)(`ds.cards.paper`)),1),r(c,{tone:`paper`}),i(`p`,m,a(e(C)(`ds.language.paperNote`)),1)]),_:1}),r(u,{tone:`cream`},{default:n(()=>[i(`p`,h,a(e(C)(`ds.cards.cream`)),1),r(c,{tone:`cream`}),i(`p`,g,a(e(C)(`ds.language.creamNote`)),1)]),_:1}),r(u,{tone:`brand`},{default:n(()=>[i(`p`,_,a(e(C)(`ds.cards.brand`)),1),r(c,{tone:`brand`}),i(`p`,v,a(e(C)(`ds.language.brandNote`)),1)]),_:1})])]),i(`section`,null,[i(`h2`,y,a(e(C)(`ds.language.floating`)),1),r(u,{tone:`paper`},{default:n(()=>[i(`p`,b,a(e(C)(`ds.language.floatingNote`)),1)]),_:1})]),i(`section`,null,[i(`h2`,x,a(e(C)(`ds.heading.usage`)),1),S[0]||=i(`div`,{class:`rounded-md border border-line bg-paper p-6 font-mono text-[12px] text-ink`},[i(`pre`,{class:`whitespace-pre-wrap`},`<LanguageSwitcher /> import{L as e,c as t,k as n,m as r,s as i,tt as a,x as o}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as s}from"./i18n-DUHlbRTU.js";import{t as c}from"./LanguageSwitcher-07dH-xIM.js";import{t as l}from"./SectionShell-DTyMkRQY.js";import{t as u}from"./Card-ailigDjs.js";var d={class:`eyebrow mb-5`},f={class:`grid md:grid-cols-3 gap-4`},p={class:`eyebrow mb-4`},m={class:`text-[13px] text-muted mt-4 leading-relaxed`},h={class:`eyebrow mb-4`},g={class:`text-[13px] text-muted mt-4 leading-relaxed`},_={class:`eyebrow mb-4`},v={class:`text-[13px] opacity-80 mt-4 leading-relaxed`},y={class:`eyebrow mb-5`},b={class:`text-[13px] text-muted max-w-2xl leading-relaxed`},x={class:`eyebrow mb-5`},S={__name:`LanguageSwitcherSection`,setup(S){let{t:C}=s();return(s,S)=>(o(),t(l,{eyebrow:e(C)(`ds.eyebrow.components`),title:e(C)(`ds.language.title`),description:e(C)(`ds.language.description`)},{default:n(()=>[i(`section`,null,[i(`h2`,d,a(e(C)(`ds.heading.onDifferentSurfaces`)),1),i(`div`,f,[r(u,{tone:`paper`},{default:n(()=>[i(`p`,p,a(e(C)(`ds.cards.paper`)),1),r(c,{tone:`paper`}),i(`p`,m,a(e(C)(`ds.language.paperNote`)),1)]),_:1}),r(u,{tone:`cream`},{default:n(()=>[i(`p`,h,a(e(C)(`ds.cards.cream`)),1),r(c,{tone:`cream`}),i(`p`,g,a(e(C)(`ds.language.creamNote`)),1)]),_:1}),r(u,{tone:`brand`},{default:n(()=>[i(`p`,_,a(e(C)(`ds.cards.brand`)),1),r(c,{tone:`brand`}),i(`p`,v,a(e(C)(`ds.language.brandNote`)),1)]),_:1})])]),i(`section`,null,[i(`h2`,y,a(e(C)(`ds.language.floating`)),1),r(u,{tone:`paper`},{default:n(()=>[i(`p`,b,a(e(C)(`ds.language.floatingNote`)),1)]),_:1})]),i(`section`,null,[i(`h2`,x,a(e(C)(`ds.heading.usage`)),1),S[0]||=i(`div`,{class:`rounded-md border border-line bg-paper p-6 font-mono text-[12px] text-ink`},[i(`pre`,{class:`whitespace-pre-wrap`},`<LanguageSwitcher />
<LanguageSwitcher tone="brand" /> <LanguageSwitcher tone="brand" />
<LanguageSwitcher floating />`)],-1)])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{S as default}; <LanguageSwitcher floating />`)],-1)])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{S as default};

View File

@@ -1 +1 @@
import{L as e,c as t,k as n,m as r,s as i,tt as a,x as o}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as s}from"./Logo-Iem0n9fv.js";import{t as c}from"./i18n-B16vzzU0.js";import{t as l}from"./SectionShell-kXxofFIK.js";var u={class:`eyebrow mb-5`},d={class:`grid md:grid-cols-3 gap-4`},f={class:`rounded-md border border-line bg-paper p-10 flex flex-col items-center gap-4`},p={class:`font-mono text-[11px] text-muted`},m={class:`rounded-md border border-line bg-cream p-10 flex flex-col items-center gap-4`},h={class:`font-mono text-[11px] text-muted`},g={class:`rounded-md bg-brand p-10 flex flex-col items-center gap-4`},_={class:`font-mono text-[11px] text-cream opacity-70`},v={class:`eyebrow mb-5`},y={class:`rounded-md border border-line bg-paper p-10 flex items-end gap-10 flex-wrap`},b={class:`flex flex-col items-center gap-3`},x={class:`flex flex-col items-center gap-3`},S={class:`flex flex-col items-center gap-3`},C={class:`eyebrow mb-5`},w={class:`rounded-md border border-line bg-paper p-6 font-mono text-[12px] text-ink`},T={class:`mb-2 text-muted`},E={__name:`LogoSection`,setup(E){let{t:D}=c();return(c,E)=>(o(),t(l,{eyebrow:e(D)(`ds.eyebrow.brand`),title:e(D)(`ds.logo.title`),description:e(D)(`ds.logo.description`)},{default:n(()=>[i(`section`,null,[i(`h2`,u,a(e(D)(`ds.heading.onDifferentSurfaces`)),1),i(`div`,d,[i(`div`,f,[r(s,{class:`w-40 h-auto text-brand`}),i(`code`,p,a(e(D)(`ds.cards.paper`)),1)]),i(`div`,m,[r(s,{class:`w-40 h-auto text-brand`}),i(`code`,h,a(e(D)(`ds.cards.cream`)),1)]),i(`div`,g,[r(s,{class:`w-40 h-auto text-paper`}),i(`code`,_,a(e(D)(`ds.cards.brand`)),1)])])]),i(`section`,null,[i(`h2`,v,a(e(D)(`ds.heading.sizes`)),1),i(`div`,y,[i(`div`,b,[r(s,{class:`w-16 h-auto text-brand`}),E[0]||=i(`code`,{class:`font-mono text-[11px] text-muted`},`w-16`,-1)]),i(`div`,x,[r(s,{class:`w-32 h-auto text-brand`}),E[1]||=i(`code`,{class:`font-mono text-[11px] text-muted`},`w-32`,-1)]),i(`div`,S,[r(s,{class:`w-60 h-auto text-brand`}),E[2]||=i(`code`,{class:`font-mono text-[11px] text-muted`},`w-60`,-1)])])]),i(`section`,null,[i(`h2`,C,a(e(D)(`ds.heading.usage`)),1),i(`div`,w,[i(`p`,T,a(e(D)(`ds.logo.usageIntro`)),1),E[3]||=i(`pre`,{class:`whitespace-pre-wrap`},`<Logo class="w-60 h-auto text-brand" />`,-1)])])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{E as default}; import{L as e,c as t,k as n,m as r,s as i,tt as a,x as o}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as s}from"./Logo-Iem0n9fv.js";import{t as c}from"./i18n-DUHlbRTU.js";import{t as l}from"./SectionShell-DTyMkRQY.js";var u={class:`eyebrow mb-5`},d={class:`grid md:grid-cols-3 gap-4`},f={class:`rounded-md border border-line bg-paper p-10 flex flex-col items-center gap-4`},p={class:`font-mono text-[11px] text-muted`},m={class:`rounded-md border border-line bg-cream p-10 flex flex-col items-center gap-4`},h={class:`font-mono text-[11px] text-muted`},g={class:`rounded-md bg-brand p-10 flex flex-col items-center gap-4`},_={class:`font-mono text-[11px] text-cream opacity-70`},v={class:`eyebrow mb-5`},y={class:`rounded-md border border-line bg-paper p-10 flex items-end gap-10 flex-wrap`},b={class:`flex flex-col items-center gap-3`},x={class:`flex flex-col items-center gap-3`},S={class:`flex flex-col items-center gap-3`},C={class:`eyebrow mb-5`},w={class:`rounded-md border border-line bg-paper p-6 font-mono text-[12px] text-ink`},T={class:`mb-2 text-muted`},E={__name:`LogoSection`,setup(E){let{t:D}=c();return(c,E)=>(o(),t(l,{eyebrow:e(D)(`ds.eyebrow.brand`),title:e(D)(`ds.logo.title`),description:e(D)(`ds.logo.description`)},{default:n(()=>[i(`section`,null,[i(`h2`,u,a(e(D)(`ds.heading.onDifferentSurfaces`)),1),i(`div`,d,[i(`div`,f,[r(s,{class:`w-40 h-auto text-brand`}),i(`code`,p,a(e(D)(`ds.cards.paper`)),1)]),i(`div`,m,[r(s,{class:`w-40 h-auto text-brand`}),i(`code`,h,a(e(D)(`ds.cards.cream`)),1)]),i(`div`,g,[r(s,{class:`w-40 h-auto text-paper`}),i(`code`,_,a(e(D)(`ds.cards.brand`)),1)])])]),i(`section`,null,[i(`h2`,v,a(e(D)(`ds.heading.sizes`)),1),i(`div`,y,[i(`div`,b,[r(s,{class:`w-16 h-auto text-brand`}),E[0]||=i(`code`,{class:`font-mono text-[11px] text-muted`},`w-16`,-1)]),i(`div`,x,[r(s,{class:`w-32 h-auto text-brand`}),E[1]||=i(`code`,{class:`font-mono text-[11px] text-muted`},`w-32`,-1)]),i(`div`,S,[r(s,{class:`w-60 h-auto text-brand`}),E[2]||=i(`code`,{class:`font-mono text-[11px] text-muted`},`w-60`,-1)])])]),i(`section`,null,[i(`h2`,C,a(e(D)(`ds.heading.usage`)),1),i(`div`,w,[i(`p`,T,a(e(D)(`ds.logo.usageIntro`)),1),E[3]||=i(`pre`,{class:`whitespace-pre-wrap`},`<Logo class="w-60 h-auto text-brand" />`,-1)])])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{E as default};

File diff suppressed because one or more lines are too long

1
dist/assets/Navbar-ie3UVUXi.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
import{C as e,et as t,m as n,o as r,r as i,s as a,u as o,x as s}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{a as c}from"./vue-router-BrNWkU63.js";import{t as l}from"./Navbar-DZE5noW0.js";var u={class:`min-h-screen bg-surface`},d={class:`max-w-5xl mx-auto px-6 py-16 space-y-6`},f={__name:`NavbarPreview`,setup(f){let p=c(),m=r(()=>{let e=p.query.variant;return[`paper`,`cream`,`brand`].includes(e)?e:`paper`}),h=r(()=>p.query.layout===`floating`?`floating`:`standard`);return(r,c)=>(s(),o(`div`,u,[n(l,{variant:m.value,layout:h.value,"cart-count":2},null,8,[`variant`,`layout`]),a(`div`,d,[(s(),o(i,null,e([40,28,40,32,40,28,36,40],e=>a(`div`,{key:e,class:`rounded-md border border-line bg-paper`,style:t({height:e*4+`px`})},null,4)),64))])]))}};export{f as default}; import{C as e,et as t,m as n,o as r,r as i,s as a,u as o,x as s}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{a as c}from"./vue-router-BrNWkU63.js";import{t as l}from"./Navbar-ie3UVUXi.js";var u={class:`min-h-screen bg-surface`},d={class:`max-w-5xl mx-auto px-6 py-16 space-y-6`},f={__name:`NavbarPreview`,setup(f){let p=c(),m=r(()=>{let e=p.query.variant;return[`paper`,`cream`,`brand`].includes(e)?e:`paper`}),h=r(()=>p.query.layout===`floating`?`floating`:`standard`);return(r,c)=>(s(),o(`div`,u,[n(l,{variant:m.value,layout:h.value,"cart-count":2},null,8,[`variant`,`layout`]),a(`div`,d,[(s(),o(i,null,e([40,28,40,32,40,28,36,40],e=>a(`div`,{key:e,class:`rounded-md border border-line bg-paper`,style:t({height:e*4+`px`})},null,4)),64))])]))}};export{f as default};

View File

@@ -1,4 +1,4 @@
import{$ as e,C as t,L as n,P as r,c as i,et as a,k as o,m as s,o as c,p as l,r as u,s as d,tt as f,u as p,x as m}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as h}from"./i18n-B16vzzU0.js";import{t as g}from"./SectionShell-kXxofFIK.js";import{t as _}from"./DevicePreview-CdOTgn6S.js";var v=[`aria-label`],y=[`aria-selected`,`onClick`],b=[`aria-label`],x=[`aria-selected`,`onClick`],S={class:`eyebrow mb-5`},C={__name:`NavbarSection`,setup(C){let{t:w}=h(),T=c(()=>[{id:`paper`,label:w(`ds.navbar.tone.paper`),swatch:`#ffffff`},{id:`cream`,label:w(`ds.navbar.tone.cream`),swatch:`var(--color-cream)`},{id:`brand`,label:w(`ds.navbar.tone.brand`),swatch:`var(--color-brand)`}]),E=c(()=>[{id:`standard`,label:w(`ds.navbar.layout.standard`)},{id:`floating`,label:w(`ds.navbar.layout.floating`)}]),D=r(`paper`),O=r(`standard`),k=c(()=>`/design/preview/navbar?variant=${D.value}&layout=${O.value}`);return(r,c)=>(m(),i(g,{eyebrow:n(w)(`ds.eyebrow.components`),title:n(w)(`ds.navbar.title`),description:n(w)(`ds.navbar.description`),wide:``},{default:o(()=>[d(`section`,null,[s(_,{src:k.value,initial:`mobile`,height:720},{controls:o(()=>[d(`div`,{role:`tablist`,"aria-label":n(w)(`ds.navbar.layout`),class:`inline-flex items-center p-1 gap-0.5 rounded-pill border border-line bg-paper`},[(m(!0),p(u,null,t(E.value,t=>(m(),p(`button`,{key:t.id,type:`button`,role:`tab`,"aria-selected":O.value===t.id,class:e([`px-3 py-1.5 text-[12px] font-semibold tracking-label rounded-pill transition-colors duration-base`,O.value===t.id?`bg-brand text-accent`:`text-muted hover:text-brand`]),onClick:e=>O.value=t.id},f(t.label),11,y))),128))],8,v),d(`div`,{role:`tablist`,"aria-label":n(w)(`ds.navbar.tone`),class:`inline-flex items-center p-1 gap-0.5 rounded-pill border border-line bg-paper`},[(m(!0),p(u,null,t(T.value,t=>(m(),p(`button`,{key:t.id,type:`button`,role:`tab`,"aria-selected":D.value===t.id,class:e([`inline-flex items-center gap-2 px-3 py-1.5 text-[12px] font-semibold tracking-label rounded-pill transition-colors duration-base`,D.value===t.id?`bg-brand text-accent`:`text-muted hover:text-brand`]),onClick:e=>D.value=t.id},[d(`span`,{class:`w-2.5 h-2.5 rounded-full border border-line-strong`,style:a({backgroundColor:t.swatch})},null,4),l(` `+f(t.label),1)],10,x))),128))],8,b)]),_:1},8,[`src`])]),d(`section`,null,[d(`h2`,S,f(n(w)(`ds.heading.usage`)),1),c[0]||=d(`div`,{class:`rounded-md border border-line bg-paper p-6 font-mono text-[12px] text-ink`},[d(`pre`,{class:`whitespace-pre-wrap`},`<Navbar import{$ as e,C as t,L as n,P as r,c as i,et as a,k as o,m as s,o as c,p as l,r as u,s as d,tt as f,u as p,x as m}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as h}from"./i18n-DUHlbRTU.js";import{t as g}from"./SectionShell-DTyMkRQY.js";import{t as _}from"./DevicePreview-BgxBx9-q.js";var v=[`aria-label`],y=[`aria-selected`,`onClick`],b=[`aria-label`],x=[`aria-selected`,`onClick`],S={class:`eyebrow mb-5`},C={__name:`NavbarSection`,setup(C){let{t:w}=h(),T=c(()=>[{id:`paper`,label:w(`ds.navbar.tone.paper`),swatch:`#ffffff`},{id:`cream`,label:w(`ds.navbar.tone.cream`),swatch:`var(--color-cream)`},{id:`brand`,label:w(`ds.navbar.tone.brand`),swatch:`var(--color-brand)`}]),E=c(()=>[{id:`standard`,label:w(`ds.navbar.layout.standard`)},{id:`floating`,label:w(`ds.navbar.layout.floating`)}]),D=r(`paper`),O=r(`standard`),k=c(()=>`/design/preview/navbar?variant=${D.value}&layout=${O.value}`);return(r,c)=>(m(),i(g,{eyebrow:n(w)(`ds.eyebrow.components`),title:n(w)(`ds.navbar.title`),description:n(w)(`ds.navbar.description`),wide:``},{default:o(()=>[d(`section`,null,[s(_,{src:k.value,initial:`mobile`,height:720},{controls:o(()=>[d(`div`,{role:`tablist`,"aria-label":n(w)(`ds.navbar.layout`),class:`inline-flex items-center p-1 gap-0.5 rounded-pill border border-line bg-paper`},[(m(!0),p(u,null,t(E.value,t=>(m(),p(`button`,{key:t.id,type:`button`,role:`tab`,"aria-selected":O.value===t.id,class:e([`px-3 py-1.5 text-[12px] font-semibold tracking-label rounded-pill transition-colors duration-base`,O.value===t.id?`bg-brand text-accent`:`text-muted hover:text-brand`]),onClick:e=>O.value=t.id},f(t.label),11,y))),128))],8,v),d(`div`,{role:`tablist`,"aria-label":n(w)(`ds.navbar.tone`),class:`inline-flex items-center p-1 gap-0.5 rounded-pill border border-line bg-paper`},[(m(!0),p(u,null,t(T.value,t=>(m(),p(`button`,{key:t.id,type:`button`,role:`tab`,"aria-selected":D.value===t.id,class:e([`inline-flex items-center gap-2 px-3 py-1.5 text-[12px] font-semibold tracking-label rounded-pill transition-colors duration-base`,D.value===t.id?`bg-brand text-accent`:`text-muted hover:text-brand`]),onClick:e=>D.value=t.id},[d(`span`,{class:`w-2.5 h-2.5 rounded-full border border-line-strong`,style:a({backgroundColor:t.swatch})},null,4),l(` `+f(t.label),1)],10,x))),128))],8,b)]),_:1},8,[`src`])]),d(`section`,null,[d(`h2`,S,f(n(w)(`ds.heading.usage`)),1),c[0]||=d(`div`,{class:`rounded-md border border-line bg-paper p-6 font-mono text-[12px] text-ink`},[d(`pre`,{class:`whitespace-pre-wrap`},`<Navbar
variant="paper" variant="paper"
layout="floating" layout="floating"
:items="[{ key: 'nav.shop', href: '/shop' }]" :items="[{ key: 'nav.shop', href: '/shop' }]"

View File

@@ -1,4 +1,4 @@
import{$ as e,E as t,L as n,P as r,c as i,k as a,l as o,m as s,o as c,p as l,s as u,tt as d,u as f,x as p}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as m}from"./Icon-BaDkTwYL.js";import{t as h}from"./i18n-B16vzzU0.js";import{t as g}from"./Button-DOTlaJtk.js";import{t as _}from"./Badge-CHfkTK9K.js";import{t as v}from"./SectionShell-kXxofFIK.js";var y=[`src`,`alt`],b={class:`flex flex-col gap-3 p-6`},x={class:`flex flex-col gap-1`},S={key:0,class:`text-sm text-muted tracking-label`},C={class:`mt-auto flex items-center justify-between gap-3 pt-2`},w={class:`font-display text-2xl font-normal text-brand`},T={key:0,class:`text-xs font-semibold tracking-label uppercase text-danger`},E={__name:`ProductCard`,props:{title:{type:String,required:!0},size:{type:String,default:``},price:{type:[String,Number],required:!0},currency:{type:String,default:``},image:{type:String,required:!0},imageAlt:{type:String,default:``},badge:{type:String,default:``},badgeVariant:{type:String,default:`accent`,validator:e=>[`neutral`,`brand`,`accent`,`subtle`,`success`,`warning`,`danger`].includes(e)},tone:{type:String,default:`paper`,validator:e=>[`paper`,`cream`].includes(e)},inStock:{type:Boolean,default:!0},href:{type:String,default:``}},emits:[`add`],setup(r){let v=r,{t:E}=h(),D={paper:{surface:`bg-paper`,media:`bg-cream`,border:`border-line`},cream:{surface:`bg-cream`,media:`bg-paper`,border:`border-line`}},O=c(()=>D[v.tone]),k=c(()=>typeof v.price==`number`?`${v.currency} ${v.price.toFixed(2).replace(`.`,`,`)}`:`${v.currency} ${v.price}`);return(c,h)=>(p(),f(`article`,{class:e([`group flex flex-col overflow-hidden rounded-md border transition-all duration-base ease-out`,O.value.surface,O.value.border,`hover:-translate-y-1 hover:shadow-md hover:border-brand-soft`])},[(p(),i(t(r.href?`a`:`div`),{href:r.href||null,class:e([`relative block aspect-[4/5] overflow-hidden`,O.value.media])},{default:a(()=>[r.badge?(p(),i(_,{key:0,variant:r.badgeVariant,class:`absolute top-4 left-4 z-[1]`},{default:a(()=>[l(d(r.badge),1)]),_:1},8,[`variant`])):o(``,!0),u(`img`,{src:r.image,alt:r.imageAlt||r.title,loading:`lazy`,decoding:`async`,class:`absolute inset-0 w-full h-full object-contain p-8 transition-transform duration-slow ease-out group-hover:scale-105`},null,8,y)]),_:1},8,[`href`,`class`])),u(`div`,b,[u(`div`,x,[(p(),i(t(r.href?`a`:`h3`),{href:r.href||null,class:e([`font-display text-xl font-normal leading-tight text-ink`,r.href?`hover:text-brand transition-colors duration-base`:``])},{default:a(()=>[l(d(r.title),1)]),_:1},8,[`href`,`class`])),r.size?(p(),f(`p`,S,d(r.size),1)):o(``,!0)]),u(`div`,C,[u(`span`,w,d(k.value),1),r.inStock?o(``,!0):(p(),f(`span`,T,d(n(E)(`ds.product.outOfStock`)),1))]),s(g,{variant:`primary`,size:`md`,block:``,disabled:!r.inStock,onClick:h[0]||=e=>c.$emit(`add`)},{before:a(()=>[s(m,{name:`plus`,size:16})]),default:a(()=>[l(` `+d(n(E)(`ds.buttons.addToCart`)),1)]),_:1},8,[`disabled`])])],2))}},D={class:`eyebrow mb-5`},O={class:`grid sm:grid-cols-2 lg:grid-cols-3 gap-6`},k={key:0,class:`mt-5 text-sm text-muted`},A={class:`font-mono text-[12px]`},j={class:`eyebrow mb-5`},M={class:`grid sm:grid-cols-2 lg:grid-cols-3 gap-6`},N={class:`eyebrow mb-5`},P=`/products/cutouts/kaiser-natron-pulver-250-g-gro%C3%9Fpackung-removebg-preview.png`,F={__name:`ProductsSection`,setup(e){let{t}=h(),c=r(``);function m(e){c.value=e,setTimeout(()=>{c.value===e&&(c.value=``)},2e3)}return(e,r)=>(p(),i(v,{eyebrow:n(t)(`ds.eyebrow.components`),title:n(t)(`ds.product.title`),description:n(t)(`ds.product.description`)},{default:a(()=>[u(`section`,null,[u(`h2`,D,d(n(t)(`ds.heading.default`)),1),u(`div`,O,[s(E,{title:`Kaiser-Natron® Pulver`,size:`250 g Großpackung`,price:4.49,image:P,"image-alt":`Kaiser-Natron Pulver 250 g Großpackung`,href:`#`,onAdd:r[0]||=e=>m(`pulver-250`)}),s(E,{title:`Kaiser-Natron® Pulver`,size:`250 g Großpackung`,price:4.49,image:P,badge:n(t)(`ds.badges.featured`),"badge-variant":`accent`,href:`#`,onAdd:r[1]||=e=>m(`pulver-250-featured`)},null,8,[`badge`]),s(E,{title:`Kaiser-Natron® Pulver`,size:`250 g Großpackung`,price:4.49,image:P,tone:`cream`,href:`#`,onAdd:r[2]||=e=>m(`pulver-250-cream`)})]),c.value?(p(),f(`p`,k,[l(d(n(t)(`ds.product.added`))+`: `,1),u(`code`,A,d(c.value),1)])):o(``,!0)]),u(`section`,null,[u(`h2`,j,d(n(t)(`ds.heading.states`)),1),u(`div`,M,[s(E,{title:`Kaiser-Natron® Pulver`,size:`250 g Großpackung`,price:4.49,image:P,"in-stock":!1}),s(E,{title:`Kaiser-Natron® Pulver`,size:`250 g Großpackung`,price:4.49,image:P,badge:n(t)(`ds.badges.newRelease`),"badge-variant":`brand`,href:`#`},null,8,[`badge`])])]),u(`section`,null,[u(`h2`,N,d(n(t)(`ds.heading.usage`)),1),r[3]||=u(`div`,{class:`rounded-md border border-line bg-paper p-6 font-mono text-[12px] text-ink`},[u(`pre`,{class:`whitespace-pre-wrap`},`<ProductCard import{$ as e,E as t,L as n,P as r,c as i,k as a,l as o,m as s,o as c,p as l,s as u,tt as d,u as f,x as p}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as m}from"./Icon-BaDkTwYL.js";import{t as h}from"./i18n-DUHlbRTU.js";import{t as g}from"./Button-De2-980w.js";import{t as _}from"./Badge-BPajj5yh.js";import{t as v}from"./SectionShell-DTyMkRQY.js";var y=[`src`,`alt`],b={class:`flex flex-col gap-3 p-6`},x={class:`flex flex-col gap-1`},S={key:0,class:`text-sm text-muted tracking-label`},C={class:`mt-auto flex items-center justify-between gap-3 pt-2`},w={class:`font-display text-2xl font-normal text-brand`},T={key:0,class:`text-xs font-semibold tracking-label uppercase text-danger`},E={__name:`ProductCard`,props:{title:{type:String,required:!0},size:{type:String,default:``},price:{type:[String,Number],required:!0},currency:{type:String,default:``},image:{type:String,required:!0},imageAlt:{type:String,default:``},badge:{type:String,default:``},badgeVariant:{type:String,default:`accent`,validator:e=>[`neutral`,`brand`,`accent`,`subtle`,`success`,`warning`,`danger`].includes(e)},tone:{type:String,default:`paper`,validator:e=>[`paper`,`cream`].includes(e)},inStock:{type:Boolean,default:!0},href:{type:String,default:``}},emits:[`add`],setup(r){let v=r,{t:E}=h(),D={paper:{surface:`bg-paper`,media:`bg-cream`,border:`border-line`},cream:{surface:`bg-cream`,media:`bg-paper`,border:`border-line`}},O=c(()=>D[v.tone]),k=c(()=>typeof v.price==`number`?`${v.currency} ${v.price.toFixed(2).replace(`.`,`,`)}`:`${v.currency} ${v.price}`);return(c,h)=>(p(),f(`article`,{class:e([`group flex flex-col overflow-hidden rounded-md border transition-all duration-base ease-out`,O.value.surface,O.value.border,`hover:-translate-y-1 hover:shadow-md hover:border-brand-soft`])},[(p(),i(t(r.href?`a`:`div`),{href:r.href||null,class:e([`relative block aspect-[4/5] overflow-hidden`,O.value.media])},{default:a(()=>[r.badge?(p(),i(_,{key:0,variant:r.badgeVariant,class:`absolute top-4 left-4 z-[1]`},{default:a(()=>[l(d(r.badge),1)]),_:1},8,[`variant`])):o(``,!0),u(`img`,{src:r.image,alt:r.imageAlt||r.title,loading:`lazy`,decoding:`async`,class:`absolute inset-0 w-full h-full object-contain p-8 transition-transform duration-slow ease-out group-hover:scale-105`},null,8,y)]),_:1},8,[`href`,`class`])),u(`div`,b,[u(`div`,x,[(p(),i(t(r.href?`a`:`h3`),{href:r.href||null,class:e([`font-display text-xl font-normal leading-tight text-ink`,r.href?`hover:text-brand transition-colors duration-base`:``])},{default:a(()=>[l(d(r.title),1)]),_:1},8,[`href`,`class`])),r.size?(p(),f(`p`,S,d(r.size),1)):o(``,!0)]),u(`div`,C,[u(`span`,w,d(k.value),1),r.inStock?o(``,!0):(p(),f(`span`,T,d(n(E)(`ds.product.outOfStock`)),1))]),s(g,{variant:`primary`,size:`md`,block:``,disabled:!r.inStock,onClick:h[0]||=e=>c.$emit(`add`)},{before:a(()=>[s(m,{name:`plus`,size:16})]),default:a(()=>[l(` `+d(n(E)(`ds.buttons.addToCart`)),1)]),_:1},8,[`disabled`])])],2))}},D={class:`eyebrow mb-5`},O={class:`grid sm:grid-cols-2 lg:grid-cols-3 gap-6`},k={key:0,class:`mt-5 text-sm text-muted`},A={class:`font-mono text-[12px]`},j={class:`eyebrow mb-5`},M={class:`grid sm:grid-cols-2 lg:grid-cols-3 gap-6`},N={class:`eyebrow mb-5`},P=`/products/cutouts/kaiser-natron-pulver-250-g-gro%C3%9Fpackung-removebg-preview.png`,F={__name:`ProductsSection`,setup(e){let{t}=h(),c=r(``);function m(e){c.value=e,setTimeout(()=>{c.value===e&&(c.value=``)},2e3)}return(e,r)=>(p(),i(v,{eyebrow:n(t)(`ds.eyebrow.components`),title:n(t)(`ds.product.title`),description:n(t)(`ds.product.description`)},{default:a(()=>[u(`section`,null,[u(`h2`,D,d(n(t)(`ds.heading.default`)),1),u(`div`,O,[s(E,{title:`Kaiser-Natron® Pulver`,size:`250 g Großpackung`,price:4.49,image:P,"image-alt":`Kaiser-Natron Pulver 250 g Großpackung`,href:`#`,onAdd:r[0]||=e=>m(`pulver-250`)}),s(E,{title:`Kaiser-Natron® Pulver`,size:`250 g Großpackung`,price:4.49,image:P,badge:n(t)(`ds.badges.featured`),"badge-variant":`accent`,href:`#`,onAdd:r[1]||=e=>m(`pulver-250-featured`)},null,8,[`badge`]),s(E,{title:`Kaiser-Natron® Pulver`,size:`250 g Großpackung`,price:4.49,image:P,tone:`cream`,href:`#`,onAdd:r[2]||=e=>m(`pulver-250-cream`)})]),c.value?(p(),f(`p`,k,[l(d(n(t)(`ds.product.added`))+`: `,1),u(`code`,A,d(c.value),1)])):o(``,!0)]),u(`section`,null,[u(`h2`,j,d(n(t)(`ds.heading.states`)),1),u(`div`,M,[s(E,{title:`Kaiser-Natron® Pulver`,size:`250 g Großpackung`,price:4.49,image:P,"in-stock":!1}),s(E,{title:`Kaiser-Natron® Pulver`,size:`250 g Großpackung`,price:4.49,image:P,badge:n(t)(`ds.badges.newRelease`),"badge-variant":`brand`,href:`#`},null,8,[`badge`])])]),u(`section`,null,[u(`h2`,N,d(n(t)(`ds.heading.usage`)),1),r[3]||=u(`div`,{class:`rounded-md border border-line bg-paper p-6 font-mono text-[12px] text-ink`},[u(`pre`,{class:`whitespace-pre-wrap`},`<ProductCard
title="Kaiser-Natron® Pulver" title="Kaiser-Natron® Pulver"
size="250 g Großpackung" size="250 g Großpackung"
:price="4.49" :price="4.49"

View File

@@ -1 +1 @@
import{C as e,L as t,c as n,et as r,k as i,r as a,s as o,tt as s,u as c,x as l}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as u}from"./i18n-B16vzzU0.js";import{t as d}from"./SectionShell-kXxofFIK.js";var f={class:`grid grid-cols-2 sm:grid-cols-3 gap-6`},p={class:`font-mono text-[12px] text-ink block`},m={class:`text-[12px] text-muted mt-1`},h={__name:`RadiiSection`,setup(h){let{t:g}=u(),_=[{name:`xs`,value:`6px`},{name:`sm`,value:`10px`},{name:`md`,value:`16px`},{name:`lg`,value:`20px`},{name:`xl`,value:`28px`},{name:`pill`,value:`100px`}];return(u,h)=>(l(),n(d,{eyebrow:t(g)(`ds.eyebrow.tokens`),title:t(g)(`ds.radii.title`),description:t(g)(`ds.radii.description`)},{default:i(()=>[o(`div`,f,[(l(),c(a,null,e(_,e=>o(`div`,{key:e.name,class:`flex flex-col items-center`},[o(`div`,{class:`h-32 w-full bg-paper border border-line mb-4 shadow-sm`,style:r({borderRadius:`var(--radius-${e.name})`})},null,4),o(`code`,p,`--radius-`+s(e.name),1),o(`span`,m,s(e.value),1)])),64))])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{h as default}; import{C as e,L as t,c as n,et as r,k as i,r as a,s as o,tt as s,u as c,x as l}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as u}from"./i18n-DUHlbRTU.js";import{t as d}from"./SectionShell-DTyMkRQY.js";var f={class:`grid grid-cols-2 sm:grid-cols-3 gap-6`},p={class:`font-mono text-[12px] text-ink block`},m={class:`text-[12px] text-muted mt-1`},h={__name:`RadiiSection`,setup(h){let{t:g}=u(),_=[{name:`xs`,value:`6px`},{name:`sm`,value:`10px`},{name:`md`,value:`16px`},{name:`lg`,value:`20px`},{name:`xl`,value:`28px`},{name:`pill`,value:`100px`}];return(u,h)=>(l(),n(d,{eyebrow:t(g)(`ds.eyebrow.tokens`),title:t(g)(`ds.radii.title`),description:t(g)(`ds.radii.description`)},{default:i(()=>[o(`div`,f,[(l(),c(a,null,e(_,e=>o(`div`,{key:e.name,class:`flex flex-col items-center`},[o(`div`,{class:`h-32 w-full bg-paper border border-line mb-4 shadow-sm`,style:r({borderRadius:`var(--radius-${e.name})`})},null,4),o(`code`,p,`--radius-`+s(e.name),1),o(`span`,m,s(e.value),1)])),64))])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{h as default};

1
dist/assets/Search-Cy8p4ZPV.js vendored Normal file

File diff suppressed because one or more lines are too long

11
dist/assets/SearchSection-DyxYPxDM.js vendored Normal file
View File

@@ -0,0 +1,11 @@
import{C as e,L as t,P as n,c as r,k as i,m as a,p as o,r as s,s as c,tt as l,u,x as d}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as f}from"./Icon-BaDkTwYL.js";import{t as p}from"./i18n-DUHlbRTU.js";import{t as m}from"./Search-Cy8p4ZPV.js";import{t as h}from"./Button-De2-980w.js";import{n as g,t as _}from"./products-BJebky8H.js";import{t as v}from"./SectionShell-DTyMkRQY.js";var y={class:`eyebrow mb-5`},b={class:`rounded-md border border-line bg-paper p-6 flex flex-col sm:flex-row sm:items-center gap-4`},x={class:`text-[13px] text-muted`},S={class:`eyebrow mb-5`},C={class:`rounded-md border border-line bg-paper divide-y divide-line overflow-hidden`},w=[`src`,`alt`],T={class:`min-w-0 flex-1`},E={class:`text-[14px] font-semibold text-ink truncate`},D={class:`text-[12px] text-muted truncate`},O={class:`shrink-0 text-[13px] font-semibold text-brand`},k={class:`eyebrow mb-5`},A=`natron pulver`,j={__name:`SearchSection`,setup(j){let{t:M}=p(),N=n(!1),P=g(A,_,4);return(n,p)=>(d(),r(v,{eyebrow:t(M)(`ds.eyebrow.components`),title:t(M)(`ds.search.title`),description:t(M)(`ds.search.description`)},{default:i(()=>[c(`section`,null,[c(`h2`,y,l(t(M)(`ds.heading.default`)),1),c(`div`,b,[a(h,{variant:`primary`,onClick:p[0]||=e=>N.value=!0},{before:i(()=>[a(f,{name:`search`,size:18})]),default:i(()=>[o(` `+l(t(M)(`ds.search.demo.label`)),1)]),_:1}),c(`p`,x,l(t(M)(`ds.search.demo.hint`)),1)])]),c(`section`,null,[c(`h2`,S,l(t(M)(`ds.heading.variants`))+` — "`+l(A)+`"`,1),c(`ul`,C,[(d(!0),u(s,null,e(t(P),e=>(d(),u(`li`,{key:e.id,class:`flex items-center gap-4 px-4 py-3`},[c(`img`,{src:e.image,alt:e.title,loading:`lazy`,decoding:`async`,class:`shrink-0 w-12 h-12 rounded-sm object-cover bg-cream`},null,8,w),c(`div`,T,[c(`p`,E,l(e.title),1),c(`p`,D,l(e.size)+` · `+l(e.category),1)]),c(`span`,O,``+l(e.price.toFixed(2).replace(`.`,`,`)),1)]))),128))])]),c(`section`,null,[c(`h2`,k,l(t(M)(`ds.heading.usage`)),1),p[2]||=c(`div`,{class:`rounded-md border border-line bg-paper p-6 font-mono text-[12px] text-ink`},[c(`pre`,{class:`whitespace-pre-wrap`},`import { products } from '@/api/products.js'
import Search from '@/design-system/components/Search.vue'
const open = ref(false)
<Search v-model:open="open" :products="products" />
// Navbar already renders its own Search overlay, so most callers
// just pass the catalogue in:
<Navbar :products="products" />`)],-1)]),a(m,{open:N.value,"onUpdate:open":p[1]||=e=>N.value=e,products:t(_)},null,8,[`open`,`products`])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{j as default};

View File

@@ -1 +1 @@
import{C as e,L as t,c as n,et as r,k as i,o as a,r as o,s,tt as c,u as l,x as u}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as d}from"./i18n-B16vzzU0.js";import{t as f}from"./SectionShell-kXxofFIK.js";var p={class:`grid sm:grid-cols-3 gap-8`},m={class:`font-mono text-[12px] text-ink block`},h={class:`text-[13px] text-muted mt-1`},g={__name:`ShadowsSection`,setup(g){let{t:_}=d(),v=a(()=>[{name:`sm`,css:`var(--shadow-sm)`,note:_(`ds.shadows.sm.note`)},{name:`md`,css:`var(--shadow-md)`,note:_(`ds.shadows.md.note`)},{name:`lg`,css:`var(--shadow-lg)`,note:_(`ds.shadows.lg.note`)}]);return(a,d)=>(u(),n(f,{eyebrow:t(_)(`ds.eyebrow.tokens`),title:t(_)(`ds.shadows.title`),description:t(_)(`ds.shadows.description`)},{default:i(()=>[s(`div`,p,[(u(!0),l(o,null,e(v.value,e=>(u(),l(`div`,{key:e.name,class:`space-y-4`},[s(`div`,{class:`h-36 rounded-md bg-paper`,style:r({boxShadow:e.css})},null,4),s(`div`,null,[s(`code`,m,`--shadow-`+c(e.name),1),s(`p`,h,c(e.note),1)])]))),128))])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{g as default}; import{C as e,L as t,c as n,et as r,k as i,o as a,r as o,s,tt as c,u as l,x as u}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as d}from"./i18n-DUHlbRTU.js";import{t as f}from"./SectionShell-DTyMkRQY.js";var p={class:`grid sm:grid-cols-3 gap-8`},m={class:`font-mono text-[12px] text-ink block`},h={class:`text-[13px] text-muted mt-1`},g={__name:`ShadowsSection`,setup(g){let{t:_}=d(),v=a(()=>[{name:`sm`,css:`var(--shadow-sm)`,note:_(`ds.shadows.sm.note`)},{name:`md`,css:`var(--shadow-md)`,note:_(`ds.shadows.md.note`)},{name:`lg`,css:`var(--shadow-lg)`,note:_(`ds.shadows.lg.note`)}]);return(a,d)=>(u(),n(f,{eyebrow:t(_)(`ds.eyebrow.tokens`),title:t(_)(`ds.shadows.title`),description:t(_)(`ds.shadows.description`)},{default:i(()=>[s(`div`,p,[(u(!0),l(o,null,e(v.value,e=>(u(),l(`div`,{key:e.name,class:`space-y-4`},[s(`div`,{class:`h-36 rounded-md bg-paper`,style:r({boxShadow:e.css})},null,4),s(`div`,null,[s(`code`,m,`--shadow-`+c(e.name),1),s(`p`,h,c(e.note),1)])]))),128))])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{g as default};

View File

@@ -1 +1 @@
import{$ as e,C as t,L as n,c as r,k as i,m as a,r as o,s,tt as c,u as l,x as u}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as d}from"./i18n-B16vzzU0.js";import{t as f}from"./SectionShell-kXxofFIK.js";import{t as p}from"./Card-DRIe4MlQ.js";var m={class:`grid md:grid-cols-2 gap-6`},h={class:`eyebrow mb-3`},g={class:`text-[14px] text-muted leading-relaxed`},_={class:`eyebrow mb-3`},v={class:`text-[14px] text-muted leading-relaxed`},y={class:`eyebrow mb-6`},b={class:`divide-y divide-line rounded-md border border-line bg-paper px-6`},x={class:`font-mono text-[11px] text-muted w-24 shrink-0`},S={__name:`TypographySection`,setup(S){let{t:C}=d(),w=[`xs`,`sm`,`base`,`lg`,`xl`,`2xl`,`3xl`,`4xl`,`5xl`];return(d,S)=>(u(),r(f,{eyebrow:n(C)(`ds.eyebrow.tokens`),title:n(C)(`ds.typography.title`),description:n(C)(`ds.typography.description`)},{default:i(()=>[s(`section`,m,[a(p,{tone:`paper`},{default:i(()=>[s(`p`,h,c(n(C)(`ds.heading.display`)),1),S[0]||=s(`p`,{class:`font-display text-5xl font-normal leading-[1.05] mb-3`},`Fraunces`,-1),s(`p`,g,c(n(C)(`ds.typography.serifDesc`)),1),S[1]||=s(`code`,{class:`font-mono text-[11px] text-muted block mt-5`},`var(--font-serif)`,-1)]),_:1}),a(p,{tone:`paper`},{default:i(()=>[s(`p`,_,c(n(C)(`ds.heading.body`)),1),S[2]||=s(`p`,{class:`font-sans text-5xl font-medium leading-[1.05] mb-3`},`DM Sans`,-1),s(`p`,v,c(n(C)(`ds.typography.sansDesc`)),1),S[3]||=s(`code`,{class:`font-mono text-[11px] text-muted block mt-5`},`var(--font-sans)`,-1)]),_:1})]),s(`section`,null,[s(`h2`,y,c(n(C)(`ds.heading.scale`)),1),s(`div`,b,[(u(),l(o,null,t(w,t=>s(`div`,{key:t,class:`flex items-baseline gap-8 py-4`},[s(`code`,x,`text-`+c(t),1),s(`span`,{class:e(`text-${t}`)},c(n(C)(`ds.typography.sample`)),3)])),64))])])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{S as default}; import{$ as e,C as t,L as n,c as r,k as i,m as a,r as o,s,tt as c,u as l,x as u}from"./runtime-core.esm-bundler-CjdnoyKJ.js";import{t as d}from"./i18n-DUHlbRTU.js";import{t as f}from"./SectionShell-DTyMkRQY.js";import{t as p}from"./Card-ailigDjs.js";var m={class:`grid md:grid-cols-2 gap-6`},h={class:`eyebrow mb-3`},g={class:`text-[14px] text-muted leading-relaxed`},_={class:`eyebrow mb-3`},v={class:`text-[14px] text-muted leading-relaxed`},y={class:`eyebrow mb-6`},b={class:`divide-y divide-line rounded-md border border-line bg-paper px-6`},x={class:`font-mono text-[11px] text-muted w-24 shrink-0`},S={__name:`TypographySection`,setup(S){let{t:C}=d(),w=[`xs`,`sm`,`base`,`lg`,`xl`,`2xl`,`3xl`,`4xl`,`5xl`];return(d,S)=>(u(),r(f,{eyebrow:n(C)(`ds.eyebrow.tokens`),title:n(C)(`ds.typography.title`),description:n(C)(`ds.typography.description`)},{default:i(()=>[s(`section`,m,[a(p,{tone:`paper`},{default:i(()=>[s(`p`,h,c(n(C)(`ds.heading.display`)),1),S[0]||=s(`p`,{class:`font-display text-5xl font-normal leading-[1.05] mb-3`},`Fraunces`,-1),s(`p`,g,c(n(C)(`ds.typography.serifDesc`)),1),S[1]||=s(`code`,{class:`font-mono text-[11px] text-muted block mt-5`},`var(--font-serif)`,-1)]),_:1}),a(p,{tone:`paper`},{default:i(()=>[s(`p`,_,c(n(C)(`ds.heading.body`)),1),S[2]||=s(`p`,{class:`font-sans text-5xl font-medium leading-[1.05] mb-3`},`DM Sans`,-1),s(`p`,v,c(n(C)(`ds.typography.sansDesc`)),1),S[3]||=s(`code`,{class:`font-mono text-[11px] text-muted block mt-5`},`var(--font-sans)`,-1)]),_:1})]),s(`section`,null,[s(`h2`,y,c(n(C)(`ds.heading.scale`)),1),s(`div`,b,[(u(),l(o,null,t(w,t=>s(`div`,{key:t,class:`flex items-baseline gap-8 py-4`},[s(`code`,x,`text-`+c(t),1),s(`span`,{class:e(`text-${t}`)},c(n(C)(`ds.typography.sample`)),3)])),64))])])]),_:1},8,[`eyebrow`,`title`,`description`]))}};export{S as default};

File diff suppressed because one or more lines are too long

1
dist/assets/i18n-DUHlbRTU.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/index-BtCKAUbd.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/products-BJebky8H.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

6
dist/index.html vendored
View File

@@ -12,12 +12,12 @@
href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,200;0,9..144,400;0,9..144,600;0,9..144,700;1,9..144,200;1,9..144,400;1,9..144,600&family=DM+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400&display=swap" href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,200;0,9..144,400;0,9..144,600;0,9..144,700;1,9..144,200;1,9..144,400;1,9..144,600&family=DM+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400&display=swap"
rel="stylesheet" rel="stylesheet"
/> />
<script type="module" crossorigin src="/assets/index-CopuHhRz.js"></script> <script type="module" crossorigin src="/assets/index-BbWJ9mTE.js"></script>
<link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-CXTkFu_Z.js"> <link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-CXTkFu_Z.js">
<link rel="modulepreload" crossorigin href="/assets/runtime-core.esm-bundler-CjdnoyKJ.js"> <link rel="modulepreload" crossorigin href="/assets/runtime-core.esm-bundler-CjdnoyKJ.js">
<link rel="modulepreload" crossorigin href="/assets/runtime-dom.esm-bundler-DWTDSpp2.js"> <link rel="modulepreload" crossorigin href="/assets/runtime-dom.esm-bundler-CDZvOKZN.js">
<link rel="modulepreload" crossorigin href="/assets/vue-router-BrNWkU63.js"> <link rel="modulepreload" crossorigin href="/assets/vue-router-BrNWkU63.js">
<link rel="stylesheet" crossorigin href="/assets/index-DZsInX8d.css"> <link rel="stylesheet" crossorigin href="/assets/index-BtCKAUbd.css">
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

View File

@@ -0,0 +1,260 @@
// Product catalog fixture. Drives the UI until the backend (Python/MySQL)
// fills in the real `src/api/` surface. Same shape — id/sku, title, brand,
// size, price, image, category, keywords, href — so consumers don't have
// to change when the real API lands.
//
// Keywords are lowercase, unaccented tokens the search layer uses to
// broaden matches (a user typing "backen" should still find "Natron Pulver",
// a user typing "kalk" should find "Entkalker", etc.).
const IMG = '/products/'
export const products = [
// --- Kaiser-Natron line -------------------------------------------------
{
sku: 'kn-pulver-50',
title: 'Kaiser-Natron® Pulver',
brand: 'Kaiser Natron',
size: '50 g Beutel',
price: 1.49,
image: `${IMG}kaiser-natron-pulver-50-g-beutel.jpg`,
category: 'Haushalt',
keywords: ['natron', 'pulver', 'backen', 'neutralisieren', 'reinigen'],
href: '/shop/kaiser-natron-pulver-50-g-beutel',
},
{
sku: 'kn-pulver-250',
title: 'Kaiser-Natron® Pulver',
brand: 'Kaiser Natron',
size: '250 g Großpackung',
price: 4.49,
image: '/products/cutouts/kaiser-natron-pulver-250-g-gro%C3%9Fpackung-removebg-preview.png',
category: 'Haushalt',
keywords: ['natron', 'pulver', 'großpackung', 'backen', 'neutralisieren'],
href: '/shop/kaiser-natron-pulver-250-g-grosspackung',
},
{
sku: 'kn-pulver-3490',
title: 'Kaiser-Natron® Pulver',
brand: 'Kaiser Natron',
size: '3.490 g Eimer',
price: 34.9,
image: `${IMG}kaiser-natron-pulver-3.490-g-eimer.jpg`,
category: 'Haushalt',
keywords: ['natron', 'pulver', 'eimer', 'gastro', 'großverbraucher'],
href: '/shop/kaiser-natron-pulver-3490-g-eimer',
},
{
sku: 'kn-tabletten-100',
title: 'Kaiser-Natron® Tabletten',
brand: 'Kaiser Natron',
size: '100 g Dose',
price: 2.99,
image: `${IMG}kaiser-natron-tabletten-100-g-dose.jpg`,
category: 'Gesundheit',
keywords: ['natron', 'tabletten', 'dose', 'magen', 'sodbrennen'],
href: '/shop/kaiser-natron-tabletten-100-g-dose',
},
{
sku: 'kn-allzweck-reiniger-750',
title: 'Kaiser-Natron® Allzweck-Reiniger',
brand: 'Kaiser Natron',
size: '750 ml',
price: 5.49,
image: `${IMG}kaiser-natron-allzweck-reiniger-750-ml.jpg`,
category: 'Reiniger',
keywords: ['reiniger', 'allzweck', 'küche', 'bad', 'haushaltsreiniger'],
href: '/shop/kaiser-natron-allzweck-reiniger-750-ml',
},
{
sku: 'kn-allzweck-spray-500',
title: 'Kaiser-Natron® Allzweck-Spray',
brand: 'Kaiser Natron',
size: '500 ml',
price: 4.99,
image: `${IMG}kaiser-natron-allzweck-spray-500-ml.jpg`,
category: 'Reiniger',
keywords: ['spray', 'allzweck', 'reiniger', 'küche', 'bad'],
href: '/shop/kaiser-natron-allzweck-spray-500-ml',
},
{
sku: 'kn-bad-500',
title: 'Kaiser-Natron® Bad',
brand: 'Kaiser Natron',
size: '500 g',
price: 3.99,
image: `${IMG}kaiser-natron-bad-500-g.jpg`,
category: 'Pflege',
keywords: ['bad', 'badezusatz', 'wellness', 'entspannung', 'haut'],
href: '/shop/kaiser-natron-bad-500-g',
},
{
sku: 'kn-fussbad-500',
title: 'Kaiser-Natron® Fußbad',
brand: 'Kaiser Natron',
size: '500 g',
price: 3.99,
image: `${IMG}kaiser-natron-fussbad-500-g.jpg`,
category: 'Pflege',
keywords: ['fußbad', 'füße', 'pflege', 'wellness', 'entspannung'],
href: '/shop/kaiser-natron-fussbad-500-g',
},
{
sku: 'kn-daunenwasch-250',
title: 'Kaiser-Natron® Daunenwasch',
brand: 'Kaiser Natron',
size: '250 ml',
price: 6.49,
image: `${IMG}kaiser-natron-daunenwasch-250-ml.jpg`,
category: 'Wäsche',
keywords: ['daunen', 'waschen', 'wäsche', 'federbett', 'jacke'],
href: '/shop/kaiser-natron-daunenwasch-250-ml',
},
{
sku: 'kn-sport-profi-250',
title: 'Kaiser-Natron® Sport-Profi',
brand: 'Kaiser Natron',
size: '250 ml',
price: 5.99,
image: `${IMG}kaiser-natron-sport-profi-250-ml.jpg`,
category: 'Wäsche',
keywords: ['sport', 'funktion', 'geruch', 'wäsche', 'schweiß'],
href: '/shop/kaiser-natron-sport-profi-250-ml',
},
{
sku: 'kn-spuelmittel-500',
title: 'Kaiser-Natron® Spülmittel',
brand: 'Kaiser Natron',
size: '500 ml',
price: 3.49,
image: `${IMG}kaiser-natron-spuelmittel-500-ml.jpg`,
category: 'Reiniger',
keywords: ['spülmittel', 'geschirr', 'küche', 'reinigen'],
href: '/shop/kaiser-natron-spuelmittel-500-ml',
},
// --- Holste line --------------------------------------------------------
{
sku: 'holste-handwaschpaste-500',
title: 'Holste Handwaschpaste',
brand: 'Holste',
size: '500 ml',
price: 6.99,
image: `${IMG}holste-handwaschpaste-500-ml.jpg`,
category: 'Reiniger',
keywords: ['handwaschpaste', 'werkstatt', 'öl', 'fett', 'grobreiniger'],
href: '/shop/holste-handwaschpaste-500-ml',
},
{
sku: 'holste-kalk-urinstein-750',
title: 'Holste Kalk- und Urinsteinlöser',
brand: 'Holste',
size: '750 ml',
price: 7.49,
image: `${IMG}holste-kalk--und-urinsteinloeser-750-ml.jpg`,
category: 'Reiniger',
keywords: ['kalk', 'urinstein', 'wc', 'toilette', 'entkalker'],
href: '/shop/holste-kalk-und-urinsteinloeser-750-ml',
},
{
sku: 'holste-reisstaerke-250',
title: 'Holste Reisstärke',
brand: 'Holste',
size: '250 g Faltschachtel',
price: 3.99,
image: `${IMG}holste-reisstaerke-250-g-faltschachtel.jpg`,
category: 'Wäsche',
keywords: ['stärke', 'reisstärke', 'bügeln', 'wäsche', 'hemd'],
href: '/shop/holste-reisstaerke-250-g-faltschachtel',
},
{
sku: 'holste-schmierseife-1l',
title: 'Holste Schmierseife flüssig',
brand: 'Holste',
size: '1 l Flasche',
price: 4.99,
image: `${IMG}holste-schmierseife-fluessig-1-l-flasche.jpg`,
category: 'Reiniger',
keywords: ['schmierseife', 'seife', 'boden', 'holz', 'flüssig'],
href: '/shop/holste-schmierseife-fluessig-1-l-flasche',
},
{
sku: 'holste-wasch-soda-500',
title: 'Holste Wasch-Soda',
brand: 'Holste',
size: '500 g Beutel',
price: 2.49,
image: `${IMG}holste-wasch-soda-500-g-beutel.jpg`,
category: 'Wäsche',
keywords: ['soda', 'wasch-soda', 'wäsche', 'reinigen', 'fett'],
href: '/shop/holste-wasch-soda-500-g-beutel',
},
{
sku: 'holste-zitronensaeure-500',
title: 'Holste Zitronensäure-Entkalker',
brand: 'Holste',
size: '500 ml flüssig',
price: 4.49,
image: `${IMG}holste-zitronensaeure-entkalker-fluessig-500-ml.jpg`,
category: 'Reiniger',
keywords: ['zitronensäure', 'entkalker', 'kalk', 'wasserkocher', 'bio'],
href: '/shop/holste-zitronensaeure-entkalker-fluessig-500-ml',
},
// --- Andere Marken ------------------------------------------------------
{
sku: 'gazelle-waeschestaerke-1000',
title: 'Gazelle Wäschestärke',
brand: 'Gazelle',
size: '1000 ml Flasche',
price: 5.99,
image: `${IMG}gazelle-waeschestaerke-1000-ml-flasche.jpg`,
category: 'Wäsche',
keywords: ['stärke', 'wäschestärke', 'bügeln', 'hemd', 'gazelle'],
href: '/shop/gazelle-waeschestaerke-1000-ml-flasche',
},
{
sku: 'gruene-tante-quarz-500',
title: 'Grüne Tante mit Quarzmehl',
brand: 'Grüne Tante',
size: '500 ml Dose',
price: 8.49,
image: `${IMG}gruene-tante-mit-quarzmehl-500-ml-dose.jpg`,
category: 'Reiniger',
keywords: ['grüne tante', 'quarzmehl', 'handreiniger', 'werkstatt', 'grob'],
href: '/shop/gruene-tante-mit-quarzmehl-500-ml-dose',
},
{
sku: 'linda-fleckenweg-200',
title: 'Linda Fleckenweg',
brand: 'Linda',
size: '200 ml Tube',
price: 3.49,
image: `${IMG}linda-fleckenweg-200-ml-tube.jpg`,
category: 'Wäsche',
keywords: ['fleck', 'fleckenentferner', 'wäsche', 'linda', 'tube'],
href: '/shop/linda-fleckenweg-200-ml-tube',
},
{
sku: 'linda-handreiniger-200',
title: 'Linda Handreiniger der Kraftvolle',
brand: 'Linda',
size: '200 g Tube',
price: 4.49,
image: `${IMG}linda-handreiniger-der-kraftvolle-200-g-tube.jpg`,
category: 'Reiniger',
keywords: ['handreiniger', 'hände', 'werkstatt', 'öl', 'fett', 'linda'],
href: '/shop/linda-handreiniger-der-kraftvolle-200-g-tube',
},
{
sku: 'linda-neutral-375',
title: 'Linda Neutral',
brand: 'Linda',
size: '375 ml Dose',
price: 5.49,
image: `${IMG}linda-neutral-375-ml-dose.jpg`,
category: 'Pflege',
keywords: ['neutral', 'pflege', 'hände', 'linda'],
href: '/shop/linda-neutral-375-ml-dose',
},
]

4
src/api/index.js Normal file
View File

@@ -0,0 +1,4 @@
// Barrel for the API boundary. Swap these imports for real backend calls
// when the Python/MySQL side lands — callers keep the same import path.
export { products } from './fixtures/products.js'

340
src/api/products.js Normal file
View File

@@ -0,0 +1,340 @@
// Product catalog — frontend fixture until the Python/MySQL backend takes over.
// Shape and helper are the same surface the API module will eventually expose.
export const products = [
{
id: 'kaiser-natron-pulver-50-g-beutel',
brand: 'Kaiser-Natron',
title: 'Kaiser-Natron® Pulver',
size: '50 g Beutel',
category: 'Pulver',
price: 1.29,
image: '/products/kaiser-natron-pulver-50-g-beutel.jpg',
href: '/shop/kaiser-natron-pulver-50-g-beutel',
inStock: true,
keywords: ['natron', 'backsoda', 'bicarbonat', 'natriumhydrogencarbonat', 'baking soda'],
},
{
id: 'kaiser-natron-pulver-250-g-grosspackung',
brand: 'Kaiser-Natron',
title: 'Kaiser-Natron® Pulver',
size: '250 g Großpackung',
category: 'Pulver',
price: 4.49,
image: '/products/kaiser-natron-pulver-250-g-gro%C3%9Fpackung.jpg',
href: '/shop/kaiser-natron-pulver-250-g-grosspackung',
inStock: true,
keywords: ['natron', 'backsoda', 'großpackung', 'bicarbonat', 'baking soda', 'vorrat'],
},
{
id: 'kaiser-natron-pulver-3490-g-eimer',
brand: 'Kaiser-Natron',
title: 'Kaiser-Natron® Pulver',
size: '3.490 g Eimer',
category: 'Pulver',
price: 29.9,
image: '/products/kaiser-natron-pulver-3.490-g-eimer.jpg',
href: '/shop/kaiser-natron-pulver-3490-g-eimer',
inStock: true,
keywords: ['natron', 'eimer', 'großgebinde', 'gastro', 'gewerbe', 'baking soda'],
},
{
id: 'kaiser-natron-tabletten-100-g-dose',
brand: 'Kaiser-Natron',
title: 'Kaiser-Natron® Tabletten',
size: '100 g Dose',
category: 'Tabletten',
price: 3.49,
image: '/products/kaiser-natron-tabletten-100-g-dose.jpg',
href: '/shop/kaiser-natron-tabletten-100-g-dose',
inStock: true,
keywords: ['natron', 'tabletten', 'magen', 'sodbrennen', 'verdauung'],
},
{
id: 'kaiser-natron-bad-500-g',
brand: 'Kaiser-Natron',
title: 'Kaiser-Natron® Bad',
size: '500 g',
category: 'Körperpflege',
price: 5.49,
image: '/products/kaiser-natron-bad-500-g.jpg',
href: '/shop/kaiser-natron-bad-500-g',
inStock: true,
keywords: ['basenbad', 'wellness', 'entspannung', 'haut', 'wanne'],
},
{
id: 'kaiser-natron-fussbad-500-g',
brand: 'Kaiser-Natron',
title: 'Kaiser-Natron® Fußbad',
size: '500 g',
category: 'Körperpflege',
price: 5.49,
image: '/products/kaiser-natron-fussbad-500-g.jpg',
href: '/shop/kaiser-natron-fussbad-500-g',
inStock: true,
keywords: ['fusspflege', 'basenbad', 'wellness', 'entspannung'],
},
{
id: 'kaiser-natron-daunenwasch-250-ml',
brand: 'Kaiser-Natron',
title: 'Kaiser-Natron® Daunenwasch',
size: '250 ml',
category: 'Wäsche',
price: 6.9,
image: '/products/kaiser-natron-daunenwasch-250-ml.jpg',
href: '/shop/kaiser-natron-daunenwasch-250-ml',
inStock: true,
keywords: ['daunen', 'wäsche', 'bettdecke', 'kissen', 'pflege'],
},
{
id: 'kaiser-natron-sport-profi-250-ml',
brand: 'Kaiser-Natron',
title: 'Kaiser-Natron® Sport Profi',
size: '250 ml',
category: 'Sport',
price: 7.9,
image: '/products/kaiser-natron-sport-profi-250-ml.jpg',
href: '/shop/kaiser-natron-sport-profi-250-ml',
inStock: true,
keywords: ['sport', 'wäsche', 'funktionskleidung', 'geruch'],
},
{
id: 'kaiser-natron-spuelmittel-500-ml',
brand: 'Kaiser-Natron',
title: 'Kaiser-Natron® Spülmittel',
size: '500 ml',
category: 'Reinigung',
price: 3.9,
image: '/products/kaiser-natron-spuelmittel-500-ml.jpg',
href: '/shop/kaiser-natron-spuelmittel-500-ml',
inStock: true,
keywords: ['geschirr', 'spülen', 'küche', 'reinigung'],
},
{
id: 'kaiser-natron-allzweck-reiniger-750-ml',
brand: 'Kaiser-Natron',
title: 'Kaiser-Natron® Allzweck-Reiniger',
size: '750 ml',
category: 'Reinigung',
price: 4.49,
image: '/products/kaiser-natron-allzweck-reiniger-750-ml.jpg',
href: '/shop/kaiser-natron-allzweck-reiniger-750-ml',
inStock: true,
keywords: ['reinigung', 'allzweck', 'universal', 'haushalt'],
},
{
id: 'kaiser-natron-allzweck-spray-500-ml',
brand: 'Kaiser-Natron',
title: 'Kaiser-Natron® Allzweck-Spray',
size: '500 ml',
category: 'Reinigung',
price: 4.9,
image: '/products/kaiser-natron-allzweck-spray-500-ml.jpg',
href: '/shop/kaiser-natron-allzweck-spray-500-ml',
inStock: true,
keywords: ['reinigung', 'allzweck', 'spray', 'haushalt'],
},
{
id: 'holste-wasch-soda-500-g-beutel',
brand: 'Holste',
title: 'Holste Wasch-Soda',
size: '500 g Beutel',
category: 'Wäsche',
price: 2.9,
image: '/products/holste-wasch-soda-500-g-beutel.jpg',
href: '/shop/holste-wasch-soda-500-g-beutel',
inStock: true,
keywords: ['soda', 'wasch-soda', 'waschen', 'natriumcarbonat'],
},
{
id: 'holste-handwaschpaste-500-ml',
brand: 'Holste',
title: 'Holste Handwaschpaste',
size: '500 ml',
category: 'Reinigung',
price: 6.9,
image: '/products/holste-handwaschpaste-500-ml.jpg',
href: '/shop/holste-handwaschpaste-500-ml',
inStock: true,
keywords: ['hände', 'werkstatt', 'arbeit', 'grobe verschmutzung'],
},
{
id: 'holste-kalk-und-urinsteinloeser-750-ml',
brand: 'Holste',
title: 'Holste Kalk- und Urinsteinlöser',
size: '750 ml',
category: 'Reinigung',
price: 5.9,
image: '/products/holste-kalk--und-urinsteinloeser-750-ml.jpg',
href: '/shop/holste-kalk-und-urinsteinloeser-750-ml',
inStock: true,
keywords: ['kalk', 'urinstein', 'bad', 'wc', 'toilette', 'entkalker'],
},
{
id: 'holste-reisstaerke-250-g-faltschachtel',
brand: 'Holste',
title: 'Holste Reisstärke',
size: '250 g Faltschachtel',
category: 'Wäsche',
price: 3.9,
image: '/products/holste-reisstaerke-250-g-faltschachtel.jpg',
href: '/shop/holste-reisstaerke-250-g-faltschachtel',
inStock: true,
keywords: ['stärke', 'reisstärke', 'wäsche', 'bügeln'],
},
{
id: 'holste-schmierseife-fluessig-1-l-flasche',
brand: 'Holste',
title: 'Holste Schmierseife flüssig',
size: '1 l Flasche',
category: 'Reinigung',
price: 6.49,
image: '/products/holste-schmierseife-fluessig-1-l-flasche.jpg',
href: '/shop/holste-schmierseife-fluessig-1-l-flasche',
inStock: true,
keywords: ['schmierseife', 'naturseife', 'boden', 'reinigung'],
},
{
id: 'holste-zitronensaeure-entkalker-fluessig-500-ml',
brand: 'Holste',
title: 'Holste Zitronensäure-Entkalker flüssig',
size: '500 ml',
category: 'Reinigung',
price: 4.9,
image: '/products/holste-zitronensaeure-entkalker-fluessig-500-ml.jpg',
href: '/shop/holste-zitronensaeure-entkalker-fluessig-500-ml',
inStock: true,
keywords: ['zitronensäure', 'entkalker', 'kalk', 'haushalt', 'küche'],
},
{
id: 'gazelle-waeschestaerke-1000-ml-flasche',
brand: 'Gazelle',
title: 'Gazelle Wäschestärke',
size: '1.000 ml Flasche',
category: 'Wäsche',
price: 5.9,
image: '/products/gazelle-waeschestaerke-1000-ml-flasche.jpg',
href: '/shop/gazelle-waeschestaerke-1000-ml-flasche',
inStock: true,
keywords: ['wäschestärke', 'stärke', 'bügeln', 'tischwäsche'],
},
{
id: 'gruene-tante-mit-quarzmehl-500-ml-dose',
brand: 'Grüne Tante',
title: 'Grüne Tante mit Quarzmehl',
size: '500 ml Dose',
category: 'Reinigung',
price: 7.9,
image: '/products/gruene-tante-mit-quarzmehl-500-ml-dose.jpg',
href: '/shop/gruene-tante-mit-quarzmehl-500-ml-dose',
inStock: true,
keywords: ['handreiniger', 'werkstatt', 'quarzmehl', 'grobe verschmutzung'],
},
{
id: 'linda-fleckenweg-200-ml-tube',
brand: 'Linda',
title: 'Linda Fleckenweg',
size: '200 ml Tube',
category: 'Wäsche',
price: 4.9,
image: '/products/linda-fleckenweg-200-ml-tube.jpg',
href: '/shop/linda-fleckenweg-200-ml-tube',
inStock: true,
keywords: ['fleck', 'fleckenentferner', 'vorbehandlung', 'wäsche'],
},
{
id: 'linda-handreiniger-der-kraftvolle-200-g-tube',
brand: 'Linda',
title: 'Linda Handreiniger Der Kraftvolle',
size: '200 g Tube',
category: 'Reinigung',
price: 5.9,
image: '/products/linda-handreiniger-der-kraftvolle-200-g-tube.jpg',
href: '/shop/linda-handreiniger-der-kraftvolle-200-g-tube',
inStock: true,
keywords: ['handreiniger', 'werkstatt', 'öl', 'fett'],
},
{
id: 'linda-neutral-375-ml-dose',
brand: 'Linda',
title: 'Linda Neutral',
size: '375 ml Dose',
category: 'Reinigung',
price: 5.49,
image: '/products/linda-neutral-375-ml-dose.jpg',
href: '/shop/linda-neutral-375-ml-dose',
inStock: true,
keywords: ['handreiniger', 'neutral', 'haut', 'empfindlich'],
},
]
// Normalize for search: lowercase + fold German diacritics so "Spülmittel"
// matches "spulmittel" and "großpackung" matches "grosspackung".
function normalize(input) {
if (!input) return ''
return String(input)
.toLowerCase()
.replace(/ß/g, 'ss')
.replace(/ä/g, 'ae')
.replace(/ö/g, 'oe')
.replace(/ü/g, 'ue')
.normalize('NFD')
.replace(/[̀-ͯ]/g, '')
}
function escapeRegExp(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}
// Ranked match: every query term must hit at least one field; fields have
// weights so a title hit outranks a keyword hit. Exact > startsWith >
// word-boundary > substring. Terms AND together (all must match something).
export function searchProducts(query, list = products, limit = 8) {
const q = normalize(query).trim()
if (!q) return []
const terms = q.split(/\s+/).filter(Boolean)
const scored = []
for (const product of list) {
const haystacks = [
{ text: normalize(product.title), weight: 3 },
{ text: normalize(product.brand), weight: 2.2 },
{ text: normalize(product.category), weight: 1.4 },
{ text: normalize(product.size), weight: 1.2 },
{ text: normalize((product.keywords || []).join(' ')), weight: 1 },
]
let total = 0
let allTermsMatched = true
for (const term of terms) {
const boundary = new RegExp(`\\b${escapeRegExp(term)}`)
let best = 0
for (const { text, weight } of haystacks) {
if (!text) continue
let s = 0
if (text === term) s = 100
else if (text.startsWith(term)) s = 80
else if (boundary.test(text)) s = 55
else if (text.includes(term)) s = 25
if (s * weight > best) best = s * weight
}
if (best === 0) {
allTermsMatched = false
break
}
total += best
}
if (allTermsMatched) scored.push({ product, score: total })
}
scored.sort((a, b) => b.score - a.score)
return scored.slice(0, limit).map((s) => s.product)
}
export function formatPrice(amount, currency = '€') {
const num = typeof amount === 'number' ? amount : Number(amount)
if (!Number.isFinite(num)) return ''
return `${currency} ${num.toFixed(2).replace('.', ',')}`
}

View File

@@ -3,6 +3,8 @@ import { ref, computed, watch, onBeforeUnmount } from 'vue'
import Logo from './Logo.vue' import Logo from './Logo.vue'
import Icon from './Icon.vue' import Icon from './Icon.vue'
import LanguageSwitcher from './LanguageSwitcher.vue' import LanguageSwitcher from './LanguageSwitcher.vue'
import Search from './Search.vue'
import { products as defaultProducts } from '@/api/index.js'
import { useI18n } from '@/i18n/index.js' import { useI18n } from '@/i18n/index.js'
const props = defineProps({ const props = defineProps({
@@ -32,12 +34,19 @@ const props = defineProps({
}, },
cartCount: { type: Number, default: 0 }, cartCount: { type: Number, default: 0 },
floatOnMobile: { type: Boolean, default: true }, floatOnMobile: { type: Boolean, default: true },
/**
* Catalogue the search overlay queries. Defaults to the frontend fixture
* so the navbar is usable on its own; callers pass in their own list once
* the product data is backend-driven.
*/
products: { type: Array, default: () => defaultProducts },
}) })
defineEmits(['cart', 'nav']) defineEmits(['cart', 'nav', 'select'])
const { t } = useI18n() const { t } = useI18n()
const menuOpen = ref(false) const menuOpen = ref(false)
const searchOpen = ref(false)
// Cart is always warm yellow — one consistent affordance on mobile and desktop, // Cart is always warm yellow — one consistent affordance on mobile and desktop,
// regardless of bar tone. // regardless of bar tone.
@@ -124,8 +133,16 @@ onBeforeUnmount(() => {
</nav> </nav>
</div> </div>
<!-- Right: language + cart (desktop only) --> <!-- Right: search + language + cart (desktop only) -->
<div class="hidden md:flex items-center gap-4"> <div class="hidden md:flex items-center gap-4">
<button
type="button"
:class="[tone.link, 'inline-flex items-center justify-center w-11 h-11 rounded-full transition-colors duration-base ease-out']"
:aria-label="t('ds.search.open')"
@click="searchOpen = true"
>
<Icon name="search" :size="20" />
</button>
<LanguageSwitcher :tone="variant" /> <LanguageSwitcher :tone="variant" />
<button <button
type="button" type="button"
@@ -142,6 +159,23 @@ onBeforeUnmount(() => {
</div> </div>
</div> </div>
<!-- Mobile floating search (bottom-left). Matches the bottom-right
cluster's safe-area handling so it clears the home indicator. -->
<div
v-if="floatOnMobile"
class="md:hidden fixed bottom-5 left-5 z-40"
style="padding-bottom: env(safe-area-inset-bottom);"
>
<button
type="button"
class="w-14 h-14 rounded-full bg-cream text-brand shadow-lg flex items-center justify-center transition-transform duration-base ease-out hover:-translate-y-0.5 active:translate-y-0"
:aria-label="t('ds.search.open')"
@click="searchOpen = true"
>
<Icon name="search" :size="22" :stroke-width="2" />
</button>
</div>
<!-- Mobile floating cluster (bottom-right) --> <!-- Mobile floating cluster (bottom-right) -->
<div <div
v-if="floatOnMobile" v-if="floatOnMobile"
@@ -236,5 +270,11 @@ onBeforeUnmount(() => {
</div> </div>
</Transition> </Transition>
</Teleport> </Teleport>
<Search
v-model="searchOpen"
:products="products"
@select="(p) => $emit('select', p)"
/>
</header> </header>
</template> </template>

View File

@@ -0,0 +1,394 @@
<script setup>
import { ref, computed, watch, nextTick, onBeforeUnmount } from 'vue'
import Icon from './Icon.vue'
import { useI18n } from '@/i18n/index.js'
const props = defineProps({
modelValue: { type: Boolean, default: false },
products: { type: Array, default: () => [] },
/**
* Surface tone. Default is 'brand' so the overlay reads as the site's
* primary affordance (pine green). Use 'paper' for light chrome, 'cream'
* for a warm-neutral alternative.
*/
tone: {
type: String,
default: 'brand',
validator: (t) => ['brand', 'paper', 'cream'].includes(t),
},
// Cap result count — keeps the list glance-able on both mobile and desktop.
limit: { type: Number, default: 12 },
// Empty-query state: show top N products so the panel never looks dead.
emptyPreview: { type: Number, default: 6 },
placeholder: { type: String, default: '' },
})
const emit = defineEmits(['update:modelValue', 'select'])
const { t } = useI18n()
// Tone-driven class bundles. Keep every interactive element colored from
// one place so swapping tones is a one-word change at the call site.
const tones = {
brand: {
surface: 'bg-brand text-cream',
border: 'border-cream-line',
inputIcon: 'text-cream/70',
input: 'text-cream placeholder:text-cream/50',
closeBtn: 'text-cream/70 hover:text-cream hover:bg-cream-wash',
hint: 'text-cream/60',
kbd: 'bg-cream-wash text-cream border-cream-line',
mediaBg: 'bg-cream',
title: 'text-cream',
meta: 'text-cream/70',
price: 'text-accent',
noResults: 'text-cream/70',
rowActive: 'bg-cream-wash',
rowHover: 'hover:bg-cream-wash/60',
eyebrowCream: true,
},
paper: {
surface: 'bg-paper text-ink',
border: 'border-line',
inputIcon: 'text-muted',
input: 'text-ink placeholder:text-muted',
closeBtn: 'text-muted hover:text-brand hover:bg-brand-wash',
hint: 'text-muted',
kbd: 'bg-cream text-ink border-line',
mediaBg: 'bg-cream',
title: 'text-ink',
meta: 'text-muted',
price: 'text-brand',
noResults: 'text-muted',
rowActive: 'bg-brand-soft-wash',
rowHover: 'hover:bg-brand-wash',
eyebrowCream: false,
},
cream: {
surface: 'bg-cream text-brand',
border: 'border-line',
inputIcon: 'text-brand/70',
input: 'text-ink placeholder:text-muted',
closeBtn: 'text-muted hover:text-brand hover:bg-brand-wash',
hint: 'text-muted',
kbd: 'bg-paper text-ink border-line',
mediaBg: 'bg-paper',
title: 'text-ink',
meta: 'text-muted',
price: 'text-brand',
noResults: 'text-muted',
rowActive: 'bg-brand-soft-wash',
rowHover: 'hover:bg-brand-wash',
eyebrowCream: false,
},
}
const toneClasses = computed(() => tones[props.tone])
const query = ref('')
const activeIndex = ref(0)
const inputRef = ref(null)
const listRef = ref(null)
// Normalize for search: lowercase, strip diacritics, expand ß so German
// users typing "grosspackung" still match "Großpackung". NFKD + combining-
// mark strip handles ü/ö/ä/é/ñ etc; ß isn't covered by NFKD so we do it
// by hand.
function normalize(s) {
return (s || '')
.toString()
.toLowerCase()
.normalize('NFKD')
.replace(/[̀-ͯ]/g, '')
.replace(/ß/g, 'ss')
}
// Token-based scoring across multiple weighted fields. Every token in the
// query has to land somewhere; prefix matches get a bonus so "nat" ranks
// "Natron" above a keyword hit.
function scoreProduct(p, tokens) {
if (!tokens.length) return 0
const fields = [
{ val: normalize(p.title), weight: 5 },
{ val: normalize(p.brand), weight: 3 },
{ val: normalize(p.size), weight: 2 },
{ val: normalize(p.category), weight: 2 },
{ val: (p.keywords || []).map(normalize).join(' '), weight: 3 },
{ val: normalize(p.sku), weight: 1 },
]
let score = 0
for (const tok of tokens) {
let hit = false
for (const f of fields) {
if (!f.val) continue
if (f.val.includes(tok)) {
hit = true
score += f.weight
if (f.val.startsWith(tok)) score += Math.ceil(f.weight / 2)
}
}
if (!hit) return 0
}
return score
}
const results = computed(() => {
const q = normalize(query.value).trim()
if (!q) return props.products.slice(0, props.emptyPreview)
const tokens = q.split(/\s+/).filter(Boolean)
return props.products
.map((p) => ({ p, score: scoreProduct(p, tokens) }))
.filter(({ score }) => score > 0)
.sort((a, b) => b.score - a.score)
.slice(0, props.limit)
.map(({ p }) => p)
})
watch(() => props.modelValue, async (open) => {
if (open) {
query.value = ''
activeIndex.value = 0
await nextTick()
inputRef.value?.focus()
}
})
watch(results, () => {
activeIndex.value = 0
})
// Scroll-lock the document while open so the overlay "feels" modal on mobile.
watch(() => props.modelValue, (open) => {
if (typeof document === 'undefined') return
document.documentElement.style.overflow = open ? 'hidden' : ''
})
onBeforeUnmount(() => {
if (typeof document !== 'undefined') document.documentElement.style.overflow = ''
})
function close() { emit('update:modelValue', false) }
function selectAt(index) {
const item = results.value[index]
if (item) emit('select', item)
close()
}
function onKeydown(e) {
if (e.key === 'Escape') {
e.preventDefault()
close()
return
}
if (!results.value.length) return
if (e.key === 'ArrowDown') {
e.preventDefault()
activeIndex.value = (activeIndex.value + 1) % results.value.length
scrollActiveIntoView()
} else if (e.key === 'ArrowUp') {
e.preventDefault()
activeIndex.value =
(activeIndex.value - 1 + results.value.length) % results.value.length
scrollActiveIntoView()
} else if (e.key === 'Enter') {
e.preventDefault()
selectAt(activeIndex.value)
}
}
function scrollActiveIntoView() {
nextTick(() => {
const node = listRef.value?.querySelector('[data-active="true"]')
node?.scrollIntoView({ block: 'nearest' })
})
}
function priceLabel(p) {
if (typeof p.price === 'number') {
return `${p.price.toFixed(2).replace('.', ',')}`
}
return p.price ? `${p.price}` : ''
}
// Real anchors still navigate; prevent the '#' fallback from hash-jumping.
function onRowClick(i, item, e) {
if (!item.href) e.preventDefault()
selectAt(i)
}
</script>
<template>
<Teleport to="body">
<Transition
enter-active-class="transition duration-base ease-out"
enter-from-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="transition duration-base ease-out"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<div
v-if="modelValue"
class="fixed inset-0 z-[60] font-sans"
role="dialog"
aria-modal="true"
:aria-label="t('ds.search.label')"
@keydown="onKeydown"
>
<!-- Backdrop (md+). Tap to dismiss. -->
<div
class="hidden md:block absolute inset-0 bg-ink/50"
@click="close"
/>
<!-- Panel: full-screen on mobile, centered modal on md+. -->
<Transition
enter-active-class="transition duration-slow ease-out"
enter-from-class="md:opacity-0 md:-translate-y-2 translate-y-4"
enter-to-class="md:opacity-100 md:translate-y-0 translate-y-0"
leave-active-class="transition duration-base ease-out"
leave-from-class="md:opacity-100 translate-y-0"
leave-to-class="md:opacity-0 translate-y-4"
appear
>
<div
v-show="modelValue"
:class="[
'relative flex flex-col h-full w-full',
'md:absolute md:left-1/2 md:top-[12vh] md:h-auto',
'md:-translate-x-1/2 md:max-h-[76vh] md:w-[min(640px,92vw)]',
'md:rounded-lg md:shadow-lg md:border',
toneClasses.surface,
toneClasses.border,
]"
>
<!-- Input row -->
<div
:class="[
'shrink-0 flex items-center gap-3 px-5 md:px-4 pt-5 md:pt-3 pb-3 border-b',
toneClasses.border,
]"
style="padding-top: calc(env(safe-area-inset-top) + 1.25rem);"
>
<Icon name="search" :size="20" :class="['shrink-0', toneClasses.inputIcon]" />
<input
ref="inputRef"
v-model="query"
type="search"
autocomplete="off"
autocorrect="off"
autocapitalize="none"
spellcheck="false"
enterkeyhint="search"
:placeholder="placeholder || t('ds.search.placeholder')"
:aria-label="t('ds.search.label')"
:class="[
'flex-1 min-w-0 bg-transparent border-0 outline-none text-[17px] md:text-[15px]',
toneClasses.input,
]"
/>
<button
type="button"
:class="[
'shrink-0 inline-flex items-center justify-center w-10 h-10 rounded-pill transition-colors',
toneClasses.closeBtn,
]"
:aria-label="t('menu.close')"
@click="close"
>
<Icon name="close" :size="20" />
</button>
</div>
<!-- Results -->
<div
ref="listRef"
class="flex-1 overflow-y-auto py-2"
style="padding-bottom: calc(env(safe-area-inset-bottom) + 0.5rem);"
role="listbox"
:aria-label="t('ds.search.results')"
>
<p
v-if="!query.trim() && results.length"
:class="[
'eyebrow px-5 md:px-4 pt-2 pb-1',
toneClasses.eyebrowCream ? 'text-cream/70' : '',
]"
>
{{ t('ds.search.suggested') }}
</p>
<p
v-if="!results.length"
:class="['px-5 md:px-4 py-10 text-center text-sm', toneClasses.noResults]"
>
{{ t('ds.search.noResults') }}
</p>
<a
v-for="(p, i) in results"
:key="p.sku"
:href="p.href || '#'"
role="option"
:aria-selected="i === activeIndex"
:data-active="i === activeIndex"
:class="[
'flex items-center gap-4 px-5 md:px-4 py-3 transition-colors',
i === activeIndex ? toneClasses.rowActive : toneClasses.rowHover,
]"
@mousemove="activeIndex = i"
@click="onRowClick(i, p, $event)"
>
<div
:class="[
'shrink-0 w-14 h-14 rounded-sm overflow-hidden flex items-center justify-center',
toneClasses.mediaBg,
]"
>
<img
v-if="p.image"
:src="p.image"
:alt="p.title"
loading="lazy"
decoding="async"
class="w-full h-full object-contain p-2"
/>
</div>
<div class="flex-1 min-w-0">
<p :class="['text-[15px] font-semibold leading-tight truncate', toneClasses.title]">
{{ p.title }}
</p>
<p v-if="p.size" :class="['text-[13px] truncate', toneClasses.meta]">
{{ p.size }}<span v-if="p.brand"> · {{ p.brand }}</span>
</p>
</div>
<span :class="['shrink-0 text-[14px] font-semibold', toneClasses.price]">{{ priceLabel(p) }}</span>
</a>
</div>
<!-- Keyboard hints desktop only. -->
<div
:class="[
'hidden md:flex shrink-0 items-center gap-4 px-4 py-2 border-t text-[11px]',
toneClasses.border,
toneClasses.hint,
]"
>
<span class="inline-flex items-center gap-1.5">
<kbd :class="['px-1.5 py-0.5 rounded-sm border font-mono text-[11px]', toneClasses.kbd]"></kbd>
<kbd :class="['px-1.5 py-0.5 rounded-sm border font-mono text-[11px]', toneClasses.kbd]"></kbd>
{{ t('ds.search.hint.navigate') }}
</span>
<span class="inline-flex items-center gap-1.5">
<kbd :class="['px-1.5 py-0.5 rounded-sm border font-mono text-[11px]', toneClasses.kbd]"></kbd>
{{ t('ds.search.hint.select') }}
</span>
<span class="inline-flex items-center gap-1.5">
<kbd :class="['px-1.5 py-0.5 rounded-sm border font-mono text-[11px]', toneClasses.kbd]">esc</kbd>
{{ t('ds.search.hint.close') }}
</span>
</div>
</div>
</Transition>
</div>
</Transition>
</Teleport>
</template>

View File

@@ -13,6 +13,17 @@ const de = {
'menu.open': 'Menü öffnen', 'menu.open': 'Menü öffnen',
'menu.close': 'Menü schließen', 'menu.close': 'Menü schließen',
'language.label': 'Sprache', 'language.label': 'Sprache',
'search.open': 'Suche öffnen',
'search.label': 'Produkte durchsuchen',
'search.placeholder': 'Produkte suchen …',
'search.clear': 'Suche zurücksetzen',
'search.results': 'Suchergebnisse',
'search.noResults': 'Keine Produkte gefunden',
'search.noResultsHint': 'Probiere eine andere Schreibweise oder einen allgemeineren Begriff.',
'search.emptyHint': 'Tippe, um Produkte zu finden z. B. „Natron Pulver" oder „Entkalker".',
'search.hint.navigate': 'Navigieren',
'search.hint.select': 'Auswählen',
'search.hint.close': 'Schließen',
// Design-system sidebar // Design-system sidebar
'ds.sidebar.brand': 'Marke', 'ds.sidebar.brand': 'Marke',
@@ -34,8 +45,25 @@ const de = {
'ds.nav.products': 'Produktkarten', 'ds.nav.products': 'Produktkarten',
'ds.nav.hero': 'Hero', 'ds.nav.hero': 'Hero',
'ds.nav.navbar': 'Navigation', 'ds.nav.navbar': 'Navigation',
'ds.nav.search': 'Suche',
'ds.nav.language': 'Sprachwahl', 'ds.nav.language': 'Sprachwahl',
// Search overlay
'ds.search.title': 'Suche',
'ds.search.description': 'Volltextsuche über den Produktkatalog mit Bildvorschau. Mobil als Vollbild-Overlay, am Desktop als zentrierte Kommando-Palette. Tastaturbedienbar und standardmäßig in der primären Markenfarbe.',
'ds.search.label': 'Produkte durchsuchen',
'ds.search.placeholder': 'Produkte durchsuchen…',
'ds.search.noResults': 'Keine Produkte gefunden.',
'ds.search.suggested': 'Vorschläge',
'ds.search.results': 'Ergebnisse',
'ds.search.open': 'Suche öffnen',
'ds.search.picked': 'Ausgewählt',
'ds.search.tryHint': 'Tipp: probier „natron", „kalk", „wäsche" oder „bad".',
'ds.search.integrationHint': 'Die Suche läuft über den im API-Layer (src/api/) exportierten Produktkatalog. Sobald das Backend steht, bleibt die Komponenten-Signatur unverändert.',
'ds.search.hint.navigate': 'Navigieren',
'ds.search.hint.select': 'Auswählen',
'ds.search.hint.close': 'Schließen',
// Common section eyebrows // Common section eyebrows
'ds.eyebrow.brand': 'Marke', 'ds.eyebrow.brand': 'Marke',
'ds.eyebrow.tokens': 'Tokens', 'ds.eyebrow.tokens': 'Tokens',
@@ -191,6 +219,12 @@ const de = {
'ds.hero.headline.b': 'was glänzen soll.', 'ds.hero.headline.b': 'was glänzen soll.',
'ds.hero.sub': 'Reinigt, backt und neutralisiert Gerüche. Die 250-g-Großpackung für den Haushalt, der sich auf das Original verlässt.', 'ds.hero.sub': 'Reinigt, backt und neutralisiert Gerüche. Die 250-g-Großpackung für den Haushalt, der sich auf das Original verlässt.',
// Search section
'ds.search.title': 'Produktsuche',
'ds.search.description': 'Suchknopf rechts in der Navbar (links der Sprachwahl) und links unten als Floating-Button auf dem Telefon. Klick öffnet ein Full-Screen-Overlay auf Mobilgeräten bzw. ein zentriertes Panel auf dem Desktop. Eingabefeld ist autofokussiert, Ergebnisliste aktualisiert sich beim Tippen, Pfeiltasten / Enter / Esc werden unterstützt.',
'ds.search.demo.label': 'Demo öffnen',
'ds.search.demo.hint': 'Probiere „Natron", „Pulver", „Entkalker" oder „Spray".',
// Navbar section // Navbar section
'ds.navbar.title': 'Navigation', 'ds.navbar.title': 'Navigation',
'ds.navbar.description': 'Logo + Nav + Warenkorb, in drei Flächentönen. Auf Mobilgeräten behält die obere Leiste das Logo, Menü + Warenkorb wechseln in ein ergonomisches bodennahes rechtes Floating-Cluster. Das Menü öffnet ein vollflächiges markengrünes Overlay.', 'ds.navbar.description': 'Logo + Nav + Warenkorb, in drei Flächentönen. Auf Mobilgeräten behält die obere Leiste das Logo, Menü + Warenkorb wechseln in ein ergonomisches bodennahes rechtes Floating-Cluster. Das Menü öffnet ein vollflächiges markengrünes Overlay.',
@@ -243,6 +277,17 @@ const en = {
'menu.open': 'Open menu', 'menu.open': 'Open menu',
'menu.close': 'Close menu', 'menu.close': 'Close menu',
'language.label': 'Language', 'language.label': 'Language',
'search.open': 'Open search',
'search.label': 'Search products',
'search.placeholder': 'Search products…',
'search.clear': 'Clear search',
'search.results': 'Search results',
'search.noResults': 'No products found',
'search.noResultsHint': 'Try a different spelling or a broader term.',
'search.emptyHint': 'Type to find products — e.g. "Natron powder" or "descaler".',
'search.hint.navigate': 'Navigate',
'search.hint.select': 'Select',
'search.hint.close': 'Close',
'ds.sidebar.brand': 'Brand', 'ds.sidebar.brand': 'Brand',
'ds.sidebar.tokens': 'Tokens', 'ds.sidebar.tokens': 'Tokens',
@@ -263,8 +308,24 @@ const en = {
'ds.nav.products': 'Product cards', 'ds.nav.products': 'Product cards',
'ds.nav.hero': 'Hero', 'ds.nav.hero': 'Hero',
'ds.nav.navbar': 'Navbar', 'ds.nav.navbar': 'Navbar',
'ds.nav.search': 'Search',
'ds.nav.language': 'Language', 'ds.nav.language': 'Language',
'ds.search.title': 'Search',
'ds.search.description': 'Full-text search across the product catalog with image previews. Full-screen overlay on mobile, centered command palette on desktop. Keyboard-navigable and brand-green by default.',
'ds.search.label': 'Search products',
'ds.search.placeholder': 'Search products…',
'ds.search.noResults': 'No products found.',
'ds.search.suggested': 'Suggestions',
'ds.search.results': 'Results',
'ds.search.open': 'Open search',
'ds.search.picked': 'Picked',
'ds.search.tryHint': 'Try: "natron", "limescale", "laundry", or "bath".',
'ds.search.integrationHint': 'Search queries the catalog exported from the project API layer (src/api/). When the real backend lands, the component signature stays the same.',
'ds.search.hint.navigate': 'Navigate',
'ds.search.hint.select': 'Select',
'ds.search.hint.close': 'Close',
'ds.eyebrow.brand': 'Brand', 'ds.eyebrow.brand': 'Brand',
'ds.eyebrow.tokens': 'Tokens', 'ds.eyebrow.tokens': 'Tokens',
'ds.eyebrow.components': 'Components', 'ds.eyebrow.components': 'Components',
@@ -406,6 +467,11 @@ const en = {
'ds.hero.headline.b': 'that should shine.', 'ds.hero.headline.b': 'that should shine.',
'ds.hero.sub': 'Cleans, bakes, and neutralises odours. The 250 g large pack for the household that trusts the original.', 'ds.hero.sub': 'Cleans, bakes, and neutralises odours. The 250 g large pack for the household that trusts the original.',
'ds.search.title': 'Product search',
'ds.search.description': 'Search trigger on the right of the navbar (left of the language switcher) and bottom-left as a floating button on mobile. Tapping it opens a full-screen overlay on mobile and a centered panel on desktop. The input auto-focuses, the results list updates as you type, and arrow keys / Enter / Esc are supported.',
'ds.search.demo.label': 'Open demo',
'ds.search.demo.hint': 'Try "Natron", "pulver", "entkalker" or "spray".',
'ds.navbar.title': 'Navbar', 'ds.navbar.title': 'Navbar',
'ds.navbar.description': 'Logo + nav + cart, in three surface tones. On mobile, the top bar keeps the logo and the menu + cart move to an ergonomic bottom-right floating cluster. The menu opens a full-screen brand-green overlay.', 'ds.navbar.description': 'Logo + nav + cart, in three surface tones. On mobile, the top bar keeps the logo and the menu + cart move to an ergonomic bottom-right floating cluster. The menu opens a full-screen brand-green overlay.',
'ds.navbar.tone': 'Navbar tone', 'ds.navbar.tone': 'Navbar tone',

View File

@@ -1,6 +1,7 @@
<script setup> <script setup>
import Navbar from '@/design-system/components/Navbar.vue' import Navbar from '@/design-system/components/Navbar.vue'
import Hero from '@/design-system/components/Hero.vue' import Hero from '@/design-system/components/Hero.vue'
import { products } from '@/api/products.js'
import { useI18n } from '@/i18n/index.js' import { useI18n } from '@/i18n/index.js'
const { t } = useI18n() const { t } = useI18n()
@@ -15,7 +16,7 @@ const imgPulver250 =
below the navbar takes the remaining space and vertically centers the below the navbar takes the remaining space and vertically centers the
hero, so the hero sits in the middle of (viewport - navbar). --> hero, so the hero sits in the middle of (viewport - navbar). -->
<div class="h-svh flex flex-col bg-brand overflow-hidden"> <div class="h-svh flex flex-col bg-brand overflow-hidden">
<Navbar variant="brand" layout="standard" :cart-count="0" /> <Navbar variant="brand" layout="standard" :cart-count="0" :products="products" />
<div class="flex-1 flex items-center"> <div class="flex-1 flex items-center">
<Hero <Hero

View File

@@ -36,6 +36,7 @@ const groups = computed(() => [
{ name: 'ds-products', label: t('ds.nav.products') }, { name: 'ds-products', label: t('ds.nav.products') },
{ name: 'ds-hero', label: t('ds.nav.hero') }, { name: 'ds-hero', label: t('ds.nav.hero') },
{ name: 'ds-navbar', label: t('ds.nav.navbar') }, { name: 'ds-navbar', label: t('ds.nav.navbar') },
{ name: 'ds-search', label: t('ds.nav.search') },
{ name: 'ds-language', label: t('ds.nav.language') }, { name: 'ds-language', label: t('ds.nav.language') },
], ],
}, },

View File

@@ -0,0 +1,82 @@
<script setup>
import { ref } from 'vue'
import SectionShell from './SectionShell.vue'
import Search from '@/design-system/components/Search.vue'
import Button from '@/design-system/components/Button.vue'
import Icon from '@/design-system/components/Icon.vue'
import { products, searchProducts } from '@/api/products.js'
import { useI18n } from '@/i18n/index.js'
const { t } = useI18n()
const demoOpen = ref(false)
// Static preview of a canned query, so the showcase communicates the
// output shape without requiring the reader to open the live overlay.
const previewQuery = 'natron pulver'
const previewResults = searchProducts(previewQuery, products, 4)
</script>
<template>
<SectionShell
:eyebrow="t('ds.eyebrow.components')"
:title="t('ds.search.title')"
:description="t('ds.search.description')"
>
<section>
<h2 class="eyebrow mb-5">{{ t('ds.heading.default') }}</h2>
<div class="rounded-md border border-line bg-paper p-6 flex flex-col sm:flex-row sm:items-center gap-4">
<Button variant="primary" @click="demoOpen = true">
<template #before>
<Icon name="search" :size="18" />
</template>
{{ t('ds.search.demo.label') }}
</Button>
<p class="text-[13px] text-muted">{{ t('ds.search.demo.hint') }}</p>
</div>
</section>
<section>
<h2 class="eyebrow mb-5">{{ t('ds.heading.variants') }} "{{ previewQuery }}"</h2>
<ul class="rounded-md border border-line bg-paper divide-y divide-line overflow-hidden">
<li
v-for="product in previewResults"
:key="product.id"
class="flex items-center gap-4 px-4 py-3"
>
<img
:src="product.image"
:alt="product.title"
loading="lazy"
decoding="async"
class="shrink-0 w-12 h-12 rounded-sm object-cover bg-cream"
/>
<div class="min-w-0 flex-1">
<p class="text-[14px] font-semibold text-ink truncate">{{ product.title }}</p>
<p class="text-[12px] text-muted truncate">{{ product.size }} · {{ product.category }}</p>
</div>
<span class="shrink-0 text-[13px] font-semibold text-brand"> {{ product.price.toFixed(2).replace('.', ',') }}</span>
</li>
</ul>
</section>
<section>
<h2 class="eyebrow mb-5">{{ t('ds.heading.usage') }}</h2>
<div class="rounded-md border border-line bg-paper p-6 font-mono text-[12px] text-ink">
<pre class="whitespace-pre-wrap">import { products } from '@/api/products.js'
import Search from '@/design-system/components/Search.vue'
const open = ref(false)
&lt;Search v-model:open="open" :products="products" /&gt;
// Navbar already renders its own Search overlay, so most callers
// just pass the catalogue in:
&lt;Navbar :products="products" /&gt;</pre>
</div>
</section>
<Search v-model:open="demoOpen" :products="products" />
</SectionShell>
</template>

View File

@@ -39,6 +39,7 @@ const routes = [
{ path: 'navbar', name: 'ds-navbar', component: () => import('@/pages/design/NavbarSection.vue') }, { path: 'navbar', name: 'ds-navbar', component: () => import('@/pages/design/NavbarSection.vue') },
{ path: 'language', name: 'ds-language', component: () => import('@/pages/design/LanguageSwitcherSection.vue') }, { path: 'language', name: 'ds-language', component: () => import('@/pages/design/LanguageSwitcherSection.vue') },
{ path: 'icons', name: 'ds-icons', component: () => import('@/pages/design/IconsSection.vue') }, { path: 'icons', name: 'ds-icons', component: () => import('@/pages/design/IconsSection.vue') },
{ path: 'search', name: 'ds-search', component: () => import('@/pages/design/SearchSection.vue') },
], ],
}, },
] ]