feat: redesign Hero block, update global design tokens and fonts to new editorial direction

This commit is contained in:
MichalC
2026-09-02 23:41:09 +02:00
parent b9f1d77053
commit 61fd266f5d
5 changed files with 79 additions and 111 deletions
+9 -4
View File
@@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import { Inter, Outfit } from 'next/font/google' import { Public_Sans, Fraunces } from 'next/font/google'
import { Providers } from '../../components/Providers' import { Providers } from '../../components/Providers'
import { LanguageSlugsProvider } from '../../components/LanguageSlugsProvider' import { LanguageSlugsProvider } from '../../components/LanguageSlugsProvider'
import { Header } from '../../components/Header' import { Header } from '../../components/Header'
@@ -8,8 +8,13 @@ import { getPayload } from 'payload'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import '../globals.css' import '../globals.css'
const inter = Inter({ subsets: ['latin'], variable: '--font-sans' }) const publicSans = Public_Sans({ subsets: ['latin'], variable: '--font-sans' })
const outfit = Outfit({ subsets: ['latin'], variable: '--font-heading' }) const fraunces = Fraunces({
subsets: ['latin'],
variable: '--font-heading',
axes: ['opsz'],
weight: 'variable',
})
export const metadata = { export const metadata = {
title: 'Finanz- & Lohnbuchhaltung Dipl.-Ing. Janusz Kędzierski', title: 'Finanz- & Lohnbuchhaltung Dipl.-Ing. Janusz Kędzierski',
@@ -50,7 +55,7 @@ export default async function RootLayout({
const footerNavItems = (footerData.navigation || []).map((item: any) => item.page).filter((page: any) => typeof page === 'object' && page !== null); const footerNavItems = (footerData.navigation || []).map((item: any) => item.page).filter((page: any) => typeof page === 'object' && page !== null);
return ( return (
<html lang="pl" suppressHydrationWarning className={`${inter.variable} ${outfit.variable}`}> <html lang="pl" suppressHydrationWarning className={`${publicSans.variable} ${fraunces.variable}`}>
<body> <body>
<Providers> <Providers>
<LanguageSlugsProvider> <LanguageSlugsProvider>
+16 -18
View File
@@ -10,35 +10,33 @@
--color-card: var(--card-color); --color-card: var(--card-color);
--color-border: var(--border-color); --color-border: var(--border-color);
--font-sans: 'Inter', sans-serif; --font-sans: 'Public Sans', sans-serif;
--font-heading: 'Outfit', sans-serif; --font-heading: 'Fraunces', serif;
} }
:root { :root {
/* Jasny motyw */ /* Jasny motyw */
--bg-color: #FAFAFA; --bg-color: #F7F5F1;
--fg-color: #18181B; --fg-color: #1C1B1A;
--card-color: #FFFFFF; --card-color: #FFFFFF;
--border-color: #E4E4E7; --border-color: #E2DDD3;
/* Ciemniejszy złoty/ochra dla zachowania kontrastu na jasnym tle */ --accent-color: #A13D2C;
--accent-color: #7B5C35; --accent-hover-color: #8A3324;
--accent-hover-color: #614828;
/* Globalny token dla przycisków prostokątnych (umiarkowane zaokrąglenie, nie pill) */ /* Globalny token dla przycisków prostokątnych (umiarkowane zaokrąglenie, nie pill) */
--radius-button: 6px; --radius-button: 6px;
} }
[data-theme="dark"] { [data-theme="dark"] {
/* Ciemny motyw: czysty, głęboki antracyt (bez niebieskiego odcienia) */ /* Ciemny motyw */
--bg-color: #121212; --bg-color: #17140F;
--fg-color: #F8FAFC; --fg-color: #F5F1E8;
--card-color: #18181B; --card-color: #1F1B14;
--border-color: #27272A; --border-color: #332C21;
/* Jasny złoty/ochra dla zachowania kontrastu na ciemnym tle */ --accent-color: #D4634A;
--accent-color: #C5A880; --accent-hover-color: #E17B62;
--accent-hover-color: #D4B995;
} }
/* Globalne style dla HTML i Body */ /* Globalne style dla HTML i Body */
@@ -57,7 +55,7 @@ body {
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {
font-family: var(--font-heading); font-family: var(--font-heading);
font-weight: 600; font-weight: 500;
letter-spacing: -0.02em; letter-spacing: -0.02em;
} }
@@ -68,7 +66,7 @@ h1, h2, h3, h4, h5, h6 {
} }
.btn-primary:hover { .btn-primary:hover {
background-color: var(--accent-hover-color); background-color: var(--accent-hover-color);
@apply shadow-lg transform -translate-y-0.5; @apply shadow-lg;
} }
/* Utility dla szerokich kontenerów i dużego oddechu (whitespace) */ /* Utility dla szerokich kontenerów i dużego oddechu (whitespace) */
+32 -67
View File
@@ -1,78 +1,29 @@
'use client' 'use client'
import React, { useState, useEffect } from 'react' import React from 'react'
import type { Page } from '../../payload-types' import type { Page, Media } from '../../payload-types'
import Link from 'next/link' import Link from 'next/link'
import Image from 'next/image' import Image from 'next/image'
import { ArrowRight } from 'lucide-react'
type Props = Extract<NonNullable<Page['layout']>[0], { blockType: 'hero' }> type Props = Extract<NonNullable<Page['layout']>[0], { blockType: 'hero' }>
/* PLACEHOLDER - do podmiany na realne zdjęcia klienta */ export const HeroBlock: React.FC<Props> = ({ title, subtitle, primaryCta, secondaryCta, image }) => {
const heroImages = [ // Sprawdzamy czy obrazek jest obiektem Media czy tylko ID
{ const heroMedia = typeof image === 'object' && image !== null ? (image as Media) : null;
url: 'https://images.unsplash.com/photo-1554224155-8d04cb21cd6c?auto=format&fit=crop&w=2000&q=80', // PLACEHOLDER - do podmiany na zdjęcie klienta jeśli brak w CMS
alt: 'Dokumenty podatkowe i praca biurowa', const imageUrl = heroMedia?.url || 'https://images.unsplash.com/photo-1554224155-8d04cb21cd6c?auto=format&fit=crop&w=1200&q=80';
}, const imageAlt = heroMedia?.alt || 'Doradztwo podatkowe - biuro';
{
url: 'https://images.unsplash.com/photo-1497366216548-37526070297c?auto=format&fit=crop&w=2000&q=80',
alt: 'Profesjonalne biuro doradztwa podatkowego',
},
{
url: 'https://images.unsplash.com/photo-1486312338219-ce68d2c6f44d?auto=format&fit=crop&w=2000&q=80',
alt: 'Praca z rozliczeniami podatkowymi',
},
]
export const HeroBlock: React.FC<Props> = ({ title, subtitle, primaryCta, secondaryCta, yearsOfExperience }) => {
const [currentSlide, setCurrentSlide] = useState(0)
useEffect(() => {
const timer = setInterval(() => {
setCurrentSlide((prev) => (prev + 1) % heroImages.length)
}, 6000)
return () => clearInterval(timer)
}, [])
return ( return (
<section className="relative w-full min-h-[90vh] flex items-end bg-background text-foreground pb-20 md:pb-32 pt-40"> <section className="w-full bg-background text-foreground pt-32 pb-16 md:pt-48 md:pb-24">
{/* Pełnoekranowe tło ze sliderem */} <div className="container-premium">
<div className="absolute inset-0 z-0 overflow-hidden"> <div className="flex flex-col-reverse md:flex-row items-center justify-between gap-12 lg:gap-24">
{heroImages.map((img, idx) => (
<div
key={idx}
className={`absolute inset-0 transition-opacity duration-1000 ease-in-out ${
idx === currentSlide ? 'opacity-100' : 'opacity-0 pointer-events-none'
}`}
>
{/* PLACEHOLDER - do podmiany na realne zdjęcia klienta */}
<Image
src={img.url}
alt={img.alt}
fill
priority={idx === 0}
sizes="100vw"
className="object-cover object-center"
/>
</div>
))}
{/* Potężny, gładki overlay z koloru tła aplikacji od dołu i od lewej, dla idealnej czytelności */}
<div className="absolute inset-0 bg-gradient-to-t from-background via-background/80 to-transparent" />
<div className="absolute inset-0 bg-gradient-to-r from-background via-background/40 to-transparent" />
</div>
<div className="container-premium relative z-10"> <div className="w-full md:w-1/2 max-w-xl flex flex-col">
<div className="max-w-4xl"> <h1 className="font-heading text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold leading-[1.05] text-foreground mb-8 text-balance">
{yearsOfExperience && (
<div className="font-mono text-sm md:text-base font-bold text-accent tracking-widest uppercase mb-4">
// {yearsOfExperience}
</div>
)}
<h1 className="font-heading text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-extrabold tracking-tight leading-[1.05] text-foreground mb-8 text-balance">
{title} {title}
</h1> </h1>
<p className="text-xl sm:text-2xl text-foreground/80 mb-12 leading-relaxed max-w-2xl font-light"> <p className="text-xl sm:text-2xl text-foreground/80 mb-12 leading-relaxed font-light">
{subtitle} {subtitle}
</p> </p>
@@ -81,21 +32,35 @@ export const HeroBlock: React.FC<Props> = ({ title, subtitle, primaryCta, second
href={process.env.NEXT_PUBLIC_WHATSAPP_LINK || '#'} href={process.env.NEXT_PUBLIC_WHATSAPP_LINK || '#'}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="px-8 py-4 bg-accent hover:bg-accent/90 text-background font-bold text-base sm:text-lg transition-colors flex items-center justify-center rounded-[var(--radius-button)]" className="btn-primary"
> >
{primaryCta} {primaryCta}
</a> </a>
{secondaryCta?.text && secondaryCta?.url && ( {secondaryCta?.text && secondaryCta?.url && (
<Link <Link
href={secondaryCta.url} href={secondaryCta.url}
className="group flex items-center gap-3 text-foreground font-semibold text-lg hover:text-accent transition-colors" className="text-foreground font-semibold text-lg hover:text-accent hover:underline underline-offset-4 transition-colors"
> >
<span>{secondaryCta.text}</span> {secondaryCta.text}
<ArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
</Link> </Link>
)} )}
</div> </div>
</div> </div>
<div className="w-full md:w-1/2 flex justify-center md:justify-end">
<div className="relative w-full aspect-[4/5] max-w-md">
<Image
src={imageUrl}
alt={imageAlt}
fill
priority
sizes="(max-width: 768px) 100vw, 50vw"
className="object-cover object-center"
/>
</div>
</div>
</div>
</div> </div>
</section> </section>
) )
+1 -1
View File
@@ -14,6 +14,6 @@ export const Hero: Block = {
{ name: 'url', type: 'text' }, { name: 'url', type: 'text' },
], ],
}, },
{ name: 'yearsOfExperience', type: 'text', localized: true }, { name: 'image', type: 'upload', relationTo: 'media', required: true },
], ],
} }
+2 -2
View File
@@ -261,7 +261,7 @@ export interface Page {
text?: string | null; text?: string | null;
url?: string | null; url?: string | null;
}; };
yearsOfExperience?: string | null; image: number | Media;
id?: string | null; id?: string | null;
blockName?: string | null; blockName?: string | null;
blockType: 'hero'; blockType: 'hero';
@@ -624,7 +624,7 @@ export interface PagesSelect<T extends boolean = true> {
text?: T; text?: T;
url?: T; url?: T;
}; };
yearsOfExperience?: T; image?: T;
id?: T; id?: T;
blockName?: T; blockName?: T;
}; };