Refine homepage and restore hero

This commit is contained in:
MOH
2026-03-15 18:26:31 +01:00
parent 6caf723695
commit cf957cbbe2
14 changed files with 1565 additions and 155 deletions
+189 -103
View File
@@ -1,27 +1,36 @@
import type { Metadata } from "next";
import {
ArrowRight,
BadgeCheck,
Blocks,
BriefcaseBusiness,
ChartNoAxesCombined,
Globe,
Package2,
Search,
} from "lucide-react";
import Link from "next/link";
import { getLocale, getTranslations } from "next-intl/server";
import { Container } from "@/components/layout/container";
import { HomeHero } from "@/components/layout/home-hero";
import { StackedMarqueeSection, type MarqueeRow } from "@/components/layout/stacked-marquee-section";
import { MotionFade } from "@/components/motion-fade";
import { getMarqueeSettings, getSiteSettings, splitMarqueeRowItems } from "@/lib/app-config";
import { BentoGridSection } from "@/components/home/bento-grid-section";
import { CapabilitiesSection } from "@/components/home/capabilities-section";
import { ContactCtaSection } from "@/components/home/contact-cta-section";
import { MarqueeSection } from "@/components/home/marquee-section";
import { ProcessSection } from "@/components/home/process-section";
import { ProjectsSection } from "@/components/home/projects-section";
import type {
BentoSectionCopy,
CapabilitiesSectionCopy,
ContactCtaSectionCopy,
MarqueeSectionCopy,
ProcessSectionCopy,
ProjectCardCopy,
ProjectsSectionCopy,
} from "@/components/home/types";
import { buildLocalizedMetadata } from "@/lib/metadata";
import { AppCard } from "@/components/ui/app-card";
import { Button } from "@/components/ui/button";
import { CardContent } from "@/components/ui/card";
import { getLocalizedPath, resolveLocale } from "@/lib/locale";
import {
getMarqueeSettings,
getSiteSettings,
splitMarqueeRowItems,
} from "@/lib/app-config";
import {
getLocalizedValue,
getPublishedPortfolioProjects,
} from "@/lib/portfolio";
import { type MarqueeRow } from "@/components/layout/stacked-marquee-section";
type HomePageProps = {
params: Promise<{
@@ -29,29 +38,22 @@ type HomePageProps = {
}>;
};
const serviceIcons = [
BriefcaseBusiness,
Globe,
Package2,
Blocks,
Search,
ChartNoAxesCombined,
BadgeCheck,
];
export const dynamic = "force-dynamic";
export async function generateMetadata({ params }: HomePageProps): Promise<Metadata> {
await params;
const siteSettings = await getSiteSettings();
const localeKey = resolveLocale(await getLocale().catch(() => siteSettings.defaultLocale), siteSettings.defaultLocale);
const localeKey = resolveLocale(
await getLocale().catch(() => siteSettings.defaultLocale),
siteSettings.defaultLocale,
);
const t = await getTranslations({ locale: localeKey, namespace: "homepage" });
return await buildLocalizedMetadata({
locale: localeKey,
pathname: "/",
title: siteSettings.locales[localeKey].siteName,
description: t("heroText"),
title: t("meta.title"),
description: t("meta.description"),
applyTitleTemplate: false,
});
}
@@ -59,100 +61,184 @@ export async function generateMetadata({ params }: HomePageProps): Promise<Metad
export default async function HomePage({ params }: HomePageProps) {
await params;
const siteSettings = await getSiteSettings();
const localeKey = resolveLocale(await getLocale().catch(() => siteSettings.defaultLocale), siteSettings.defaultLocale);
const [t, marqueeSettings] = await Promise.all([
const localeKey = resolveLocale(
await getLocale().catch(() => siteSettings.defaultLocale),
siteSettings.defaultLocale,
);
const [t, marqueeSettings, publishedProjects] = await Promise.all([
getTranslations({ locale: localeKey, namespace: "homepage" }),
getMarqueeSettings(),
getPublishedPortfolioProjects(),
]);
const serviceItems = [
t("serviceBrand"),
t("serviceWebsite"),
t("serviceStore"),
t("serviceWordPress"),
t("serviceSeo"),
t("serviceTracking"),
t("serviceSystem"),
];
const bentoCopy: BentoSectionCopy = {
eyebrow: t("bento.eyebrow"),
title: t("bento.title"),
description: t("bento.description"),
about: {
eyebrow: t("bento.about.eyebrow"),
title: t("bento.about.title"),
description: t("bento.about.description"),
points: t.raw("bento.about.points") as string[],
},
coreStack: {
eyebrow: t("bento.coreStack.eyebrow"),
title: t("bento.coreStack.title"),
items: t.raw("bento.coreStack.items") as string[],
},
availability: {
eyebrow: t("bento.availability.eyebrow"),
title: t("bento.availability.title"),
description: t("bento.availability.description"),
status: t("bento.availability.status"),
},
currentFocus: {
eyebrow: t("bento.currentFocus.eyebrow"),
title: t("bento.currentFocus.title"),
items: t.raw("bento.currentFocus.items") as string[],
},
contact: {
eyebrow: t("bento.contact.eyebrow"),
title: t("bento.contact.title"),
description: t("bento.contact.description"),
emailLabel: t("bento.contact.emailLabel"),
emailValue: t("bento.contact.emailValue"),
},
highlights: {
eyebrow: t("bento.highlights.eyebrow"),
title: t("bento.highlights.title"),
items: t.raw("bento.highlights.items") as Array<{ value: string; label: string }>,
},
};
const marqueeCopy: MarqueeSectionCopy = {
eyebrow: t("marquee.eyebrow"),
title: t("marquee.title"),
description: t("marquee.description"),
};
const projectsCopy: ProjectsSectionCopy = {
eyebrow: t("projects.eyebrow"),
title: t("projects.title"),
description: t("projects.description"),
stackLabel: t("projects.stackLabel"),
fallbackItems: t.raw("projects.fallbackItems") as ProjectsSectionCopy["fallbackItems"],
};
const capabilitiesCopy: CapabilitiesSectionCopy = {
eyebrow: t("capabilities.eyebrow"),
title: t("capabilities.title"),
description: t("capabilities.description"),
items: t.raw("capabilities.items") as CapabilitiesSectionCopy["items"],
};
const processCopy: ProcessSectionCopy = {
eyebrow: t("process.eyebrow"),
title: t("process.title"),
description: t("process.description"),
steps: t.raw("process.steps") as ProcessSectionCopy["steps"],
};
const contactCtaCopy: ContactCtaSectionCopy = {
eyebrow: t("contactCta.eyebrow"),
title: t("contactCta.title"),
description: t("contactCta.description"),
contactCta: t("contactCta.contactCta"),
githubCta: t("contactCta.githubCta"),
emailLabel: t("contactCta.emailLabel"),
emailValue: t("contactCta.emailValue"),
availabilityLabel: t("contactCta.availabilityLabel"),
availabilityValue: t("contactCta.availabilityValue"),
githubHref: t("contactCta.githubHref"),
};
const marqueeLocale = marqueeSettings.locales[localeKey];
const marqueeRows: MarqueeRow[] = [
const marqueeRows = [
{ direction: "left", duration: 126, items: splitMarqueeRowItems(marqueeLocale.row1) },
{ direction: "right", duration: 220, items: splitMarqueeRowItems(marqueeLocale.row2) },
{ direction: "left", duration: 168, items: splitMarqueeRowItems(marqueeLocale.row3) },
{ direction: "right", duration: 144, items: splitMarqueeRowItems(marqueeLocale.row4) },
];
] satisfies MarqueeRow[];
const portfolioHref = getLocalizedPath(localeKey, "/portfolio", siteSettings.defaultLocale);
const contactHref = getLocalizedPath(localeKey, "/contact", siteSettings.defaultLocale);
const stackSets = t.raw("projects.stackSets") as string[][];
const selectedProjects = [
...publishedProjects.filter((project) => project.isFeatured),
...publishedProjects.filter((project) => !project.isFeatured),
].slice(0, 3);
const projectCards: ProjectCardCopy[] = selectedProjects.map((project, index) => ({
title: getLocalizedValue(project.title, localeKey),
summary: getLocalizedValue(project.summary, localeKey),
stack: stackSets[index] ?? stackSets[stackSets.length - 1] ?? [],
href: getLocalizedPath(
localeKey,
`/portfolio/${project.slug}`,
siteSettings.defaultLocale,
),
cta: t("projects.cta"),
meta: [
getLocalizedValue(project.category.name, localeKey),
getLocalizedValue(project.serviceLabel, localeKey),
String(project.projectYear),
],
featured: index === 0,
}));
const fallbackProjectCards: ProjectCardCopy[] = projectsCopy.fallbackItems.map((item) => ({
title: item.title,
summary: item.summary,
stack: item.stack,
href: item.href === "/portfolio" ? portfolioHref : item.href === "/contact" ? contactHref : item.href,
cta: t("projects.cta"),
meta: [t("projects.fallbackMeta")],
}));
const mergedProjectCards = [
...projectCards,
...fallbackProjectCards.slice(0, Math.max(0, 3 - projectCards.length)),
].slice(0, 3);
return (
<>
<HomeHero
locale={localeKey}
kicker={t("heroKicker")}
title={t("heroTitle")}
description={t("heroText")}
kicker={t("heroVisual.kicker")}
title={t("heroVisual.title")}
description={t("heroVisual.description")}
/>
<Container id="home-content" className="relative z-10 -mt-6 flex scroll-mt-28 flex-col gap-section pb-12 lg:-mt-8 lg:pb-16">
<MotionFade delay={0.05}>
<AppCard className="overflow-hidden">
<CardContent className="p-6 lg:p-10">
<div className="grid gap-8 lg:grid-cols-[minmax(0,1.05fr)_minmax(0,1fr)] lg:items-start">
<div>
<p className="text-sm font-medium tracking-[0.08em] text-foreground/56">
{t("servicesEyebrow")}
</p>
<h2 className="mt-4 max-w-[12ch] text-balance text-4xl font-semibold leading-[0.92] tracking-[-0.06em] text-foreground sm:text-5xl lg:text-6xl">
{t("servicesTitle")}
</h2>
<p className="mt-5 max-w-[34rem] text-sm leading-7 text-muted-foreground sm:text-base">
{t("servicesText")}
</p>
<div className="mt-8 flex flex-wrap gap-3">
<Button asChild size="lg" className="min-w-[11rem]">
<Link href={getLocalizedPath(localeKey, "/contact", siteSettings.defaultLocale)}>
{t("servicesPrimaryCta")}
<ArrowRight className="h-4 w-4" />
</Link>
</Button>
<Button
asChild
size="lg"
variant="outline"
className="min-w-[11rem] border-border/80 bg-background/70 hover:bg-background"
<Container
id="home-content"
className="relative z-10 flex max-w-7xl flex-col gap-16 pb-14 pt-6 sm:gap-20 sm:pb-16 sm:pt-8 lg:gap-24 lg:pb-20 lg:pt-10"
>
<Link href={getLocalizedPath(localeKey, "/portfolio", siteSettings.defaultLocale)}>
{t("servicesSecondaryCta")}
</Link>
</Button>
</div>
</div>
<MotionFade>
<BentoGridSection copy={bentoCopy} />
</MotionFade>
<div className="grid gap-3 sm:grid-cols-2">
{serviceItems.map((item, index) => {
const Icon = serviceIcons[index];
<MotionFade delay={0.06}>
<MarqueeSection copy={marqueeCopy} rows={marqueeRows} />
</MotionFade>
return (
<AppCard
key={item}
layer="single"
interactive
className="h-full"
>
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-[linear-gradient(135deg,hsl(var(--hero-left)/0.42),hsl(var(--hero-right)/0.26))]">
<Icon className="h-5 w-5 text-foreground/78" />
</div>
<p className="mt-4 text-sm font-medium leading-6 text-foreground/88 sm:text-base">
{item}
</p>
</AppCard>
);
})}
</div>
</div>
</CardContent>
</AppCard>
<MotionFade delay={0.08}>
<ProjectsSection copy={projectsCopy} items={mergedProjectCards} />
</MotionFade>
<MotionFade delay={0.1}>
<CapabilitiesSection copy={capabilitiesCopy} />
</MotionFade>
<MotionFade delay={0.12}>
<ProcessSection copy={processCopy} />
</MotionFade>
<MotionFade delay={0.14}>
<ContactCtaSection copy={contactCtaCopy} contactHref={contactHref} />
</MotionFade>
</Container>
<StackedMarqueeSection rows={marqueeRows} className="relative z-10 mt-2 pb-10 sm:pb-12 lg:pb-14" />
</>
);
}
+57
View File
@@ -0,0 +1,57 @@
import type { ReactNode } from "react";
import { AppCard } from "@/components/ui/app-card";
import { cn } from "@/lib/utils";
type BentoCardProps = {
eyebrow: string;
title: string;
description?: string;
icon?: ReactNode;
className?: string;
children?: ReactNode;
};
export function BentoCard({
eyebrow,
title,
description,
icon,
className,
children,
}: BentoCardProps) {
return (
<AppCard
layer="single"
interactive
className={cn(
"group h-full border-border/80 bg-surface-2 transition-all duration-300 hover:-translate-y-1 hover:border-border-strong hover:bg-background/80 hover:shadow-lg",
className,
)}
>
<div className="flex h-full flex-col gap-6 p-6">
<div className="flex items-start justify-between gap-4">
<div className="space-y-3">
<p className="text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground">
{eyebrow}
</p>
<div className="space-y-2">
<h3 className="text-2xl font-semibold tracking-[-0.05em] text-foreground transition-colors duration-300 group-hover:text-foreground/95">
{title}
</h3>
{description ? (
<p className="text-sm leading-7 text-muted-foreground">{description}</p>
) : null}
</div>
</div>
{icon ? (
<div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-nested border border-border/70 bg-background text-foreground transition-all duration-300 group-hover:-translate-y-0.5 group-hover:border-border-strong group-hover:shadow-sm">
{icon}
</div>
) : null}
</div>
{children}
</div>
</AppCard>
);
}
+125
View File
@@ -0,0 +1,125 @@
import {
Activity,
BriefcaseBusiness,
Layers3,
Mail,
Sparkles,
TimerReset,
} from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { SectionHeading } from "./section-heading";
import { BentoCard } from "./bento-card";
import type { BentoSectionCopy } from "./types";
type BentoGridSectionProps = {
copy: BentoSectionCopy;
};
export function BentoGridSection({ copy }: BentoGridSectionProps) {
return (
<section className="space-y-8">
<SectionHeading
eyebrow={copy.eyebrow}
title={copy.title}
description={copy.description}
/>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4 xl:gap-6">
<BentoCard
eyebrow={copy.about.eyebrow}
title={copy.about.title}
description={copy.about.description}
icon={<BriefcaseBusiness className="h-5 w-5" />}
className="xl:col-span-2 xl:row-span-2"
>
<ul className="grid gap-3 sm:grid-cols-2">
{copy.about.points.map((item) => (
<li
key={item}
className="rounded-nested border border-border/70 bg-background px-4 py-3 text-sm text-foreground/88"
>
{item}
</li>
))}
</ul>
</BentoCard>
<BentoCard
eyebrow={copy.coreStack.eyebrow}
title={copy.coreStack.title}
icon={<Layers3 className="h-5 w-5" />}
>
<div className="flex flex-wrap gap-2">
{copy.coreStack.items.map((item) => (
<Badge key={item} variant="outline" className="border-border/80 bg-background">
{item}
</Badge>
))}
</div>
</BentoCard>
<BentoCard
eyebrow={copy.availability.eyebrow}
title={copy.availability.title}
description={copy.availability.description}
icon={<Activity className="h-5 w-5" />}
>
<Badge variant="success" className="w-fit">
{copy.availability.status}
</Badge>
</BentoCard>
<BentoCard
eyebrow={copy.currentFocus.eyebrow}
title={copy.currentFocus.title}
icon={<Sparkles className="h-5 w-5" />}
>
<ul className="space-y-3 text-sm text-foreground/88">
{copy.currentFocus.items.map((item) => (
<li key={item} className="rounded-nested border border-border/70 bg-background px-4 py-3">
{item}
</li>
))}
</ul>
</BentoCard>
<BentoCard
eyebrow={copy.contact.eyebrow}
title={copy.contact.title}
description={copy.contact.description}
icon={<Mail className="h-5 w-5" />}
>
<div className="rounded-nested border border-border/70 bg-background px-4 py-4">
<p className="text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground">
{copy.contact.emailLabel}
</p>
<p className="mt-2 text-base font-medium text-foreground">
{copy.contact.emailValue}
</p>
</div>
</BentoCard>
<BentoCard
eyebrow={copy.highlights.eyebrow}
title={copy.highlights.title}
icon={<TimerReset className="h-5 w-5" />}
>
<div className="grid gap-3 sm:grid-cols-3 xl:grid-cols-1">
{copy.highlights.items.map((item) => (
<div
key={item.label}
className="rounded-nested border border-border/70 bg-background px-4 py-4"
>
<p className="text-2xl font-semibold tracking-[-0.05em] text-foreground">
{item.value}
</p>
<p className="mt-1 text-sm text-muted-foreground">{item.label}</p>
</div>
))}
</div>
</BentoCard>
</div>
</section>
);
}
+50
View File
@@ -0,0 +1,50 @@
import { Bot, Database, Globe, ServerCog } from "lucide-react";
import { AppCard } from "@/components/ui/app-card";
import { SectionHeading } from "./section-heading";
import type { CapabilitiesSectionCopy } from "./types";
type CapabilitiesSectionProps = {
copy: CapabilitiesSectionCopy;
};
const capabilityIcons = [Globe, Database, ServerCog, Bot];
export function CapabilitiesSection({ copy }: CapabilitiesSectionProps) {
return (
<section className="space-y-8">
<SectionHeading
eyebrow={copy.eyebrow}
title={copy.title}
description={copy.description}
/>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4 xl:gap-6">
{copy.items.map((item, index) => {
const Icon = capabilityIcons[index] ?? Globe;
return (
<AppCard
key={item.title}
layer="single"
interactive
className="group border-border/80 bg-surface-2 transition-all duration-300 hover:-translate-y-1 hover:border-border-strong hover:shadow-panel"
>
<div className="flex h-full flex-col gap-5 p-6">
<div className="flex h-11 w-11 items-center justify-center rounded-nested border border-border/70 bg-background text-foreground transition-transform duration-300 group-hover:-translate-y-0.5">
<Icon className="h-5 w-5" />
</div>
<div className="space-y-3">
<h3 className="text-xl font-semibold tracking-[-0.04em] text-foreground">
{item.title}
</h3>
<p className="text-sm leading-7 text-muted-foreground">{item.description}</p>
</div>
</div>
</AppCard>
);
})}
</div>
</section>
);
}
+64
View File
@@ -0,0 +1,64 @@
import { ArrowUpRight, Mail } from "lucide-react";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import { AppCard } from "@/components/ui/app-card";
import type { ContactCtaSectionCopy } from "./types";
type ContactCtaSectionProps = {
copy: ContactCtaSectionCopy;
contactHref: string;
};
export function ContactCtaSection({
copy,
contactHref,
}: ContactCtaSectionProps) {
return (
<section>
<AppCard className="overflow-hidden">
<div className="grid gap-6 rounded-nested border border-border/70 bg-background p-6 sm:p-8 lg:grid-cols-[minmax(0,1fr)_auto] lg:items-end lg:p-10">
<div className="space-y-4">
<p className="text-xs font-semibold uppercase tracking-[0.16em] text-muted-foreground">
{copy.eyebrow}
</p>
<h2 className="max-w-3xl text-balance text-3xl font-semibold tracking-[-0.05em] text-foreground sm:text-4xl lg:text-5xl">
{copy.title}
</h2>
<p className="max-w-2xl text-sm leading-7 text-muted-foreground sm:text-base">
{copy.description}
</p>
<div className="flex flex-wrap gap-6 text-sm">
<div className="space-y-1">
<p className="font-semibold text-foreground">{copy.emailLabel}</p>
<a href={`mailto:${copy.emailValue}`} className="text-muted-foreground hover:text-foreground">
{copy.emailValue}
</a>
</div>
<div className="space-y-1">
<p className="font-semibold text-foreground">{copy.availabilityLabel}</p>
<p className="text-muted-foreground">{copy.availabilityValue}</p>
</div>
</div>
</div>
<div className="flex flex-wrap gap-3 lg:justify-end">
<Button asChild size="lg">
<Link href={contactHref}>
{copy.contactCta}
<Mail className="h-4 w-4" />
</Link>
</Button>
<Button asChild size="lg" variant="outline">
<a href={copy.githubHref} target="_blank" rel="noreferrer">
{copy.githubCta}
<ArrowUpRight className="h-4 w-4" />
</a>
</Button>
</div>
</div>
</AppCard>
</section>
);
}
+113
View File
@@ -0,0 +1,113 @@
import { ArrowRight, MapPin, Sparkles } from "lucide-react";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { AppCard } from "@/components/ui/app-card";
import { cn } from "@/lib/utils";
import type { HeroCopy } from "./types";
type HeroSectionProps = {
copy: HeroCopy;
contactHref: string;
portfolioHref: string;
};
export function HeroSection({
copy,
contactHref,
portfolioHref,
}: HeroSectionProps) {
return (
<section className="relative pt-8 sm:pt-12 lg:pt-16">
<div className="grid gap-6 lg:grid-cols-[minmax(0,1.15fr)_minmax(320px,0.85fr)] lg:items-end">
<div className="space-y-8">
<Badge
variant="outline"
className="w-fit border-border/80 bg-background/80 text-foreground shadow-xs"
>
<span className="mr-2 h-2 w-2 rounded-full bg-status-success" aria-hidden="true" />
{copy.availability}
</Badge>
<div className="space-y-5">
<p className="text-sm font-semibold uppercase tracking-[0.18em] text-muted-foreground">
{copy.name}
</p>
<h1 className="max-w-4xl text-balance text-5xl font-semibold tracking-[-0.07em] text-foreground sm:text-6xl lg:text-7xl">
{copy.headline}
</h1>
<p className="max-w-2xl text-base leading-8 text-muted-foreground sm:text-lg">
{copy.description}
</p>
</div>
<div className="flex flex-wrap gap-3">
<Button asChild size="lg" className="shadow-sm">
<Link href={contactHref}>
{copy.primaryCta}
<ArrowRight className="h-4 w-4" />
</Link>
</Button>
<Button
asChild
size="lg"
variant="outline"
className="border-border/80 bg-background/70 hover:border-border-strong hover:bg-background"
>
<Link href={portfolioHref}>{copy.secondaryCta}</Link>
</Button>
</div>
<div className="flex flex-wrap gap-3">
{copy.highlights.map((item) => (
<Badge
key={item}
variant="secondary"
className="bg-secondary/70 text-foreground shadow-none"
>
{item}
</Badge>
))}
</div>
</div>
<AppCard className="overflow-hidden">
<div className="grid gap-3">
<div className="grid gap-3 sm:grid-cols-2">
<MetricCard label={copy.roleLabel} value={copy.roleValue} />
<MetricCard label={copy.locationLabel} value={copy.locationValue} icon={MapPin} />
</div>
<MetricCard label={copy.focusLabel} value={copy.focusValue} icon={Sparkles} strong />
</div>
</AppCard>
</div>
</section>
);
}
type MetricCardProps = {
label: string;
value: string;
strong?: boolean;
icon?: typeof MapPin;
};
function MetricCard({ label, value, strong = false, icon: Icon }: MetricCardProps) {
return (
<div
className={cn(
"rounded-nested border border-border/70 bg-background p-5 transition-all duration-300",
strong && "bg-surface-2",
)}
>
<div className="flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground">
{Icon ? <Icon className="h-3.5 w-3.5" /> : null}
<span>{label}</span>
</div>
<p className="mt-3 text-xl font-semibold tracking-[-0.04em] text-foreground">
{value}
</p>
</div>
);
}
+27
View File
@@ -0,0 +1,27 @@
import { StackedMarqueeSection, type MarqueeRow } from "@/components/layout/stacked-marquee-section";
import { AppCard } from "@/components/ui/app-card";
import { SectionHeading } from "./section-heading";
import type { MarqueeSectionCopy } from "./types";
type MarqueeSectionProps = {
copy: MarqueeSectionCopy;
rows: MarqueeRow[];
};
export function MarqueeSection({ copy, rows }: MarqueeSectionProps) {
return (
<section className="space-y-8 overflow-hidden">
<SectionHeading
eyebrow={copy.eyebrow}
title={copy.title}
description={copy.description}
/>
<AppCard className="overflow-hidden">
<div className="rounded-nested border border-border/70 bg-background py-6">
<StackedMarqueeSection rows={rows} className="py-0" />
</div>
</AppCard>
</section>
);
}
+42
View File
@@ -0,0 +1,42 @@
import { AppCard } from "@/components/ui/app-card";
import { SectionHeading } from "./section-heading";
import type { ProcessSectionCopy } from "./types";
type ProcessSectionProps = {
copy: ProcessSectionCopy;
};
export function ProcessSection({ copy }: ProcessSectionProps) {
return (
<section className="space-y-8">
<SectionHeading
eyebrow={copy.eyebrow}
title={copy.title}
description={copy.description}
/>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4 xl:gap-6">
{copy.steps.map((step, index) => (
<AppCard
key={step.title}
layer="single"
interactive
className="group border-border/80 bg-surface-2 transition-all duration-300 hover:-translate-y-1 hover:border-border-strong hover:shadow-panel"
>
<div className="flex h-full flex-col gap-5 p-6">
<p className="text-sm font-semibold tracking-[0.18em] text-muted-foreground">
{String(index + 1).padStart(2, "0")}
</p>
<div className="space-y-3">
<h3 className="text-2xl font-semibold tracking-[-0.05em] text-foreground">
{step.title}
</h3>
<p className="text-sm leading-7 text-muted-foreground">{step.description}</p>
</div>
</div>
</AppCard>
))}
</div>
</section>
);
}
+86
View File
@@ -0,0 +1,86 @@
import { ArrowUpRight } from "lucide-react";
import Link from "next/link";
import { Badge } from "@/components/ui/badge";
import { AppCard } from "@/components/ui/app-card";
import { SectionHeading } from "./section-heading";
import type { ProjectCardCopy, ProjectsSectionCopy } from "./types";
type ProjectsSectionProps = {
copy: ProjectsSectionCopy;
items: ProjectCardCopy[];
};
export function ProjectsSection({ copy, items }: ProjectsSectionProps) {
return (
<section className="space-y-10">
<SectionHeading
eyebrow={copy.eyebrow}
title={copy.title}
description={copy.description}
/>
<div className="grid grid-cols-1 gap-4 lg:grid-cols-3 xl:gap-6">
{items.map((item, index) => (
<AppCard
key={`${item.title}-${index}`}
layer="single"
interactive
className={[
"group h-full border-border/80 bg-surface-2 transition-all duration-300 hover:-translate-y-1 hover:border-border-strong hover:bg-background/80 hover:shadow-lg",
index === 0 ? "lg:col-span-2" : "",
].join(" ")}
>
<div className="flex h-full flex-col gap-6 p-6">
<div className="flex flex-wrap items-center gap-2">
{item.meta.map((metaItem) => (
<Badge
key={metaItem}
variant="outline"
className="border-border/80 bg-background text-muted-foreground transition-colors duration-300 group-hover:border-border-strong group-hover:text-foreground/80"
>
{metaItem}
</Badge>
))}
</div>
<div className="space-y-3">
<h3 className="text-2xl font-semibold tracking-[-0.05em] text-foreground transition-colors duration-300 group-hover:text-foreground/95">
{item.title}
</h3>
<p className="text-sm leading-7 text-muted-foreground">{item.summary}</p>
</div>
<div className="space-y-3">
<p className="text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground">
{copy.stackLabel}
</p>
<div className="flex flex-wrap gap-2">
{item.stack.map((stackItem) => (
<Badge
key={stackItem}
variant="secondary"
className="bg-secondary/70 text-foreground transition-colors duration-300 group-hover:bg-secondary"
>
{stackItem}
</Badge>
))}
</div>
</div>
<div className="mt-auto">
<Link
href={item.href}
className="inline-flex items-center gap-2 text-sm font-medium text-foreground transition-all duration-200 group-hover:translate-x-0.5"
>
{item.cta}
<ArrowUpRight className="h-4 w-4 transition-transform duration-200 group-hover:-translate-y-0.5 group-hover:translate-x-0.5" />
</Link>
</div>
</div>
</AppCard>
))}
</div>
</section>
);
}
+37
View File
@@ -0,0 +1,37 @@
import { cn } from "@/lib/utils";
type SectionHeadingProps = {
eyebrow: string;
title: string;
description: string;
align?: "left" | "center";
className?: string;
};
export function SectionHeading({
eyebrow,
title,
description,
align = "left",
className,
}: SectionHeadingProps) {
return (
<div
className={cn(
"space-y-4",
align === "center" && "mx-auto max-w-3xl text-center",
className,
)}
>
<p className="text-xs font-semibold uppercase tracking-[0.16em] text-muted-foreground">
{eyebrow}
</p>
<h2 className="max-w-4xl text-balance text-3xl font-semibold tracking-[-0.05em] text-foreground sm:text-4xl lg:text-5xl">
{title}
</h2>
<p className="max-w-3xl text-sm leading-7 text-muted-foreground sm:text-[1.02rem]">
{description}
</p>
</div>
);
}
+120
View File
@@ -0,0 +1,120 @@
export type HeroCopy = {
availability: string;
name: string;
headline: string;
description: string;
primaryCta: string;
secondaryCta: string;
locationLabel: string;
locationValue: string;
roleLabel: string;
roleValue: string;
focusLabel: string;
focusValue: string;
highlights: string[];
};
export type BentoSectionCopy = {
eyebrow: string;
title: string;
description: string;
about: {
eyebrow: string;
title: string;
description: string;
points: string[];
};
coreStack: {
eyebrow: string;
title: string;
items: string[];
};
availability: {
eyebrow: string;
title: string;
description: string;
status: string;
};
currentFocus: {
eyebrow: string;
title: string;
items: string[];
};
contact: {
eyebrow: string;
title: string;
description: string;
emailLabel: string;
emailValue: string;
};
highlights: {
eyebrow: string;
title: string;
items: Array<{
value: string;
label: string;
}>;
};
};
export type MarqueeSectionCopy = {
eyebrow: string;
title: string;
description: string;
};
export type ProjectCardCopy = {
title: string;
summary: string;
stack: string[];
href: string;
cta: string;
meta: string[];
featured?: boolean;
};
export type ProjectsSectionCopy = {
eyebrow: string;
title: string;
description: string;
stackLabel: string;
fallbackItems: Array<{
title: string;
summary: string;
stack: string[];
href: string;
}>;
};
export type CapabilitiesSectionCopy = {
eyebrow: string;
title: string;
description: string;
items: Array<{
title: string;
description: string;
}>;
};
export type ProcessSectionCopy = {
eyebrow: string;
title: string;
description: string;
steps: Array<{
title: string;
description: string;
}>;
};
export type ContactCtaSectionCopy = {
eyebrow: string;
title: string;
description: string;
contactCta: string;
githubCta: string;
emailLabel: string;
emailValue: string;
availabilityLabel: string;
availabilityValue: string;
githubHref: string;
};
+218 -17
View File
@@ -36,23 +36,224 @@
"secondaryCta": "العودة للرئيسية"
},
"homepage": {
"heroKicker": "مرحبااا، أنا محمد",
"heroTitle": طوّر واجهات ويب\nومصمّم جرافيك",
"heroText": "بشتغل على حلول رقمية لمشاكل حقيقية\nبتطلع ممتازة وبتشتغل بكفاءة.",
"servicesEyebrow": "شو فيني قدّم",
"servicesTitle": "حلول رقمية مصممة حسب شغلك، مو قوالب جاهزة.",
"servicesText": "من الهوية والموقع، للمتجر والسيو والتراكنغ، لحد الأنظمة الخاصة الداخلية. الفكرة إنو كل شيء يطلع مرتب، مفهوم، وقابل للنمو.",
"servicesPrimaryCta": "احكيلي عن مشروعك",
"servicesSecondaryCta": "شوف شغلي",
"serviceBrand": "بناء براند واضح ومرتب.",
"serviceWebsite": "موقع إلكتروني يليق بشغلك.",
"serviceStore": "متجر أسهل بالعرض والبيع.",
"serviceWordPress": "إضافة WordPress خاصة فيك.",
"serviceSeo": "تحسين الظهور بمحركات البحث.",
"serviceTracking": "تراكنغ واضح وفهم لحركة الزوار.",
"serviceSystem": "سستم خاص لشركتك أو لفريقك.",
"toPortfolio": "عرض الأعمال",
"toContact": "تواصل معي"
"meta": {
"title": عرض أعمال مطور Full-stack",
"description": "منتجات رقمية حديثة وأنظمة قابلة للتوسع وتجارب متعددة اللغات مبنية بعقلية منتج واضحة."
},
"heroVisual": {
"kicker": "مرحبا، أنا محمد",
"title": "مطوّر واجهات ويب\nومصمّم جرافيك",
"description": "أبني حلول رقمية لمشاكل حقيقية\nتبدو قوية وتعمل بكفاءة."
},
"hero": {
"availability": "متاح لمشاريع مختارة",
"name": "محمد فراواتي",
"headline": "منتجات Full-stack تجمع بين تنفيذ Frontend قوي وأنظمة Backend موثوقة.",
"description": "أصمم وأبني تجارب ويب بمستوى عالي لفرق تحتاج وضوح وسرعة وتنفيذ جاهز للإنتاج من أول الفكرة حتى الإطلاق.",
"primaryCta": "ابدأ مشروعك",
"secondaryCta": "شوف الأعمال المختارة",
"locationLabel": "الموقع",
"locationValue": "برلين، ألمانيا",
"roleLabel": "الدور",
"roleValue": "مطور Full-stack",
"focusLabel": "التركيز الحالي",
"focusValue": "منتجات عالية الأداء وأتمتة وعمل منصات قابلة للصيانة.",
"highlights": [
"تسليم بثلاث لغات",
"تنفيذ بعقلية منتج",
"شريك تقني لفرق جاهزة للإطلاق"
]
},
"bento": {
"eyebrow": "لمحات",
"title": "مبني لفرق تهتم بالجودة والسرعة والقرارات التقنية النظيفة.",
"description": "تنظيم الصفحة يعكس أسلوب شغلي: وضوح أولاً، تنفيذ قوي ثانياً، ومن دون تعقيد زائد بالنص.",
"about": {
"eyebrow": "عني",
"title": "أشتغل بين المنتج والهندسة والتنفيذ.",
"description": "شغلي يجمع بين التفكير المنتج والتنفيذ العملي، حتى يحصل المؤسسون والوكالات والفرق التقنية على مطور قادر ينقل المشروع من المعمارية للتفاصيل النهائية بدون ما يضيع المستوى.",
"points": [
"أنظمة Frontend مع انضباط UX واضح",
"خدمات Backend وربط أنظمة لسير عمل حقيقي",
"تواصل واضح مع أصحاب القرار التقني وغير التقني",
"تسليم مع قابلية صيانة محسوبة من أول يوم"
]
},
"coreStack": {
"eyebrow": "Core stack",
"title": "أدواتي اليومية",
"items": [
"Next.js",
"TypeScript",
"React",
"Node.js",
"PostgreSQL",
"Tailwind CSS",
"Prisma",
"Docker"
]
},
"availability": {
"eyebrow": "التوفر",
"title": "جاهز لتعاونات مركزة",
"description": "أفضل مناسبة لستارت أب ووكالات وفرق تريد شخصاً تقنياً واحداً يدير التنفيذ من البداية للنهاية.",
"status": "فرص جديدة متاحة"
},
"currentFocus": {
"eyebrow": "التركيز الحالي",
"title": "أكثر شيء أعمل عليه حالياً",
"items": [
"مواقع تسويقية بمستوى UX قريب من المنتجات",
"لوحات تحكم وأدوات داخلية للإدارة",
"مسارات أتمتة للمحتوى والعمليات"
]
},
"contact": {
"eyebrow": "تواصل سريع",
"title": "أقصر طريق للوصول إلي",
"description": "إذا كان الطلب واضحاً من الآن، أرسل السياق والمدة الزمنية وأرجع لك بالخطوة الصحيحة مباشرة.",
"emailLabel": "البريد الإلكتروني",
"emailValue": "hello@moh-sass.dev"
},
"highlights": {
"eyebrow": "لمحات",
"title": "مبادئ العمل",
"items": [
{
"value": "3",
"label": "لغات مدعومة"
},
{
"value": "E2E",
"label": "ملكية تسليم كاملة"
},
{
"value": "24/7",
"label": "عقلية إنتاج"
}
]
}
},
"marquee": {
"eyebrow": "الأدوات المعتمدة",
"title": "Stack عملي مختار على أساس الصيانة والسرعة والقيمة الطويلة المدى.",
"description": "قسم Marquee يستخدم إعدادات الموقع الموجودة مسبقاً حتى تبقى الصفحة متناسقة مع الأدوات والقدرات التي تريد إبرازها."
},
"projects": {
"eyebrow": "مشاريع مختارة",
"title": "ثلاثة أمثلة على نوع الشغل الذي أحب أن أطلقه.",
"description": "الأعمال المختارة تجمع بين جودة واجهة قوية وبنية تقنية تبقى ثابتة بعد الإطلاق.",
"stackLabel": "Stack",
"cta": "فتح المشروع",
"fallbackMeta": "عمل مختار",
"stackSets": [
[
"Next.js",
"TypeScript",
"Design Systems"
],
[
"APIs",
"PostgreSQL",
"Automation"
],
[
"Tailwind CSS",
"Prisma",
"Infrastructure"
]
],
"fallbackItems": [
{
"title": "إعادة بناء منصة منتج",
"summary": "تحديث عملي لمنصة خدمات مع UX أوضح ومعمارية أقوى ودورات تسليم أسرع.",
"stack": [
"Next.js",
"TypeScript",
"PostgreSQL"
],
"href": "/portfolio"
},
{
"title": "لوحة عمليات داخلية",
"summary": "أداة داخلية تركز على تسريع القرار وتنظيف سير العمل وتقليل التنسيق اليدوي بين الفرق.",
"stack": [
"React",
"Node.js",
"Automation"
],
"href": "/portfolio"
},
{
"title": "نظام مواقع للإطلاقات",
"summary": "أساس قابل لإعادة الاستخدام لإطلاق مواقع بسرعة بدون التنازل عن الجودة التقنية أو التحكم بالمحتوى.",
"stack": [
"Tailwind CSS",
"CMS",
"Performance"
],
"href": "/contact"
}
]
},
"capabilities": {
"eyebrow": "القدرات",
"title": "تغطية تقنية من الطرف للطرف بدون خسارة تركيز المنتج.",
"description": "كل مجال هنا مبني على شغل إنتاجي فعلي، وليس مجرد تسميات خدمات عامة.",
"items": [
{
"title": "Frontend Engineering",
"description": "واجهات Responsive وأنظمة تصميم وتفاصيل تفاعل ومعمارية Components قابلة للتوسع."
},
{
"title": "Backend Systems",
"description": "APIs منظمة وربط أنظمة ونمذجة بيانات ومنطق تطبيقي مصمم لتسليم يمكن الاعتماد عليه."
},
{
"title": "DevOps & Infrastructure",
"description": "نشر وبيئات وتشغيل ومراقبة أساسية وقرارات Infrastructure تقلل العبء التشغيلي."
},
{
"title": "Automation & AI Workflows",
"description": "طبقات أتمتة عملية تقلل العمل اليدوي وتربط بين المنتج والمحتوى والعمليات."
}
]
},
"process": {
"eyebrow": "آلية العمل",
"title": "مسار مختصر يبقي المشروع متقدماً.",
"description": "العملية مقصودة أن تكون بسيطة: نقلل الغموض مبكراً، نثبت القرارات التقنية، ثم نطلق بانضباط.",
"steps": [
{
"title": "Discover",
"description": "نحدد الأهداف والقيود والجمهور والنتيجة الحقيقية المطلوبة من المنتج."
},
{
"title": "Architect",
"description": "نرسم شكل النظام وحدوده التقنية وبنية المحتوى ومسار التنفيذ قبل الدخول في البناء الثقيل."
},
{
"title": "Build",
"description": "ننّفذ مع عناية قوية بالواجهة ومنطق تطبيق نظيف وبنية قابلة للصيانة عبر كامل الـ stack."
},
{
"title": "Ship",
"description": "نطلق بثقة ونتأكد من النتيجة ونترك المنتج بحالة مستقرة للمرحلة التالية."
}
]
},
"contactCta": {
"eyebrow": "ابدأ الحديث",
"title": "إذا المشروع يحتاج تنفيذ قوي، أقدر أدفعه للأمام.",
"description": "هات الملخص أو الاختناق الحالي أو الاتجاه العام. أقدر أساعد في ضبط النطاق وتصميم النظام وبناء المنتج.",
"contactCta": "تواصل معي",
"githubCta": "GitHub",
"emailLabel": "البريد الإلكتروني",
"emailValue": "hello@moh-sass.dev",
"availabilityLabel": "التوفر",
"availabilityValue": "مفتوح لشغل منتجات مركز",
"githubHref": "https://github.com/mohfarawati"
}
},
"portfolioPage": {
"title": "الأعمال",
+218 -17
View File
@@ -36,23 +36,224 @@
"secondaryCta": "Zur Startseite"
},
"homepage": {
"heroKicker": "Hi, ich bin Moh",
"heroTitle": "frontend\ndeveloper\n& designer",
"heroText": "Ich entwickle digitale Lösungen für echte Probleme,\ndie gut aussehen und sauber funktionieren.",
"servicesEyebrow": "Leistungen",
"servicesTitle": "Digitale Arbeit, die sich an deinem Geschäft orientiert und nicht an Vorlagen.",
"servicesText": "Von Markenaufbau und Websites bis zu Shops, SEO, Tracking, WordPress Plugins und internen Systemen geht es immer um dasselbe: mehr Klarheit, bessere Wirkung und eine saubere Grundlage für Wachstum.",
"servicesPrimaryCta": "Erzähl mir von deinem Projekt",
"servicesSecondaryCta": "Meine Arbeiten ansehen",
"serviceBrand": "Markenaufbau mit klarer Richtung.",
"serviceWebsite": "Websites mit sauberer Struktur.",
"serviceStore": "Shops, die klarer verkaufen.",
"serviceWordPress": "Eigene WordPress Plugins.",
"serviceSeo": "Mehr Sichtbarkeit in Suchmaschinen.",
"serviceTracking": "Tracking und bessere Auswertung.",
"serviceSystem": "Interne Systeme für Teams und Firmen.",
"toPortfolio": "Portfolio ansehen",
"toContact": "Kontakt aufnehmen"
"meta": {
"title": "Portfolio fuer Full-stack Entwicklung",
"description": "Moderne digitale Produkte, skalierbare Systeme und mehrsprachige Erlebnisse mit starkem Produktfokus."
},
"heroVisual": {
"kicker": "Hi, ich bin Moh",
"title": "frontend\ndeveloper\n& designer",
"description": "Ich entwickle digitale Loesungen fuer echte Probleme,\ndie gut aussehen und sauber funktionieren."
},
"hero": {
"availability": "Verfuegbar fuer ausgewaehlte Projekte",
"name": "Moh Farawati",
"headline": "Full-stack Produkte mit starker Frontend Umsetzung und verlaesslichen Backend Systemen.",
"description": "Ich konzipiere und entwickle hochwertige Web-Erlebnisse fuer Teams, die Klarheit, Geschwindigkeit und produktionsreife Umsetzung von der Strategie bis zum Launch brauchen.",
"primaryCta": "Projekt starten",
"secondaryCta": "Ausgewaehlte Arbeiten",
"locationLabel": "Standort",
"locationValue": "Berlin, Deutschland",
"roleLabel": "Rolle",
"roleValue": "Full-stack Entwickler",
"focusLabel": "Aktueller Fokus",
"focusValue": "Performante Produkte, Automatisierung und wartbare Plattformarbeit.",
"highlights": [
"3-sprachige Umsetzung",
"Produktorientierte Ausfuehrung",
"Technischer Partner fuer launchbereite Teams"
]
},
"bento": {
"eyebrow": "Highlights",
"title": "Gebaut fuer Teams, die Qualitaet, Tempo und saubere technische Entscheidungen ernst nehmen.",
"description": "Die Startseite ist so aufgebaut, wie ich arbeite: erst Klarheit, dann starke Umsetzung, ohne unnoetige Komplexitaet dazwischen.",
"about": {
"eyebrow": "Ueber mich",
"title": "Ich arbeite ueber Produkt, Engineering und Delivery hinweg.",
"description": "Meine Arbeit verbindet Produktdenken mit direkter Umsetzung. So bekommen Gruender, Agenturen und technische Teams einen Entwickler, der von Architektur bis zum finalen Detail liefern kann, ohne an Qualitaet zu verlieren.",
"points": [
"Frontend Systeme mit klarer UX Disziplin",
"Backend Services und Integrationen fuer reale Prozesse",
"Klare Kommunikation mit technischen und nicht-technischen Stakeholdern",
"Lieferung mit Blick auf Wartbarkeit ab dem ersten Tag"
]
},
"coreStack": {
"eyebrow": "Core stack",
"title": "Taegliche Werkzeuge",
"items": [
"Next.js",
"TypeScript",
"React",
"Node.js",
"PostgreSQL",
"Tailwind CSS",
"Prisma",
"Docker"
]
},
"availability": {
"eyebrow": "Verfuegbarkeit",
"title": "Offen fuer fokussierte Zusammenarbeit",
"description": "Am besten passend fuer Startups, Agenturen und Teams, die einen technischen Owner suchen, der die Umsetzung Ende zu Ende treiben kann.",
"status": "Neue Slots offen"
},
"currentFocus": {
"eyebrow": "Aktueller Fokus",
"title": "Woran ich gerade am meisten baue",
"items": [
"Marketing Websites mit Produktniveau in der UX",
"Admin Tools und interne Dashboards",
"Automatisierungsablaeufe fuer Content und Operations"
]
},
"contact": {
"eyebrow": "Schneller Kontakt",
"title": "Direkter Weg zu mir",
"description": "Wenn das Briefing schon klar ist, schick Kontext und Zeitrahmen und ich melde mich mit dem richtigen naechsten Schritt.",
"emailLabel": "E-Mail",
"emailValue": "hello@moh-sass.dev"
},
"highlights": {
"eyebrow": "Highlights",
"title": "Arbeitsprinzipien",
"items": [
{
"value": "3",
"label": "Unterstuetzte Sprachen"
},
{
"value": "E2E",
"label": "Delivery Ownership"
},
{
"value": "24/7",
"label": "Produktionsdenken"
}
]
}
},
"marquee": {
"eyebrow": "Werkzeuge",
"title": "Ein Arbeits-Stack, ausgewaehlt fuer Wartbarkeit, Geschwindigkeit und langfristigen Hebel.",
"description": "Das Marquee nutzt die bestehende Site-Konfiguration, damit die Startseite mit den hervorgehobenen Tools und Kompetenzen konsistent bleibt."
},
"projects": {
"eyebrow": "Ausgewaehlte Projekte",
"title": "Drei Beispiele fuer die Art von Arbeit, die ich gerne ausliefere.",
"description": "Ausgewaehlte Projekte verbinden starke Interface-Qualitaet mit technischer Struktur, die auch nach dem Launch stabil bleibt.",
"stackLabel": "Stack",
"cta": "Projekt oeffnen",
"fallbackMeta": "Kuratiertes Projekt",
"stackSets": [
[
"Next.js",
"TypeScript",
"Design Systems"
],
[
"APIs",
"PostgreSQL",
"Automation"
],
[
"Tailwind CSS",
"Prisma",
"Infrastructure"
]
],
"fallbackItems": [
{
"title": "Produktplattform Relaunch",
"summary": "Eine produktionsreife Erneuerung fuer eine Service-Plattform mit klarerer UX, besserer Architektur und schnelleren Lieferzyklen.",
"stack": [
"Next.js",
"TypeScript",
"PostgreSQL"
],
"href": "/portfolio"
},
{
"title": "Operations Dashboard",
"summary": "Interne Tools mit Fokus auf schnellere Entscheidungen, sauberere Workflows und weniger manuelle Abstimmung im Team.",
"stack": [
"React",
"Node.js",
"Automation"
],
"href": "/portfolio"
},
{
"title": "Launch Website System",
"summary": "Eine wiederverwendbare Website Grundlage fuer schnelle Launches ohne Abstriche bei technischer Qualitaet oder redaktioneller Kontrolle.",
"stack": [
"Tailwind CSS",
"CMS",
"Performance"
],
"href": "/contact"
}
]
},
"capabilities": {
"eyebrow": "Capabilities",
"title": "Technische Abdeckung von Ende zu Ende ohne den Produktfokus zu verlieren.",
"description": "Jeder Bereich ist um echte Produktionsarbeit aufgebaut und nicht um generische Service-Begriffe.",
"items": [
{
"title": "Frontend Engineering",
"description": "Responsive Interfaces, Design Systems, Interaktionsqualitaet und Komponentenarchitektur fuer skalierbare Produkte."
},
{
"title": "Backend Systems",
"description": "Strukturierte APIs, Integrationen, Datenmodelle und Applikationslogik fuer verlaessliche Umsetzung."
},
{
"title": "DevOps & Infrastructure",
"description": "Deployments, Umgebungen, grundlegende Observability und Infrastrukturentscheidungen mit weniger operativer Reibung."
},
{
"title": "Automation & AI Workflows",
"description": "Praktische Automatisierungsebenen, die manuelle Arbeit reduzieren und Produkt, Content und Operations verbinden."
}
]
},
"process": {
"eyebrow": "Prozess",
"title": "Ein kompakter Ablauf, der Projekte in Bewegung haelt.",
"description": "Der Prozess ist absichtlich einfach: Unklarheit frueh reduzieren, solide technische Entscheidungen treffen und diszipliniert ausliefern.",
"steps": [
{
"title": "Discover",
"description": "Ziele, Grenzen, Zielgruppe und das eigentliche Ergebnis klaeren, das das Produkt erreichen soll."
},
{
"title": "Architect",
"description": "Systemform, technische Grenzen, Inhaltsstruktur und Lieferweg definieren, bevor die schwere Umsetzung startet."
},
{
"title": "Build",
"description": "Mit starker UI Qualitaet, sauberer Applikationslogik und wartbarer Struktur ueber den gesamten Stack umsetzen."
},
{
"title": "Ship",
"description": "Sicher launchen, das Ergebnis validieren und das Produkt stabil fuer die naechste Phase hinterlassen."
}
]
},
"contactCta": {
"eyebrow": "Gespraech starten",
"title": "Wenn das Projekt starke Umsetzung braucht, kann ich es voranbringen.",
"description": "Bring das Briefing, den Engpass oder die grobe Richtung mit. Ich kann Scope schaerfen, das System entwerfen und das Produkt bauen.",
"contactCta": "Kontakt aufnehmen",
"githubCta": "GitHub",
"emailLabel": "E-Mail",
"emailValue": "hello@moh-sass.dev",
"availabilityLabel": "Verfuegbarkeit",
"availabilityValue": "Offen fuer fokussierte Produktarbeit",
"githubHref": "https://github.com/mohfarawati"
}
},
"portfolioPage": {
"title": "Portfolio",
+218 -17
View File
@@ -36,23 +36,224 @@
"secondaryCta": "Back to homepage"
},
"homepage": {
"heroKicker": "Hi, I am Moh",
"heroTitle": "frontend\ndeveloper\n& designer",
"heroText": "I craft digital solutions for human problems\nthat look good and work well.",
"servicesEyebrow": "Capabilities",
"servicesTitle": "Digital work shaped around what your business actually needs.",
"servicesText": "From brand direction and websites to stores, SEO, tracking, WordPress plugins, and internal systems, the goal is the same: clearer structure, better performance, and room to grow.",
"servicesPrimaryCta": "Tell me about your project",
"servicesSecondaryCta": "See my work",
"serviceBrand": "Brand systems and visual direction.",
"serviceWebsite": "Business websites with clear structure.",
"serviceStore": "Stores built for easier selling.",
"serviceWordPress": "Custom WordPress plugin development.",
"serviceSeo": "Search visibility and technical SEO.",
"serviceTracking": "Tracking setup and user insights.",
"serviceSystem": "Custom internal systems for teams.",
"toPortfolio": "View portfolio",
"toContact": "Contact me"
"meta": {
"title": "Full-stack developer portfolio",
"description": "Modern digital products, scalable systems, and multilingual experiences built with product focus."
},
"heroVisual": {
"kicker": "Hi, I am Moh",
"title": "frontend\ndeveloper\n& designer",
"description": "I craft digital solutions for human problems\nthat look good and work well."
},
"hero": {
"availability": "Available for selected projects",
"name": "Moh Farawati",
"headline": "Full-stack products with sharp frontend execution and reliable backend systems.",
"description": "I design and build premium web experiences for teams that need clarity, speed, and production-ready engineering from strategy to launch.",
"primaryCta": "Start a project",
"secondaryCta": "View selected work",
"locationLabel": "Base",
"locationValue": "Berlin, Germany",
"roleLabel": "Role",
"roleValue": "Full-stack developer",
"focusLabel": "Current focus",
"focusValue": "High-performance products, automation, and maintainable platform work.",
"highlights": [
"3-language delivery",
"Product-minded execution",
"Technical partner for launch-ready teams"
]
},
"bento": {
"eyebrow": "Highlights",
"title": "Built for teams that care about quality, speed, and clean technical decisions.",
"description": "The homepage is structured around the way I work: strategy first, strong execution second, and no wasted surface area in between.",
"about": {
"eyebrow": "About me",
"title": "I work across product, engineering, and delivery.",
"description": "My work combines product thinking with hands-on implementation, so founders, agencies, and technical teams get a developer who can move from architecture to shipped detail without losing quality.",
"points": [
"Frontend systems with strong UX discipline",
"Backend services and integrations for real workflows",
"Clear communication with technical and non-technical stakeholders",
"Shipping with maintainability in mind from day one"
]
},
"coreStack": {
"eyebrow": "Core stack",
"title": "Daily tools",
"items": [
"Next.js",
"TypeScript",
"React",
"Node.js",
"PostgreSQL",
"Tailwind CSS",
"Prisma",
"Docker"
]
},
"availability": {
"eyebrow": "Availability",
"title": "Open for focused collaborations",
"description": "Best fit for startups, agencies, and teams that want one technical owner who can drive implementation end to end.",
"status": "New slots open"
},
"currentFocus": {
"eyebrow": "Current focus",
"title": "What I am building most",
"items": [
"Marketing sites with product-grade UX",
"Admin tooling and internal dashboards",
"Automation flows around content and operations"
]
},
"contact": {
"eyebrow": "Quick contact",
"title": "Short path to reach me",
"description": "If the brief is already clear, send the context and timeline and I will reply with the right next step.",
"emailLabel": "Email",
"emailValue": "hello@moh-sass.dev"
},
"highlights": {
"eyebrow": "Highlights",
"title": "Working principles",
"items": [
{
"value": "3",
"label": "Supported languages"
},
{
"value": "E2E",
"label": "Delivery ownership"
},
{
"value": "24/7",
"label": "Production mindset"
}
]
}
},
"marquee": {
"eyebrow": "Trusted tools",
"title": "A working stack chosen for maintainability, speed, and long-term leverage.",
"description": "The marquee pulls from the existing site configuration so the homepage stays aligned with the tools and capabilities you want to highlight."
},
"projects": {
"eyebrow": "Selected projects",
"title": "Three examples of the kind of work I like to ship.",
"description": "Featured work combines strong interface quality with technical structure that stays stable after launch.",
"stackLabel": "Stack",
"cta": "Open project",
"fallbackMeta": "Curated work",
"stackSets": [
[
"Next.js",
"TypeScript",
"Design Systems"
],
[
"APIs",
"PostgreSQL",
"Automation"
],
[
"Tailwind CSS",
"Prisma",
"Infrastructure"
]
],
"fallbackItems": [
{
"title": "Product platform overhaul",
"summary": "A production-minded refresh for a service platform with clearer UX, stronger architecture, and faster delivery cycles.",
"stack": [
"Next.js",
"TypeScript",
"PostgreSQL"
],
"href": "/portfolio"
},
{
"title": "Operations dashboard",
"summary": "Internal tooling focused on decision speed, cleaner workflows, and less manual coordination across teams.",
"stack": [
"React",
"Node.js",
"Automation"
],
"href": "/portfolio"
},
{
"title": "Launch website system",
"summary": "A reusable website foundation for rapid launches without compromising technical quality or editorial control.",
"stack": [
"Tailwind CSS",
"CMS",
"Performance"
],
"href": "/contact"
}
]
},
"capabilities": {
"eyebrow": "Capabilities",
"title": "End-to-end technical coverage without losing product focus.",
"description": "Each capability area is shaped around production work, not generic service labels.",
"items": [
{
"title": "Frontend Engineering",
"description": "Responsive interfaces, design systems, interaction polish, and component architecture built for scale."
},
{
"title": "Backend Systems",
"description": "Structured APIs, integrations, data modeling, and application logic designed for dependable delivery."
},
{
"title": "DevOps & Infrastructure",
"description": "Deployments, environments, observability basics, and infrastructure decisions that reduce operational drag."
},
{
"title": "Automation & AI Workflows",
"description": "Practical automation layers that remove manual work and connect product, content, and operational tasks."
}
]
},
"process": {
"eyebrow": "Process",
"title": "A compact workflow that keeps projects moving.",
"description": "The process is intentionally simple: reduce ambiguity early, make sound technical decisions, and ship with discipline.",
"steps": [
{
"title": "Discover",
"description": "Clarify goals, constraints, audience, and the real outcome the product needs to drive."
},
{
"title": "Architect",
"description": "Define the system shape, technical boundaries, content structure, and delivery path before heavy implementation."
},
{
"title": "Build",
"description": "Implement with strong UI detail, clean application logic, and maintainable structure across the stack."
},
{
"title": "Ship",
"description": "Launch with confidence, validate the result, and leave the product in a stable state for the next phase."
}
]
},
"contactCta": {
"eyebrow": "Start the conversation",
"title": "If the project needs strong execution, I can help move it forward.",
"description": "Bring the brief, the bottleneck, or the rough direction. I can help shape the scope, design the system, and build the product.",
"contactCta": "Contact me",
"githubCta": "GitHub",
"emailLabel": "Email",
"emailValue": "hello@moh-sass.dev",
"availabilityLabel": "Availability",
"availabilityValue": "Open for focused product work",
"githubHref": "https://github.com/mohfarawati"
}
},
"portfolioPage": {
"title": "Portfolio",