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:
@@ -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};
|
||||||
@@ -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};
|
||||||
@@ -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};
|
||||||
@@ -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};
|
||||||
1
dist/assets/DesignLayout-BgcQwikW.css
vendored
1
dist/assets/DesignLayout-BgcQwikW.css
vendored
@@ -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
1
dist/assets/DesignLayout-COYFKSYJ.css
vendored
Normal 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
1
dist/assets/DesignLayout-Dj2IRTg_.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/DesignLayout-H92PtQVS.js
vendored
1
dist/assets/DesignLayout-H92PtQVS.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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};
|
||||||
@@ -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
1
dist/assets/HomePage-BzDuqA9T.js
vendored
Normal 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};
|
||||||
1
dist/assets/HomePage-di6JY8ls.js
vendored
1
dist/assets/HomePage-di6JY8ls.js
vendored
@@ -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};
|
|
||||||
@@ -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};
|
||||||
@@ -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};
|
||||||
@@ -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};
|
||||||
@@ -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};
|
||||||
@@ -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};
|
||||||
1
dist/assets/Navbar-DZE5noW0.js
vendored
1
dist/assets/Navbar-DZE5noW0.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/Navbar-ie3UVUXi.js
vendored
Normal file
1
dist/assets/Navbar-ie3UVUXi.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -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};
|
||||||
@@ -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' }]"
|
||||||
@@ -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"
|
||||||
@@ -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
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
11
dist/assets/SearchSection-DyxYPxDM.js
vendored
Normal 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};
|
||||||
@@ -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};
|
||||||
@@ -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};
|
||||||
1
dist/assets/i18n-B16vzzU0.js
vendored
1
dist/assets/i18n-B16vzzU0.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/i18n-DUHlbRTU.js
vendored
Normal file
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
1
dist/assets/index-BtCKAUbd.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/index-DZsInX8d.css
vendored
1
dist/assets/index-DZsInX8d.css
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/products-BJebky8H.js
vendored
Normal file
1
dist/assets/products-BJebky8H.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/runtime-dom.esm-bundler-CDZvOKZN.js
vendored
Normal file
1
dist/assets/runtime-dom.esm-bundler-CDZvOKZN.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
6
dist/index.html
vendored
6
dist/index.html
vendored
@@ -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>
|
||||||
|
|||||||
260
src/api/fixtures/products.js
Normal file
260
src/api/fixtures/products.js
Normal 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
4
src/api/index.js
Normal 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
340
src/api/products.js
Normal 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('.', ',')}`
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
394
src/design-system/components/Search.vue
Normal file
394
src/design-system/components/Search.vue
Normal 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>
|
||||||
@@ -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',
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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') },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
82
src/pages/design/SearchSection.vue
Normal file
82
src/pages/design/SearchSection.vue
Normal 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)
|
||||||
|
|
||||||
|
<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" /></pre>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Search v-model:open="demoOpen" :products="products" />
|
||||||
|
</SectionShell>
|
||||||
|
</template>
|
||||||
@@ -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') },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user