Refine home hero and header experience
This commit is contained in:
+14
-16
@@ -368,34 +368,32 @@ html[lang="ar"] {
|
|||||||
|
|
||||||
.hero-title-accent {
|
.hero-title-accent {
|
||||||
background-image: linear-gradient(
|
background-image: linear-gradient(
|
||||||
120deg,
|
135deg,
|
||||||
hsl(var(--hero-ink)) 0%,
|
hsl(var(--primary)) 0%,
|
||||||
hsl(var(--hero-ink)) 24%,
|
color-mix(in srgb, hsl(var(--primary)) 82%, hsl(var(--foreground))) 100%
|
||||||
hsl(var(--primary) / 0.58) 52%,
|
|
||||||
hsl(var(--hero-right) / 0.34) 78%,
|
|
||||||
hsl(var(--hero-ink)) 100%
|
|
||||||
);
|
);
|
||||||
background-size: 220% 220%;
|
|
||||||
background-clip: text;
|
background-clip: text;
|
||||||
-webkit-background-clip: text;
|
-webkit-background-clip: text;
|
||||||
color: transparent;
|
color: transparent;
|
||||||
animation: hero-title-shift 8s ease-in-out infinite alternate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-title-accent-soft {
|
.hero-title-accent-soft {
|
||||||
|
color: hsl(var(--hero-ink));
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .hero-title-accent {
|
||||||
background-image: linear-gradient(
|
background-image: linear-gradient(
|
||||||
120deg,
|
135deg,
|
||||||
hsl(var(--hero-ink)) 0%,
|
hsl(var(--primary)) 0%,
|
||||||
hsl(var(--hero-ink)) 34%,
|
color-mix(in srgb, hsl(var(--primary)) 74%, hsl(var(--foreground))) 100%
|
||||||
hsl(var(--hero-left) / 0.26) 62%,
|
|
||||||
hsl(var(--primary) / 0.24) 82%,
|
|
||||||
hsl(var(--hero-ink)) 100%
|
|
||||||
);
|
);
|
||||||
background-size: 220% 220%;
|
|
||||||
background-clip: text;
|
background-clip: text;
|
||||||
-webkit-background-clip: text;
|
-webkit-background-clip: text;
|
||||||
color: transparent;
|
color: transparent;
|
||||||
animation: hero-title-shift 10s ease-in-out infinite alternate;
|
}
|
||||||
|
|
||||||
|
.dark .hero-title-accent-soft {
|
||||||
|
color: hsl(var(--hero-ink));
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-scroll-link {
|
.hero-scroll-link {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
import { motion, useReducedMotion } from "framer-motion";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
HeroContentMotion,
|
HeroContentMotion,
|
||||||
HeroMotionItem,
|
HeroMotionItem,
|
||||||
HeroScrollLink,
|
|
||||||
HeroShell,
|
HeroShell,
|
||||||
HeroTitle,
|
HeroTitle,
|
||||||
} from "@/components/layout/site-hero";
|
} from "@/components/layout/site-hero";
|
||||||
@@ -19,36 +20,83 @@ export function HomeHero({
|
|||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
}: HomeHeroProps) {
|
}: HomeHeroProps) {
|
||||||
|
const reducedMotion = useReducedMotion();
|
||||||
const titleLines = title.split("\n").filter(Boolean);
|
const titleLines = title.split("\n").filter(Boolean);
|
||||||
const isArabic = locale === "ar";
|
const isArabic = locale === "ar";
|
||||||
const lines = titleLines.map((line, index) => ({
|
const lines = isArabic
|
||||||
text: line,
|
? [
|
||||||
tone: isArabic
|
{
|
||||||
? index === 0
|
text: titleLines[0] ?? "",
|
||||||
? "accent"
|
align: "center" as const,
|
||||||
: "default"
|
segments: [
|
||||||
: index === titleLines.length - 1
|
{ text: "مطوّر ", tone: "accent" as const },
|
||||||
? "accent"
|
{ text: "واجهات", tone: "soft" as const },
|
||||||
: index === 0
|
{ text: " ويب", tone: "accent" as const },
|
||||||
? "soft"
|
],
|
||||||
: "default",
|
},
|
||||||
align: isArabic
|
{
|
||||||
? "center"
|
text: titleLines[1] ?? "",
|
||||||
: index === 1
|
align: "center" as const,
|
||||||
? "end"
|
segments: [
|
||||||
: "start",
|
{ text: "ومصمّم ", tone: "soft" as const },
|
||||||
})) as {
|
{ text: "جرافيك", tone: "accent" as const },
|
||||||
text: string;
|
],
|
||||||
tone: "default" | "accent" | "soft";
|
},
|
||||||
align: "start" | "center" | "end";
|
]
|
||||||
}[];
|
: [
|
||||||
|
{
|
||||||
|
text: titleLines[0] ?? "",
|
||||||
|
tone: "soft" as const,
|
||||||
|
align: "start" as const,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: titleLines[1] ?? "",
|
||||||
|
tone: "accent" as const,
|
||||||
|
align: "end" as const,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: titleLines[2] ?? "",
|
||||||
|
segments: [
|
||||||
|
{ text: "& ", tone: "accent" as const },
|
||||||
|
{ text: "designer", tone: "soft" as const },
|
||||||
|
],
|
||||||
|
align: "start" as const,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HeroShell showBridges>
|
<HeroShell showBridges>
|
||||||
<HeroContentMotion variant="home">
|
<HeroContentMotion variant="home">
|
||||||
<HeroMotionItem variant="home">
|
<HeroMotionItem variant="home">
|
||||||
<p className="max-w-[34rem] text-sm font-medium tracking-[0.12em] text-foreground/62 sm:text-base">
|
<p className="inline-flex items-center gap-1.5 rounded-pill border border-border/70 bg-background/75 px-3 py-1 text-[0.84rem] font-medium tracking-[-0.01em] text-foreground/82 shadow-xs backdrop-blur-chrome sm:px-3.5 sm:py-1 sm:text-[0.9rem]">
|
||||||
{kicker}
|
<span>{kicker}</span>
|
||||||
|
<motion.span
|
||||||
|
aria-hidden="true"
|
||||||
|
className="text-[0.95rem] leading-none"
|
||||||
|
initial={reducedMotion ? false : { rotate: 0, scale: 1 }}
|
||||||
|
animate={
|
||||||
|
reducedMotion
|
||||||
|
? undefined
|
||||||
|
: {
|
||||||
|
rotate: [0, 16, -10, 16, -4, 0],
|
||||||
|
scale: [1, 1.08, 1.08, 1.08, 1.04, 1],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transition={
|
||||||
|
reducedMotion
|
||||||
|
? undefined
|
||||||
|
: {
|
||||||
|
duration: 1.4,
|
||||||
|
delay: 0.5,
|
||||||
|
repeat: Number.POSITIVE_INFINITY,
|
||||||
|
repeatDelay: 2.8,
|
||||||
|
ease: "easeInOut",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
style={{ transformOrigin: "70% 70%" }}
|
||||||
|
>
|
||||||
|
👋
|
||||||
|
</motion.span>
|
||||||
</p>
|
</p>
|
||||||
</HeroMotionItem>
|
</HeroMotionItem>
|
||||||
<HeroTitle locale={locale} lines={lines} />
|
<HeroTitle locale={locale} lines={lines} />
|
||||||
@@ -57,13 +105,6 @@ export function HomeHero({
|
|||||||
{description}
|
{description}
|
||||||
</p>
|
</p>
|
||||||
</HeroMotionItem>
|
</HeroMotionItem>
|
||||||
<HeroMotionItem variant="home" className="mt-9 flex items-center justify-center">
|
|
||||||
<HeroScrollLink
|
|
||||||
href="#home-content"
|
|
||||||
label="Scroll to content"
|
|
||||||
className="text-[hsl(var(--hero-ink)/0.78)] dark:text-foreground/82"
|
|
||||||
/>
|
|
||||||
</HeroMotionItem>
|
|
||||||
</HeroContentMotion>
|
</HeroContentMotion>
|
||||||
</HeroShell>
|
</HeroShell>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ export function SiteHeader({
|
|||||||
}: SiteHeaderProps) {
|
}: SiteHeaderProps) {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [isScrolled, setIsScrolled] = useState(false);
|
const [isScrolled, setIsScrolled] = useState(false);
|
||||||
|
const [logoReplayKey, setLogoReplayKey] = useState(0);
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const locale = useLocale();
|
const locale = useLocale();
|
||||||
const t = useTranslations("navigation");
|
const t = useTranslations("navigation");
|
||||||
@@ -137,7 +138,7 @@ export function SiteHeader({
|
|||||||
? "مــــــطـــــــــــــور ويــــــــــب | فــــــــــــريــــــلانــــــســــــر"
|
? "مــــــطـــــــــــــور ويــــــــــب | فــــــــــــريــــــلانــــــســــــر"
|
||||||
: "Webdesigner Bremen | Freelancer";
|
: "Webdesigner Bremen | Freelancer";
|
||||||
const desktopControlRailClassName =
|
const desktopControlRailClassName =
|
||||||
"inline-flex items-center gap-1 rounded-pill border border-border/70 bg-background/80 p-1 shadow-panel backdrop-blur-chrome";
|
"inline-flex items-center gap-1 rounded-pill border border-border/70 bg-background/80 p-1 backdrop-blur-chrome";
|
||||||
const desktopControlButtonClassName =
|
const desktopControlButtonClassName =
|
||||||
"h-9 w-9 rounded-pill border border-transparent p-0 text-foreground/80 hover:bg-accent hover:text-foreground";
|
"h-9 w-9 rounded-pill border border-transparent p-0 text-foreground/80 hover:bg-accent hover:text-foreground";
|
||||||
const mobileControlButtonClassName =
|
const mobileControlButtonClassName =
|
||||||
@@ -158,7 +159,7 @@ export function SiteHeader({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="fixed inset-x-0 top-3 z-40 bg-transparent">
|
<header className="fixed inset-x-0 top-3 z-40 bg-transparent">
|
||||||
<Container className="max-w-[88rem]">
|
<Container className="max-w-[88rem] px-5 sm:px-6 lg:px-8">
|
||||||
<div className="relative grid min-h-[4.5rem] grid-cols-[auto_1fr_auto] items-center gap-3 lg:min-h-[5rem]">
|
<div className="relative grid min-h-[4.5rem] grid-cols-[auto_1fr_auto] items-center gap-3 lg:min-h-[5rem]">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={false}
|
initial={false}
|
||||||
@@ -172,21 +173,16 @@ export function SiteHeader({
|
|||||||
<Link
|
<Link
|
||||||
href={getLocalizedPath(locale)}
|
href={getLocalizedPath(locale)}
|
||||||
className="inline-flex items-center gap-2.5 rounded-pill px-1 py-1 sm:gap-3 lg:py-2"
|
className="inline-flex items-center gap-2.5 rounded-pill px-1 py-1 sm:gap-3 lg:py-2"
|
||||||
|
onClick={() => setLogoReplayKey((value) => value + 1)}
|
||||||
>
|
>
|
||||||
<motion.span
|
<SiteLogo
|
||||||
initial={reducedMotion ? false : isArabic ? { opacity: 0, x: 18, scale: 0.82, rotate: 14, filter: "blur(10px)" } : { opacity: 0, x: -18, scale: 0.82, rotate: -14, filter: "blur(10px)" }}
|
lightLogoUrl={lightLogoUrl}
|
||||||
animate={{ opacity: 1, x: 0, scale: 1, rotate: 0, filter: "blur(0px)" }}
|
darkLogoUrl={darkLogoUrl}
|
||||||
transition={reducedMotion ? { duration: 0 } : { duration: 0.32, ease: [0.22, 1, 0.36, 1] }}
|
alt="mohfarawati"
|
||||||
className="relative inline-flex items-center justify-center [transform-style:preserve-3d]"
|
priority
|
||||||
>
|
className="relative h-10 opacity-95"
|
||||||
<SiteLogo
|
replayKey={logoReplayKey}
|
||||||
lightLogoUrl={lightLogoUrl}
|
/>
|
||||||
darkLogoUrl={darkLogoUrl}
|
|
||||||
alt="mohfarawati"
|
|
||||||
priority
|
|
||||||
className="relative h-10 opacity-95"
|
|
||||||
/>
|
|
||||||
</motion.span>
|
|
||||||
<motion.span
|
<motion.span
|
||||||
initial={isArabic ? false : "hidden"}
|
initial={isArabic ? false : "hidden"}
|
||||||
animate="visible"
|
animate="visible"
|
||||||
@@ -250,7 +246,7 @@ export function SiteHeader({
|
|||||||
animate={isScrolled ? { y: -1, scale: 0.985 } : { y: 0, scale: 1 }}
|
animate={isScrolled ? { y: -1, scale: 0.985 } : { y: 0, scale: 1 }}
|
||||||
transition={reducedMotion ? { duration: 0 } : { duration: 0.3, ease: [0.22, 1, 0.36, 1] }}
|
transition={reducedMotion ? { duration: 0 } : { duration: 0.3, ease: [0.22, 1, 0.36, 1] }}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center gap-1 rounded-pill border border-border/70 bg-background/80 p-1.5 shadow-panel backdrop-blur-chrome",
|
"inline-flex items-center gap-1 rounded-pill border border-border/70 bg-background/80 p-1.5 backdrop-blur-chrome",
|
||||||
isArabic ? "w-auto max-w-none" : "min-w-[27rem] max-w-[42rem]",
|
isArabic ? "w-auto max-w-none" : "min-w-[27rem] max-w-[42rem]",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -298,7 +294,7 @@ export function SiteHeader({
|
|||||||
onClick={() => setIsOpen((open) => !open)}
|
onClick={() => setIsOpen((open) => !open)}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
className="col-start-3 ml-auto h-11 w-11 justify-self-end rounded-pill border border-border/70 bg-background/80 text-foreground shadow-panel backdrop-blur-chrome hover:bg-accent lg:hidden"
|
className="col-start-3 ml-auto h-11 w-11 justify-self-end rounded-pill border border-border/70 bg-background/80 text-foreground backdrop-blur-chrome hover:bg-accent lg:hidden"
|
||||||
aria-label={isOpen ? t("closeMenu") : t("openMenu")}
|
aria-label={isOpen ? t("closeMenu") : t("openMenu")}
|
||||||
>
|
>
|
||||||
<motion.span
|
<motion.span
|
||||||
@@ -321,13 +317,13 @@ export function SiteHeader({
|
|||||||
transition={{ duration: 0.22, ease: "easeOut" }}
|
transition={{ duration: 0.22, ease: "easeOut" }}
|
||||||
className="bg-transparent lg:hidden"
|
className="bg-transparent lg:hidden"
|
||||||
>
|
>
|
||||||
<Container className="pb-2 pt-3">
|
<Container className="px-5 pb-2 pt-3 sm:px-6 lg:px-8">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, scale: 0.98 }}
|
initial={{ opacity: 0, scale: 0.98 }}
|
||||||
animate={{ opacity: 1, scale: 1 }}
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
exit={{ opacity: 0, scale: 0.98 }}
|
exit={{ opacity: 0, scale: 0.98 }}
|
||||||
transition={{ duration: 0.2, ease: "easeOut" }}
|
transition={{ duration: 0.2, ease: "easeOut" }}
|
||||||
className="overflow-hidden rounded-surface border border-border/70 bg-card/95 shadow-panel backdrop-blur-chrome"
|
className="overflow-hidden rounded-surface border border-border/70 bg-card/95 backdrop-blur-chrome"
|
||||||
>
|
>
|
||||||
<motion.nav
|
<motion.nav
|
||||||
initial="closed"
|
initial="closed"
|
||||||
|
|||||||
@@ -12,8 +12,14 @@ import { cn } from "@/lib/utils";
|
|||||||
type HeroTone = "default" | "accent" | "soft";
|
type HeroTone = "default" | "accent" | "soft";
|
||||||
type HeroLineAlign = "start" | "center" | "end";
|
type HeroLineAlign = "start" | "center" | "end";
|
||||||
|
|
||||||
|
type HeroTitleSegment = {
|
||||||
|
text: string;
|
||||||
|
tone?: HeroTone;
|
||||||
|
};
|
||||||
|
|
||||||
type HeroTitleLine = {
|
type HeroTitleLine = {
|
||||||
text: string;
|
text: string;
|
||||||
|
segments?: HeroTitleSegment[];
|
||||||
tone?: HeroTone;
|
tone?: HeroTone;
|
||||||
align?: HeroLineAlign;
|
align?: HeroLineAlign;
|
||||||
};
|
};
|
||||||
@@ -55,9 +61,9 @@ const toneClasses: Record<HeroTone, string> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const lineAlignClasses: Record<HeroLineAlign, string> = {
|
const lineAlignClasses: Record<HeroLineAlign, string> = {
|
||||||
start: "self-start text-start",
|
start: "self-center text-center lg:self-start lg:text-start",
|
||||||
center: "self-center text-center",
|
center: "self-center text-center",
|
||||||
end: "self-end text-end",
|
end: "self-center text-center lg:self-end lg:text-end",
|
||||||
};
|
};
|
||||||
|
|
||||||
export function HeroShell({
|
export function HeroShell({
|
||||||
@@ -97,7 +103,7 @@ export function HeroShell({
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"mx-auto flex w-full flex-col items-center text-center",
|
"mx-auto flex w-full flex-col items-center text-center",
|
||||||
variant === "home" ? "max-w-[58rem]" : "max-w-[46rem]",
|
variant === "home" ? "max-w-[68rem]" : "max-w-[46rem]",
|
||||||
contentClassName,
|
contentClassName,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -183,9 +189,9 @@ export function HeroTitle({
|
|||||||
"text-balance font-semibold text-[hsl(var(--hero-ink))]",
|
"text-balance font-semibold text-[hsl(var(--hero-ink))]",
|
||||||
variant === "home"
|
variant === "home"
|
||||||
? isArabic
|
? isArabic
|
||||||
? "mt-6 flex w-full max-w-[16rem] flex-col gap-y-1 text-[clamp(3rem,11vw,5.9rem)] leading-[1.06] tracking-[-0.03em] md:max-w-[22rem] lg:max-w-[32rem]"
|
? "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-[16rem] flex-col gap-y-1 text-[clamp(3.1rem,11vw,6.3rem)] leading-[0.94] tracking-[-0.06em] md:max-w-[22rem] 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-[16rem] flex-col gap-y-1 text-[clamp(2.8rem,10vw,4.9rem)] leading-[0.96] tracking-[-0.06em] md:max-w-[22rem] 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]",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -194,11 +200,23 @@ export function HeroTitle({
|
|||||||
key={`${index}-${line.text}`}
|
key={`${index}-${line.text}`}
|
||||||
className={cn(
|
className={cn(
|
||||||
"hero-title-line",
|
"hero-title-line",
|
||||||
toneClasses[line.tone ?? "default"],
|
|
||||||
lineAlignClasses[line.align ?? "center"],
|
lineAlignClasses[line.align ?? "center"],
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{line.text}
|
{line.segments?.length
|
||||||
|
? line.segments.map((segment, segmentIndex) => (
|
||||||
|
<span
|
||||||
|
key={`${index}-${segmentIndex}-${segment.text}`}
|
||||||
|
className={cn(toneClasses[segment.tone ?? "default"])}
|
||||||
|
>
|
||||||
|
{segment.text}
|
||||||
|
</span>
|
||||||
|
))
|
||||||
|
: (
|
||||||
|
<span className={cn(toneClasses[line.tone ?? "default"])}>
|
||||||
|
{line.text}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { motion } from "framer-motion";
|
import { motion, useAnimationControls, useReducedMotion } from "framer-motion";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
@@ -13,6 +13,7 @@ type SiteLogoProps = {
|
|||||||
alt: string;
|
alt: string;
|
||||||
priority?: boolean;
|
priority?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
replayKey?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function SiteLogo({
|
export function SiteLogo({
|
||||||
@@ -21,14 +22,46 @@ export function SiteLogo({
|
|||||||
alt,
|
alt,
|
||||||
priority = false,
|
priority = false,
|
||||||
className,
|
className,
|
||||||
|
replayKey = 0,
|
||||||
}: SiteLogoProps) {
|
}: SiteLogoProps) {
|
||||||
const { theme, resolvedTheme } = useTheme();
|
const { theme, resolvedTheme } = useTheme();
|
||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
|
const controls = useAnimationControls();
|
||||||
|
const reducedMotion = useReducedMotion();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMounted(true);
|
setMounted(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (reducedMotion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
controls.set({
|
||||||
|
opacity: 0,
|
||||||
|
x: -42,
|
||||||
|
scale: 0.88,
|
||||||
|
rotate: -360,
|
||||||
|
filter: "blur(8px)",
|
||||||
|
});
|
||||||
|
|
||||||
|
void controls.start({
|
||||||
|
opacity: 1,
|
||||||
|
x: 0,
|
||||||
|
scale: 1,
|
||||||
|
rotate: 0,
|
||||||
|
filter: "blur(0px)",
|
||||||
|
transition: {
|
||||||
|
x: { duration: 0.78, ease: [0.22, 1, 0.36, 1] },
|
||||||
|
opacity: { duration: 0.46 },
|
||||||
|
rotate: { duration: 0.56, ease: "easeOut" },
|
||||||
|
scale: { duration: 0.72, ease: [0.22, 1, 0.36, 1] },
|
||||||
|
filter: { duration: 0.42, ease: [0.22, 1, 0.36, 1] },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, [controls, reducedMotion, replayKey]);
|
||||||
|
|
||||||
const activeTheme = theme === "system" ? resolvedTheme : theme;
|
const activeTheme = theme === "system" ? resolvedTheme : theme;
|
||||||
const isDark = mounted && activeTheme === "dark";
|
const isDark = mounted && activeTheme === "dark";
|
||||||
const fallbackSrc = isDark ? "/logos/dark-primary.svg" : "/logos/light-primary.svg";
|
const fallbackSrc = isDark ? "/logos/dark-primary.svg" : "/logos/light-primary.svg";
|
||||||
@@ -37,11 +70,10 @@ export function SiteLogo({
|
|||||||
return (
|
return (
|
||||||
<motion.span
|
<motion.span
|
||||||
className="block [transform-style:preserve-3d]"
|
className="block [transform-style:preserve-3d]"
|
||||||
initial={{ opacity: 0, x: -42, scale: 0.88, rotate: -360 }}
|
initial={reducedMotion ? false : undefined}
|
||||||
animate={{ opacity: 1, x: 0, scale: 1, rotate: 0 }}
|
animate={reducedMotion ? undefined : controls}
|
||||||
whileHover={{ scale: 1.05, y: -2, rotate: -2, filter: "drop-shadow(0 16px 28px rgba(15,23,42,0.14))" }}
|
whileHover={{ scale: 1.05, y: -2, rotate: -2, filter: "drop-shadow(0 16px 28px rgba(15,23,42,0.14))" }}
|
||||||
whileTap={{ scale: 0.9, rotate: 6, y: 1, filter: "drop-shadow(0 8px 18px rgba(15,23,42,0.18))" }}
|
whileTap={{ scale: 0.9, rotate: 6, y: 1, filter: "drop-shadow(0 8px 18px rgba(15,23,42,0.18))" }}
|
||||||
transition={{ x: { duration: 0.78, ease: [0.22, 1, 0.36, 1] }, opacity: { duration: 0.46 }, rotate: { duration: 0.56, ease: "easeOut" }, scale: { duration: 0.72, ease: [0.22, 1, 0.36, 1] } }}
|
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src={src}
|
src={src}
|
||||||
@@ -51,6 +83,7 @@ export function SiteLogo({
|
|||||||
sizes="(min-width: 1280px) 360px, (min-width: 1024px) 320px, (min-width: 640px) 280px, 240px"
|
sizes="(min-width: 1280px) 360px, (min-width: 1024px) 320px, (min-width: 640px) 280px, 240px"
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-9 w-auto object-contain object-left drop-shadow-[0_10px_24px_rgba(15,23,42,0.12)] transition-[filter,opacity,transform] duration-300 ease-out sm:h-10 lg:h-11 xl:h-12",
|
"h-9 w-auto object-contain object-left drop-shadow-[0_10px_24px_rgba(15,23,42,0.12)] transition-[filter,opacity,transform] duration-300 ease-out sm:h-10 lg:h-11 xl:h-12",
|
||||||
|
mounted ? "opacity-100" : "opacity-0",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
priority={priority}
|
priority={priority}
|
||||||
|
|||||||
+1
-1
@@ -28,7 +28,7 @@
|
|||||||
},
|
},
|
||||||
"homepage": {
|
"homepage": {
|
||||||
"heroKicker": "مرحبااا، أنا محمد",
|
"heroKicker": "مرحبااا، أنا محمد",
|
||||||
"heroTitle": "مطور واجهات ويب\nومصمم جرافيك",
|
"heroTitle": "مطوّر واجهات ويب\nومصمّم جرافيك",
|
||||||
"heroText": "بشتغل على حلول رقمية لمشاكل حقيقية\nبتطلع ممتازة وبتشتغل بكفاءة.",
|
"heroText": "بشتغل على حلول رقمية لمشاكل حقيقية\nبتطلع ممتازة وبتشتغل بكفاءة.",
|
||||||
"servicesEyebrow": "شو فيني قدّم",
|
"servicesEyebrow": "شو فيني قدّم",
|
||||||
"servicesTitle": "حلول رقمية مصممة حسب شغلك، مو قوالب جاهزة.",
|
"servicesTitle": "حلول رقمية مصممة حسب شغلك، مو قوالب جاهزة.",
|
||||||
|
|||||||
Reference in New Issue
Block a user