feat: change color

This commit is contained in:
2026-02-05 19:58:48 +03:30
parent aa6f3c9b35
commit e21abe3443
8 changed files with 199 additions and 101 deletions
+52 -52
View File
@@ -122,71 +122,71 @@
:root {
--font-iran-sans: "IRANSansX", var(--font-geist-sans);
--radius: 0.625rem;
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: #001BB7;
--card: oklch(1 0.006 175);
--card-foreground: oklch(0.145 0.02 175);
--popover: oklch(1 0.006 175);
--popover-foreground: oklch(0.145 0.02 175);
--primary: #01796F;
--primary-foreground: oklch(0.985 0 0);
--secondary: #0046FF;
--secondary: #0D9488;
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.97 0.01 280);
--muted-foreground: oklch(0.35 0.05 250);
--muted: oklch(0.97 0.012 175);
--muted-foreground: oklch(0.35 0.05 175);
--accent: #FCC61D;
--accent-foreground: oklch(0.25 0.05 50);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.9 0.01 280);
--input: oklch(0.95 0.01 280);
--ring: oklch(0.5 0.15 220);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--border: oklch(0.9 0.012 175);
--input: oklch(0.95 0.01 175);
--ring: oklch(0.45 0.1 175);
--chart-1: oklch(0.45 0.1 175);
--chart-2: oklch(0.6 0.12 162);
--chart-3: oklch(0.55 0.08 175);
--chart-4: oklch(0.7 0.1 170);
--chart-5: oklch(0.5 0.09 180);
--sidebar: oklch(0.985 0.008 175);
--sidebar-foreground: oklch(0.145 0.02 175);
--sidebar-primary: #01796F;
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
--background: oklch(0.99 0.005 280);
--foreground: oklch(0.25 0.05 250);
--sidebar-accent: oklch(0.97 0.015 175);
--sidebar-accent-foreground: oklch(0.205 0.02 175);
--sidebar-border: oklch(0.922 0.01 175);
--sidebar-ring: oklch(0.45 0.1 175);
--background: oklch(0.99 0.008 175);
--foreground: oklch(0.25 0.03 175);
}
.dark {
--background: oklch(0.145 0.01 280);
--background: oklch(0.145 0.02 175);
--foreground: oklch(0.95 0.01 280);
--card: oklch(0.22 0.02 280);
--card: oklch(0.22 0.025 175);
--card-foreground: oklch(0.95 0.01 280);
--popover: oklch(0.22 0.02 280);
--popover: oklch(0.22 0.025 175);
--popover-foreground: oklch(0.95 0.01 280);
--primary: #001BB7;
--primary: #01796F;
--primary-foreground: oklch(0.985 0 0);
--secondary: #0046FF;
--secondary-foreground: oklch(0.95 0.01 280);
--muted: oklch(0.28 0.02 280);
--muted-foreground: oklch(0.75 0.02 280);
--secondary: #14B8A6;
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.28 0.025 175);
--muted-foreground: oklch(0.75 0.02 175);
--accent: #FCC61D;
--accent-foreground: oklch(0.2 0.05 50);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(0.3 0.02 280);
--input: oklch(0.3 0.02 280);
--ring: oklch(0.5 0.15 220);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.22 0.02 280);
--border: oklch(0.3 0.025 175);
--input: oklch(0.3 0.025 175);
--ring: oklch(0.5 0.12 175);
--chart-1: oklch(0.5 0.12 175);
--chart-2: oklch(0.6 0.14 162);
--chart-3: oklch(0.55 0.1 175);
--chart-4: oklch(0.65 0.11 170);
--chart-5: oklch(0.45 0.09 180);
--sidebar: oklch(0.22 0.025 175);
--sidebar-foreground: oklch(0.95 0.01 280);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary: #01796F;
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.3 0.02 280);
--sidebar-accent: oklch(0.3 0.03 175);
--sidebar-accent-foreground: oklch(0.95 0.01 280);
--sidebar-border: oklch(0.3 0.02 280);
--sidebar-ring: oklch(0.5 0.15 220);
--sidebar-border: oklch(0.3 0.025 175);
--sidebar-ring: oklch(0.5 0.12 175);
}
@layer base {
@@ -202,17 +202,17 @@
letter-spacing: normal;
line-height: 1.6;
background-image:
radial-gradient(at 0% 0%, oklch(0.98 0.01 280 / 0.3) 0px, transparent 50%),
radial-gradient(at 100% 100%, oklch(0.95 0.02 220 / 0.2) 0px, transparent 50%),
radial-gradient(at 50% 50%, oklch(0.97 0.01 300 / 0.15) 0px, transparent 50%);
radial-gradient(at 0% 0%, oklch(0.98 0.012 175 / 0.3) 0px, transparent 50%),
radial-gradient(at 100% 100%, oklch(0.95 0.02 175 / 0.2) 0px, transparent 50%),
radial-gradient(at 50% 50%, oklch(0.97 0.01 170 / 0.15) 0px, transparent 50%);
background-attachment: fixed;
transition: background-color 0.3s ease;
}
.dark body {
background-image:
radial-gradient(at 0% 0%, oklch(0.2 0.02 280 / 0.3) 0px, transparent 50%),
radial-gradient(at 100% 100%, oklch(0.18 0.02 220 / 0.2) 0px, transparent 50%),
radial-gradient(at 50% 50%, oklch(0.22 0.02 300 / 0.15) 0px, transparent 50%);
radial-gradient(at 0% 0%, oklch(0.2 0.025 175 / 0.3) 0px, transparent 50%),
radial-gradient(at 100% 100%, oklch(0.18 0.02 175 / 0.2) 0px, transparent 50%),
radial-gradient(at 50% 50%, oklch(0.22 0.025 170 / 0.15) 0px, transparent 50%);
}
h1, h2, h3, h4, h5, h6 {
letter-spacing: normal;
+2
View File
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
import "./globals.css";
import { ConditionalLayout } from "@/components/conditional-layout";
import { ThemeProvider } from "@/components/theme-provider";
import { TopProgressBar } from "@/components/top-progress-bar";
export const metadata: Metadata = {
@@ -24,6 +25,7 @@ export default function RootLayout({
className={`antialiased`}
>
<ThemeProvider>
<TopProgressBar />
<ConditionalLayout>{children}</ConditionalLayout>
</ThemeProvider>
</body>
+1 -1
View File
@@ -43,7 +43,7 @@ export function BlogCard({
</div>
<CardHeader className="flex-1 flex flex-col">
<h3 className="text-xl font-bold leading-tight tracking-normal group-hover:text-primary transition-colors line-clamp-2">
<h3 className="text-base py-2 font-bold leading-tight tracking-normal group-hover:text-primary transition-colors line-clamp-2">
{title}
</h3>
</CardHeader>
+1 -1
View File
@@ -92,7 +92,7 @@ export function BlogPostCard({
</div>
)}
<CardHeader className="pb-2">
<CardTitle className="text-xl font-bold leading-tight line-clamp-2 mt-2">
<CardTitle className="text-base font-bold leading-tight line-clamp-2 my-2">
{post.title.rendered}
</CardTitle>
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-muted-foreground bg-muted inline-flex p-1 rounded-sm">
+9 -15
View File
@@ -6,6 +6,10 @@ const links = [
title: "درباره ما",
href: "/about",
},
{
title: "بلاگ",
href: "/blog",
},
{
title: "راه حل‌ها",
href: "/solutions",
@@ -14,11 +18,11 @@ const links = [
export default function FooterSection() {
return (
<footer className="py-16 md:py-32">
<div className="mx-auto max-w-5xl px-6">
<Logo />
<footer className="py-16 md:py-16 bg-muted border-t">
<div className="mx-auto max-w-5xl px-6 flex items-center flex-col">
<Logo className="mx-auto" />
<div className="my-8 flex flex-wrap justify-center gap-6 text-sm">
<div className="my-4 flex flex-wrap justify-center gap-6 text-sm">
{links.map((link, index) => (
<Link
key={index}
@@ -29,18 +33,8 @@ export default function FooterSection() {
</Link>
))}
</div>
<div className="my-8 flex flex-wrap justify-center items-center gap-2 text-sm">
<span className="text-muted-foreground">تماس با تیم فیپ:</span>
<a
href="tel:09162949770"
className="text-foreground hover:text-accent transition-colors duration-150 font-medium"
>
09162949770
</a>
</div>
<span className="text-muted-foreground block text-center text-sm">
{" "}
© {new Date().getFullYear()} فیپ، تمامی حقوق محفوظ است
© 1404 فیپ، تمامی حقوق محفوظ است
</span>
</div>
</footer>
+22 -24
View File
@@ -38,21 +38,22 @@ export const HeroHeader = () => {
<nav
data-state={menuState && 'active'}
className="fixed z-20 w-full px-2">
<div className={cn('mx-auto mt-2 max-w-6xl px-6 transition-all duration-300 lg:px-12 bg-background/80 backdrop-blur-md rounded-2xl border border-border/50', isScrolled && 'bg-background/95 max-w-4xl shadow-lg lg:px-5')}>
<div className="relative flex flex-wrap items-center justify-between gap-6 py-2 lg:gap-0 lg:py-3">
<div className={cn('mx-auto mt-2 max-w-6xl px-2 transition-all duration-300 lg:px-2 bg-background/30 backdrop-blur-md rounded-md border border-border/10', isScrolled && 'bg-background/20 max-w-4xl shadow-lg lg:px-5')}>
<div className="relative flex flex-wrap items-center justify-between gap-2 lg:gap-0">
<div className="flex w-full justify-between lg:w-auto">
<Logo />
<button
onClick={() => setMenuState(!menuState)}
aria-label={menuState == true ? 'بستن منو' : 'باز کردن منو'}
className="relative z-20 -m-2.5 -mr-4 block cursor-pointer p-2.5 lg:hidden">
<Menu className="in-data-[state=active]:rotate-180 in-data-[state=active]:scale-0 in-data-[state=active]:opacity-0 m-auto size-6 duration-200" />
<X className="in-data-[state=active]:rotate-0 in-data-[state=active]:scale-100 in-data-[state=active]:opacity-100 absolute inset-0 m-auto size-6 -rotate-180 scale-0 opacity-0 duration-200" />
<Menu className="in-data-[state=active]:rotate-180 in-data-[state=active]:scale-0 in-data-[state=active]:opacity-0 m-auto size-4 duration-200" />
<X className="in-data-[state=active]:rotate-0 in-data-[state=active]:scale-100 in-data-[state=active]:opacity-100 absolute inset-0 m-auto size-4 -rotate-180 scale-0 opacity-0 duration-200" />
</button>
</div>
<div className="absolute inset-0 m-auto hidden size-fit lg:flex items-center gap-8">
<ul className="flex gap-8 text-sm">
<div className="absolute inset-0 m-auto hidden size-fit lg:flex items-center">
<ul className="flex gap-2 text-sm">
{menuItems.map((item, index) => {
const active = isLinkActive(pathname ?? '', item.href)
return (
@@ -60,51 +61,48 @@ export const HeroHeader = () => {
<Link
href={item.href}
className={cn(
'text-muted-foreground hover:text-foreground transition-all duration-300 relative group',
active && 'text-foreground'
'text-muted-foreground hover:text-foreground hover:bg-accent/20 transition-all duration-300 relative group p-2 px-4 rounded-md',
active && 'text-foreground bg-accent/40'
)}>
<span className="relative z-10">{item.name}</span>
<span className={cn(
'absolute bottom-0 right-0 h-0.5 bg-accent transition-all duration-300 group-hover:w-full',
active ? 'w-full' : 'w-0'
)} />
</Link>
</li>
)
})}
</ul>
<ThemeToggle />
</div>
<div className="bg-background in-data-[state=active]:block lg:in-data-[state=active]:flex mb-6 hidden w-full flex-wrap items-center justify-end space-y-8 rounded-3xl border p-6 shadow-2xl shadow-zinc-300/20 md:flex-nowrap lg:m-0 lg:flex lg:w-fit lg:gap-6 lg:space-y-0 lg:border-transparent lg:bg-transparent lg:p-0 lg:shadow-none dark:shadow-none dark:lg:bg-transparent">
<div className="in-data-[state=active]:block lg:in-data-[state=active]:flex mb-3 hidden w-full flex-wrap items-center justify-end space-y-8 md:flex-nowrap lg:m-0 lg:flex lg:w-fit lg:gap-6 lg:space-y-0 lg:border-transparent lg:bg-transparent lg:p-0 lg:shadow-none dark:shadow-none dark:lg:bg-transparent">
<div className="lg:hidden w-full">
<ul className="space-y-6 text-base">
<ul className="text-base flex flex-col items-stretch">
{menuItems.map((item, index) => {
const active = isLinkActive(pathname ?? '', item.href)
return (
<li key={index}>
<Link
href={item.href}
onClick={() => setMenuState(false)}
className={cn(
'text-muted-foreground hover:text-foreground transition-all duration-300 relative group block',
active && 'text-foreground'
'text-muted-foreground block w-full hover:text-foreground hover:bg-accent/20 transition-all duration-300 relative group p-2 px-4 rounded-md',
active && 'text-foreground bg-accent/40'
)}>
<span className="relative z-10">{item.name}</span>
<span className={cn(
'absolute bottom-0 right-0 h-0.5 bg-accent transition-all duration-300 group-hover:w-full',
active ? 'w-full' : 'w-0'
)} />
</Link>
</li>
)
})}
</ul>
<div className="mt-6 flex justify-end">
<ThemeToggle />
</div>
</div>
</div>
<div className={`flex justify-end max-md:absolute top-0.5 left-8 in-data-[state=active]:block`}>
<ThemeToggle />
</div>
</div>
</div>
</nav>
+8 -8
View File
@@ -10,9 +10,9 @@ export const Logo = ({
uniColor?: boolean;
}) => {
return (
<Link href="/" className="flex items-center space-x-2">
<Link href="/" className="flex items-center">
<LogoIcon className={className} uniColor={uniColor} />
<span className="font-bold text-sm">فیپ</span>
<span className="font-bold text-xl">فـــیــپ</span>
</Link>
);
};
@@ -25,12 +25,12 @@ export const LogoIcon = ({
uniColor?: boolean;
}) => {
return (
<Image
src="/assets/feep-Logo.svg"
alt="Logo"
width={50}
height={50}
className={cn("h-10 w-auto md:h-12 dark:invert", className)}
<Image
src="/assets/feep-Logo.svg"
alt="Logo"
width={50}
height={50}
className={cn("h-10 w-auto md:h-12 dark:invert", className)}
/>
);
};
+104
View File
@@ -0,0 +1,104 @@
"use client";
import { usePathname } from "next/navigation";
import { useCallback, useEffect, useRef, useState } from "react";
const TOP_BAR_HEIGHT = 3;
const MAX_PERCENT = 90;
const COMPLETE_PERCENT = 100;
const TICK_MS = 50;
const INCREMENT = 2;
const COMPLETE_DURATION = 200;
export function TopProgressBar() {
const pathname = usePathname();
const [visible, setVisible] = useState(false);
const [percent, setPercent] = useState(0);
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
const completeTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const prevPathnameRef = useRef(pathname);
const clearTimers = useCallback(() => {
if (timerRef.current) {
clearInterval(timerRef.current);
timerRef.current = null;
}
if (completeTimeoutRef.current) {
clearTimeout(completeTimeoutRef.current);
completeTimeoutRef.current = null;
}
}, []);
const startProgress = useCallback(() => {
clearTimers();
setVisible(true);
setPercent(0);
timerRef.current = setInterval(() => {
setPercent((p) => {
if (p >= MAX_PERCENT) {
if (timerRef.current) {
clearInterval(timerRef.current);
timerRef.current = null;
}
return MAX_PERCENT;
}
return Math.min(p + INCREMENT, MAX_PERCENT);
});
}, TICK_MS);
}, [clearTimers]);
const completeProgress = useCallback(() => {
clearTimers();
setPercent(COMPLETE_PERCENT);
completeTimeoutRef.current = setTimeout(() => {
setVisible(false);
setPercent(0);
completeTimeoutRef.current = null;
}, COMPLETE_DURATION);
}, [clearTimers]);
// Start bar when user clicks an internal link
useEffect(() => {
const handleClick = (e: MouseEvent) => {
const target = (e.target as Element).closest("a");
if (!target?.href) return;
try {
const url = new URL(target.href);
if (url.origin === window.location.origin && url.pathname !== pathname) {
startProgress();
}
} catch {
// ignore invalid URLs
}
};
document.addEventListener("click", handleClick, true);
return () => document.removeEventListener("click", handleClick, true);
}, [pathname, startProgress]);
// Complete bar when pathname changes (route loaded)
useEffect(() => {
if (prevPathnameRef.current !== pathname) {
prevPathnameRef.current = pathname;
if (visible) completeProgress();
}
}, [pathname, visible, completeProgress]);
useEffect(() => {
return clearTimers;
}, [clearTimers]);
if (!visible) return null;
return (
<div
className="fixed left-0 right-0 z-[9999] overflow-hidden bg-transparent"
style={{ height: TOP_BAR_HEIGHT }}
aria-hidden
>
<div
className="h-full bg-primary transition-[width] duration-150 ease-out"
style={{ width: `${percent}%` }}
/>
</div>
);
}