diff --git a/app/[locale]/(site)/about/page.tsx b/app/[locale]/(site)/about/page.tsx index f922d6c..a5ecc29 100644 --- a/app/[locale]/(site)/about/page.tsx +++ b/app/[locale]/(site)/about/page.tsx @@ -36,7 +36,12 @@ export default async function AboutPage({ params: { locale } }: AboutPageProps) return ( <> - + diff --git a/app/[locale]/(site)/contact/page.tsx b/app/[locale]/(site)/contact/page.tsx index 05e57f8..1496380 100644 --- a/app/[locale]/(site)/contact/page.tsx +++ b/app/[locale]/(site)/contact/page.tsx @@ -50,7 +50,12 @@ export default async function ContactPage({ return ( <> - + diff --git a/app/[locale]/(site)/portfolio/page.tsx b/app/[locale]/(site)/portfolio/page.tsx index 768bf13..c15e1ac 100644 --- a/app/[locale]/(site)/portfolio/page.tsx +++ b/app/[locale]/(site)/portfolio/page.tsx @@ -57,7 +57,12 @@ export default async function PortfolioPage({ return ( <> - +
diff --git a/app/[locale]/(site)/products/page.tsx b/app/[locale]/(site)/products/page.tsx index 2b5474f..03dafe5 100644 --- a/app/[locale]/(site)/products/page.tsx +++ b/app/[locale]/(site)/products/page.tsx @@ -38,7 +38,12 @@ export default async function ProductsPage({ params: { locale } }: ProductsPageP return ( <> - +
diff --git a/components/layout/hero-badge.tsx b/components/layout/hero-badge.tsx new file mode 100644 index 0000000..662dc32 --- /dev/null +++ b/components/layout/hero-badge.tsx @@ -0,0 +1,23 @@ +import type { ReactNode } from "react"; + +import { cn } from "@/lib/utils"; + +type HeroBadgeProps = { + children: ReactNode; + className?: string; + trailing?: ReactNode; +}; + +export function HeroBadge({ children, className, trailing }: HeroBadgeProps) { + return ( +

+ {children} + {trailing} +

+ ); +} diff --git a/components/layout/home-hero.tsx b/components/layout/home-hero.tsx index fe35dcb..526a0e2 100644 --- a/components/layout/home-hero.tsx +++ b/components/layout/home-hero.tsx @@ -2,6 +2,7 @@ import { motion, useReducedMotion } from "framer-motion"; +import { HeroBadge } from "@/components/layout/hero-badge"; import { HeroContentMotion, HeroMotionItem, @@ -70,8 +71,8 @@ export function HomeHero({ -

- {kicker} +

+ } + > + {kicker} +
diff --git a/components/layout/page-hero.tsx b/components/layout/page-hero.tsx index 907371e..e9602cd 100644 --- a/components/layout/page-hero.tsx +++ b/components/layout/page-hero.tsx @@ -1,20 +1,30 @@ -import { HeroContentMotion, HeroMotionItem, HeroShell, HeroTitle } from "@/components/layout/site-hero"; +import { HeroBadge } from "@/components/layout/hero-badge"; +import { HeroContentMotion, HeroMotionItem, HeroShell, HeroTitle, type HeroTitleLineValue } from "@/components/layout/site-hero"; type PageHeroProps = { + badge?: string; title: string; description?: string; + lines?: HeroTitleLineValue[]; + locale?: string; }; -export function PageHero({ title, description }: PageHeroProps) { +function getDefaultLines(title: string): HeroTitleLineValue[] { + return title.split("\n").filter(Boolean).map((line, index) => ({ + text: line, + tone: index === 0 ? "soft" : "accent", + align: "center", + })); +} + +export function PageHero({ badge, title, description, lines, locale }: PageHeroProps) { return ( -

- {title} -

+ {badge ?? title}
- + {description ? (

diff --git a/components/layout/site-hero.tsx b/components/layout/site-hero.tsx index f03bd8c..008fd15 100644 --- a/components/layout/site-hero.tsx +++ b/components/layout/site-hero.tsx @@ -66,6 +66,8 @@ const lineAlignClasses: Record = { end: "self-center text-center lg:self-end lg:text-end", }; +export type HeroTitleLineValue = HeroTitleLine; + export function HeroShell({ children, className, @@ -189,9 +191,9 @@ export function HeroTitle({ "text-balance font-semibold text-[hsl(var(--hero-ink))]", variant === "home" ? isArabic - ? "mt-6 flex w-full max-w-[20rem] flex-col gap-y-1 text-[clamp(3rem,11vw,5.9rem)] leading-[1.06] tracking-[-0.03em] sm:max-w-[24rem] md:max-w-[30rem] lg:max-w-[32rem]" - : "mt-6 flex w-full max-w-[20rem] flex-col gap-y-1 text-[clamp(3.1rem,11vw,6.3rem)] leading-[0.94] tracking-[-0.06em] sm:max-w-[24rem] md:max-w-[30rem] lg:max-w-[32rem]" - : "mt-4 flex w-full max-w-[20rem] flex-col gap-y-1 text-[clamp(2.8rem,10vw,4.9rem)] leading-[0.96] tracking-[-0.06em] sm:max-w-[24rem] md:max-w-[28rem] lg:max-w-[32rem]", + ? "mt-6 flex w-full max-w-[26rem] flex-col gap-y-1 px-4 text-[clamp(2.85rem,11vw,5.9rem)] leading-[1.06] tracking-[-0.03em] sm:max-w-[30rem] sm:px-0 md:max-w-[36rem] lg:max-w-[32rem]" + : "mt-6 flex w-full max-w-[26rem] flex-col gap-y-1 px-4 text-[clamp(2.7rem,12vw,6.3rem)] leading-[0.94] tracking-[-0.045em] sm:max-w-[30rem] sm:px-0 md:max-w-[36rem] lg:max-w-[32rem]" + : "mt-4 flex w-full max-w-[30rem] flex-col gap-y-1 px-4 text-[clamp(2.55rem,9vw,4.9rem)] leading-[0.96] tracking-[-0.05em] sm:px-0 md:max-w-[36rem] lg:max-w-[40rem]", className, )} > diff --git a/messages/ar.json b/messages/ar.json index 97c2f5c..1e42a05 100644 --- a/messages/ar.json +++ b/messages/ar.json @@ -48,6 +48,8 @@ }, "portfolioPage": { "title": "الأعمال", + "heroBadge": "شغل مختار", + "heroTitle": "شغل\nمختار", "intro": "مجموعة مشاريع مختارة مع تركيز على الوضوح والنتائج.", "open": "فتح المشروع", "all": "كل التصنيفات", @@ -55,11 +57,15 @@ }, "productsPage": { "title": "المنتجات", + "heroBadge": "حلول جاهزة", + "heroTitle": "باقات\nجاهزة", "intro": "باقات للفرق من البداية حتى التوسع.", "open": "فتح المنتج" }, "aboutPage": { "title": "من أنا", + "heroBadge": "خلّيني عرّفك عليّي", + "heroTitle": "عنّي\nأكتر", "intro": "أبني تجارب رقمية واضحة للعلامات والمنتجات والفرق.", "valuesTitle": "كيف أعمل", "valueA": "الاستراتيجية أولاً", @@ -75,6 +81,8 @@ }, "contactPage": { "title": "تواصل", + "heroBadge": "خلّينا نحكي", + "heroTitle": "خلّينا\nنحكي", "intro": "ابعث لي هدفك باختصار وسأعود إليك بسرعة.", "name": "الاسم", "email": "البريد الإلكتروني", diff --git a/messages/de.json b/messages/de.json index 397fc45..2db5145 100644 --- a/messages/de.json +++ b/messages/de.json @@ -48,6 +48,8 @@ }, "portfolioPage": { "title": "Portfolio", + "heroBadge": "Ausgewaehlte Arbeiten", + "heroTitle": "Ausgewaehlte\nProjekte", "intro": "Eine Auswahl von Projekten mit Fokus auf Klarheit und Ergebnis.", "open": "Projekt oeffnen", "all": "Alle Kategorien", @@ -55,11 +57,15 @@ }, "productsPage": { "title": "Produkte", + "heroBadge": "Pakete und Loesungen", + "heroTitle": "Klare\nAngebote", "intro": "Pakete fuer Teams von Start bis Skalierung.", "open": "Produkt oeffnen" }, "aboutPage": { "title": "Ueber mich", + "heroBadge": "Mehr ueber mich", + "heroTitle": "Ueber\nmich", "intro": "Ich baue klare digitale Erlebnisse fuer Marken, Produkte und Teams.", "valuesTitle": "Wie ich arbeite", "valueA": "Strategie zuerst", @@ -75,6 +81,8 @@ }, "contactPage": { "title": "Kontakt", + "heroBadge": "Lass uns reden", + "heroTitle": "Lass uns\nreden", "intro": "Schreib mir kurz dein Ziel und ich melde mich zeitnah.", "name": "Name", "email": "E-Mail", diff --git a/messages/en.json b/messages/en.json index 5cdbfee..dffd429 100644 --- a/messages/en.json +++ b/messages/en.json @@ -48,6 +48,8 @@ }, "portfolioPage": { "title": "Portfolio", + "heroBadge": "Selected work", + "heroTitle": "Selected\nwork", "intro": "Selected projects with a focus on clarity and outcomes.", "open": "Open project", "all": "All categories", @@ -55,11 +57,15 @@ }, "productsPage": { "title": "Products", + "heroBadge": "Packages and offers", + "heroTitle": "Clear\noffers", "intro": "Packages for teams from early stage to scale.", "open": "Open product" }, "aboutPage": { "title": "About", + "heroBadge": "A bit more about me", + "heroTitle": "About\nme", "intro": "I build clear digital experiences for brands, products, and teams.", "valuesTitle": "How I work", "valueA": "Strategy first", @@ -75,6 +81,8 @@ }, "contactPage": { "title": "Contact", + "heroBadge": "Let us talk", + "heroTitle": "Let us\ntalk", "intro": "Share your goal and I will get back quickly.", "name": "Name", "email": "Email",