Initial Next.js 14 project — HyggeCraftery web
- App Router mit TypeScript & Tailwind CSS - Landing Page mit Hero, Über uns, Shop-Teaser, Apps-Teaser, Newsletter - /shop mit 6 Platzhalter-Produkten - /apps Übersicht mit Snakkimo, Fittimo, Rezeptimo - Nav mit Apps-Dropdown, Footer - Design System: Cormorant Garamond + Mulish, Markenfarben - lib/shopify.ts als Platzhalter für Storefront API - Hero-Bild aus ZIP übernommen Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
95
components/ui/Footer.tsx
Normal file
95
components/ui/Footer.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer style={{ background: '#3D2B1F', color: '#FAFAF7', padding: '0 48px 48px' }}>
|
||||
<div style={{ maxWidth: 1240, margin: '0 auto', borderTop: '1px solid rgba(250,250,247,0.14)', paddingTop: 56 }}>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1.6fr 1fr 1fr 1fr', gap: 40, paddingBottom: 52 }}>
|
||||
|
||||
{/* Brand */}
|
||||
<div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 11, marginBottom: 18 }}>
|
||||
<div style={{ width: 30, height: 30, borderRadius: '50% 50% 50% 10px', background: '#A8B89A' }} />
|
||||
<span style={{ fontFamily: 'var(--font-cormorant), "Cormorant Garamond", serif', fontSize: 23, fontWeight: 600, color: '#FAFAF7' }}>
|
||||
HyggeCraftery
|
||||
</span>
|
||||
</div>
|
||||
<p style={{ fontSize: 14.5, color: 'rgba(250,250,247,0.55)', maxWidth: 280, margin: 0 }}>
|
||||
Skandinavisches Lifestyle-Handwerk für ein langsameres, schöneres Leben. Mit Liebe gemacht in Schweden.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Entdecken */}
|
||||
<div>
|
||||
<h4 style={{ fontSize: 13, fontWeight: 700, letterSpacing: 1.4, textTransform: 'uppercase', color: '#A8B89A', margin: '0 0 18px' }}>
|
||||
Entdecken
|
||||
</h4>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
<FooterLink href="/shop">Shop</FooterLink>
|
||||
<FooterLink href="/apps">Apps</FooterLink>
|
||||
<FooterLink href="/#ueber">Über uns</FooterLink>
|
||||
<FooterLink href="/#newsletter">Newsletter</FooterLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Service */}
|
||||
<div>
|
||||
<h4 style={{ fontSize: 13, fontWeight: 700, letterSpacing: 1.4, textTransform: 'uppercase', color: '#A8B89A', margin: '0 0 18px' }}>
|
||||
Service
|
||||
</h4>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
<FooterLink href="#">Versand</FooterLink>
|
||||
<FooterLink href="#">Kontakt</FooterLink>
|
||||
<FooterLink href="#">Impressum</FooterLink>
|
||||
<FooterLink href="#">Datenschutz</FooterLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Social */}
|
||||
<div>
|
||||
<h4 style={{ fontSize: 13, fontWeight: 700, letterSpacing: 1.4, textTransform: 'uppercase', color: '#A8B89A', margin: '0 0 18px' }}>
|
||||
Folgen
|
||||
</h4>
|
||||
<div style={{ display: 'flex', gap: 12 }}>
|
||||
{[['Ig', 'Instagram'], ['Pin', 'Pinterest'], ['Jo', 'Journal']].map(([abbr, label]) => (
|
||||
<a
|
||||
key={abbr}
|
||||
href="#"
|
||||
title={label}
|
||||
style={{
|
||||
textDecoration: 'none',
|
||||
width: 42,
|
||||
height: 42,
|
||||
borderRadius: '50% 50% 50% 12px',
|
||||
background: 'rgba(250,250,247,0.08)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: 13,
|
||||
fontWeight: 600,
|
||||
color: '#FAFAF7',
|
||||
}}
|
||||
>
|
||||
{abbr}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ borderTop: '1px solid rgba(250,250,247,0.14)', paddingTop: 28, display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: 14 }}>
|
||||
<span style={{ fontSize: 13.5, color: 'rgba(250,250,247,0.45)' }}>© 2026 HyggeCraftery AB · Göteborg, Sverige</span>
|
||||
<span style={{ fontSize: 13.5, color: 'rgba(250,250,247,0.45)' }}>Leben. Langsam. Schön.</span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
||||
function FooterLink({ href, children }: { href: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<Link href={href} style={{ textDecoration: 'none', fontSize: 14.5, color: 'rgba(250,250,247,0.7)' }}>
|
||||
{children}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
146
components/ui/Nav.tsx
Normal file
146
components/ui/Nav.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import { useState } from 'react'
|
||||
|
||||
const apps = [
|
||||
{ name: 'Snakkimo', href: '/apps/snakkimo', accent: '#7BA7BC' },
|
||||
{ name: 'Fittimo', href: '/apps/fittimo', accent: '#7DAF8A' },
|
||||
{ name: 'Rezeptimo', href: '/apps/rezeptimo', accent: '#C4896A' },
|
||||
]
|
||||
|
||||
export default function Nav() {
|
||||
const [appsOpen, setAppsOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<header
|
||||
style={{
|
||||
position: 'relative',
|
||||
zIndex: 100,
|
||||
maxWidth: 1240,
|
||||
margin: '0 auto',
|
||||
padding: '34px 48px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
{/* Logo */}
|
||||
<Link href="/" style={{ textDecoration: 'none', display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<div
|
||||
style={{
|
||||
width: 34,
|
||||
height: 34,
|
||||
borderRadius: '50% 50% 50% 12px',
|
||||
background: '#A8B89A',
|
||||
}}
|
||||
/>
|
||||
<span
|
||||
style={{
|
||||
fontFamily: 'var(--font-cormorant), "Cormorant Garamond", serif',
|
||||
fontSize: 25,
|
||||
fontWeight: 600,
|
||||
letterSpacing: 0.5,
|
||||
color: '#3D2B1F',
|
||||
}}
|
||||
>
|
||||
HyggeCraftery
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
{/* Nav links */}
|
||||
<nav style={{ display: 'flex', alignItems: 'center', gap: 38 }}>
|
||||
<Link href="/#ueber" style={navLinkStyle}>Über uns</Link>
|
||||
<Link href="/shop" style={navLinkStyle}>Shop</Link>
|
||||
|
||||
{/* Apps dropdown */}
|
||||
<div
|
||||
style={{ position: 'relative' }}
|
||||
onMouseEnter={() => setAppsOpen(true)}
|
||||
onMouseLeave={() => setAppsOpen(false)}
|
||||
>
|
||||
<button
|
||||
style={{ ...navLinkStyle, background: 'none', border: 'none', cursor: 'pointer', padding: 0 }}
|
||||
onClick={() => setAppsOpen((v) => !v)}
|
||||
aria-expanded={appsOpen}
|
||||
>
|
||||
Apps
|
||||
</button>
|
||||
|
||||
{appsOpen && (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '100%',
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
marginTop: 12,
|
||||
background: '#FAFAF7',
|
||||
border: '1.5px solid #F5F0E8',
|
||||
borderRadius: 20,
|
||||
padding: '8px 0',
|
||||
minWidth: 160,
|
||||
boxShadow: '0 8px 32px rgba(61,43,31,0.10)',
|
||||
zIndex: 200,
|
||||
}}
|
||||
>
|
||||
{apps.map((app) => (
|
||||
<Link
|
||||
key={app.name}
|
||||
href={app.href}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
padding: '10px 20px',
|
||||
textDecoration: 'none',
|
||||
color: '#3D2B1F',
|
||||
fontSize: 14.5,
|
||||
fontWeight: 500,
|
||||
}}
|
||||
onClick={() => setAppsOpen(false)}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: '50%',
|
||||
background: app.accent,
|
||||
flexShrink: 0,
|
||||
}}
|
||||
/>
|
||||
{app.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href="/#newsletter"
|
||||
style={{
|
||||
textDecoration: 'none',
|
||||
color: '#FAFAF7',
|
||||
background: '#3D2B1F',
|
||||
fontSize: 14,
|
||||
fontWeight: 600,
|
||||
letterSpacing: 0.3,
|
||||
padding: '11px 22px',
|
||||
borderRadius: 40,
|
||||
}}
|
||||
>
|
||||
Newsletter
|
||||
</Link>
|
||||
</nav>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
const navLinkStyle: React.CSSProperties = {
|
||||
textDecoration: 'none',
|
||||
color: '#3D2B1F',
|
||||
fontSize: 14.5,
|
||||
fontWeight: 500,
|
||||
letterSpacing: 0.3,
|
||||
opacity: 0.78,
|
||||
}
|
||||
65
components/ui/NewsletterForm.tsx
Normal file
65
components/ui/NewsletterForm.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
|
||||
export default function NewsletterForm() {
|
||||
const [email, setEmail] = useState('')
|
||||
const [submitted, setSubmitted] = useState(false)
|
||||
|
||||
if (submitted) {
|
||||
return (
|
||||
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 12, background: '#F5F0E8', borderRadius: 40, padding: '16px 28px' }}>
|
||||
<span style={{ width: 26, height: 26, borderRadius: '50%', background: '#A8B89A', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', color: '#FAFAF7', fontSize: 14 }}>
|
||||
✓
|
||||
</span>
|
||||
<span style={{ fontSize: 15, fontWeight: 500, color: '#3D2B1F' }}>Tack så mycket! Willkommen in der Ruhe.</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
if (email.trim()) setSubmitted(true)
|
||||
}}
|
||||
style={{ display: 'flex', gap: 12, maxWidth: 480, margin: '0 auto', flexWrap: 'wrap' }}
|
||||
>
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="Deine E-Mail-Adresse"
|
||||
required
|
||||
style={{
|
||||
flex: 1,
|
||||
minWidth: 200,
|
||||
fontFamily: 'inherit',
|
||||
fontSize: 15,
|
||||
color: '#3D2B1F',
|
||||
background: '#F5F0E8',
|
||||
border: '1.5px solid transparent',
|
||||
borderRadius: 40,
|
||||
padding: '15px 24px',
|
||||
outline: 'none',
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
style={{
|
||||
fontFamily: 'inherit',
|
||||
fontSize: 15,
|
||||
fontWeight: 600,
|
||||
color: '#FAFAF7',
|
||||
background: '#C4896A',
|
||||
border: 'none',
|
||||
borderRadius: 40,
|
||||
padding: '15px 30px',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
Anmelden
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user