Merge remote-tracking branch 'origin/main' into feat/merge

This commit is contained in:
2026-06-22 19:06:19 +03:30
50 changed files with 962 additions and 215 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ export function BlogBlockquote({ payload }: BlogBlockquoteProps) {
{citation ? (
<footer className="mt-4 border-t border-border/60 pt-4">
<cite className="text-sm text-muted-foreground not-italic">
— {citation}
{citation}
</cite>
</footer>
) : null}
+2 -1
View File
@@ -32,7 +32,8 @@ export function BlogCard({
alt={title}
fill
className="object-cover transition-transform duration-300 group-hover:scale-105"
sizes="(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw"
quality={100}
sizes="(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 400px"
/>
{/* Category Badge */}
<div className="absolute top-4 right-4">
+54 -4
View File
@@ -4,12 +4,13 @@ import { ImageIcon, User } from "lucide-react";
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import { getFeaturedImageUrl, type WpPost } from "@/lib/wp-api";
import { formatDateShamsi } from "@/lib/format-date";
import { formatAuthorName } from "@/lib/blog-utils";
function stripHtml(html: string): string {
return html.replace(/<[^>]*>/g, "").trim();
}
type BlogPostCardVariant = "default" | "tiny";
type BlogPostCardVariant = "default" | "tiny" | "related";
export function BlogPostCard({
post,
@@ -20,9 +21,57 @@ export function BlogPostCard({
}) {
const excerpt = stripHtml(post.excerpt.rendered) || stripHtml(post.content.rendered).slice(0, 160);
const modifiedDate = post.modified ? formatDateShamsi(post.modified) : null;
const authorName = post._embedded?.author?.[0]?.name ?? null;
const authorName = formatAuthorName(post._embedded?.author?.[0]?.name);
const thumbnailUrl = getFeaturedImageUrl(post, "full");
if (variant === "related") {
return (
<Link href={`/blog/${encodeURIComponent(post.slug)}`} className="block h-full min-w-0">
<Card className="h-full overflow-hidden transition-all rounded-md duration-300 hover:shadow-md flex flex-col p-0 gap-0">
<CardContent className="p-3 sm:p-4 grid grid-cols-3 gap-3 sm:gap-4 min-w-0">
{thumbnailUrl ? (
<div className="relative rounded-md aspect-[4/3] w-full overflow-hidden bg-muted shrink-0 col-span-1 min-w-0">
<Image
src={thumbnailUrl}
alt=""
fill
className="object-cover"
quality={100}
sizes="(max-width: 640px) 33vw, (max-width: 1024px) 20vw, 200px"
/>
</div>
) : (
<div
className="relative rounded-md flex aspect-[4/3] w-full items-center justify-center overflow-hidden bg-muted shrink-0 col-span-1"
aria-hidden
>
<ImageIcon className="size-10 text-muted-foreground/50" />
</div>
)}
<div className="col-span-2 min-w-0 flex flex-col justify-center">
<h4 className="text-base font-semibold leading-snug line-clamp-3">
{post.title.rendered}
</h4>
<div className="flex flex-wrap w-full items-center gap-x-4 gap-y-1 text-sm text-muted-foreground bg-muted inline-flex p-1.5 mt-3 rounded-sm">
{authorName && (
<span className="inline-flex flex-1 items-center gap-1.5 min-w-0">
<User className="size-4 shrink-0" aria-hidden />
<span className="truncate">{authorName}</span>
</span>
)}
{modifiedDate && (
<time dateTime={post.modified} className="shrink-0">
{modifiedDate}
</time>
)}
</div>
</div>
</CardContent>
</Card>
</Link>
);
}
if (variant === "tiny") {
return (
<Link href={`/blog/${encodeURIComponent(post.slug)}`} className="block h-full">
@@ -35,7 +84,8 @@ export function BlogPostCard({
alt=""
fill
className="object-cover"
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
quality={100}
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 400px"
/>
</div>
) : (
@@ -82,7 +132,7 @@ export function BlogPostCard({
className="object-cover"
quality={100}
priority
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 350px"
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 400px"
/>
</div>
) : (
-2
View File
@@ -140,7 +140,6 @@ export function BlogPostMeta({
<Button
variant="ghost"
size="icon-sm"
className="size-8 text-muted-foreground hover:text-foreground"
onClick={copyLink}
aria-label="کپی لینک"
>
@@ -160,7 +159,6 @@ export function BlogPostMeta({
<Button
variant="ghost"
size="icon-sm"
className="size-8 text-muted-foreground hover:text-foreground"
onClick={handleShare}
aria-label="اشتراک‌گذاری"
>
+1 -1
View File
@@ -20,7 +20,7 @@ export function BlogPullquote({ payload }: BlogPullquoteProps) {
{citation ? (
<footer className="mt-4">
<cite className="text-sm text-muted-foreground not-italic">
— {citation}
{citation}
</cite>
</footer>
) : null}
+4 -4
View File
@@ -24,21 +24,21 @@ export default function ContentSection() {
</div>
<div className="relative space-y-4">
<p className="text-muted-foreground leading-relaxed tracking-normal">
<p className="text-muted-foreground leading-relaxed tracking-normal text-justify sm:text-start">
فیپ یک گروه متخصص است که در حوزه اینتگریشن و روش‌های نوین یکپارچه‌سازی فعالیت می‌کند.{" "}
<span className="text-accent-foreground font-bold">
<span className="text-primary font-bold">
ما با استفاده از تکنولوژی‌های پیشرفته و رویکردهای نوآورانه
</span>{" "}
به کسب‌وکارها کمک می‌کنیم تا سیستم‌های خود را به شکلی کارآمد و قابل‌اعتماد بهم متصل کنند.
</p>
<p className="text-muted-foreground leading-relaxed tracking-normal">
<p className="text-muted-foreground leading-relaxed tracking-normal text-justify sm:text-start">
ما در زمینه یکپارچه‌سازی سیستم‌ها، اتوماسیون فرآیندها، ارتباطات چندکاناله و مدیریت داده‌ها تخصص داریم. با استفاده از روش‌های نوین و ابزارهای پیشرفته، راهکارهای سفارشی برای هر کسب‌وکار طراحی و پیاده‌سازی می‌کنیم که به آن‌ها امکان می‌دهد تا از قدرت اتوماسیون و ارتباطات هوشمند بهره‌مند شوند.
</p>
<div className="pt-6">
<blockquote className="border-s-4 border-primary/30 ps-4">
<p className="leading-relaxed tracking-normal text-foreground">
ماموریت ما این است که با استفاده از روش‌های نوین اینتگریشن، به کسب‌وکارها کمک کنیم تا
ماموریت ما این است که با استفاده از روش‌های نوین اینتگریشن، به کسب‌وکارها کمک کنیم تا
بدون پیچیدگی‌های فنی، سیستم‌های خود را بهم متصل کنند و به اهداف خود برسند.
</p>
+56 -34
View File
@@ -20,22 +20,29 @@ export default function Features() {
type ImageKey = "item-1" | "item-2" | "item-3" | "item-4";
const [activeItem, setActiveItem] = useState<ImageKey>("item-1");
const sectionImage = (filename: string) =>
`/assets/main-pic/4section/${encodeURIComponent(filename)}`;
const images = {
"item-1": {
image: "/assets/main-pic/4section/Systems Integration.jpg",
alt: "System integration",
image: sectionImage("Systems Integration.jpg"),
imageDark: sectionImage("Systems Integration-dark.jpg"),
alt: "یکپارچه‌سازی سیستم‌ها",
},
"item-2": {
image: "/assets/main-pic/4section/Powerful Management panels.jpg",
alt: "Management panels",
image: sectionImage("Powerful Management panels.jpg"),
imageDark: sectionImage("Powerful Management panels-dark.jpg"),
alt: "پنل‌های مدیریتی قدرتمند",
},
"item-3": {
image: "/assets/main-pic/4section/Interaction with Social Networks.jpg",
alt: "Social media integration",
image: sectionImage("Interaction with Social Networks.jpg"),
imageDark: sectionImage("Interaction with Social Networks_dark.jpg"),
alt: "تعامل با شبکه‌های اجتماعی",
},
"item-4": {
image: "/assets/main-pic/4section/Process Automation.jpg",
alt: "Process automation",
image: sectionImage("Process Automation.jpg"),
imageDark: sectionImage("Process Automation-dark.jpg"),
alt: "اتوماسیون فرآیندها",
},
};
@@ -113,33 +120,48 @@ export default function Features() {
</AccordionItem>
</Accordion>
<div className="bg-background relative flex overflow-hidden rounded-3xl border p-0 m-0">
<div className="w-15 absolute inset-0 right-0 ml-auto border-l bg-[repeating-linear-gradient(-45deg,var(--color-border),var(--color-border)_1px,transparent_1px,transparent_8px)]"></div>
<div className="aspect-76/59 bg-background relative w-full rounded-3xl m-0 p-0">
<AnimatePresence mode="wait">
<motion.div
key={`${activeItem}-id`}
initial={{ opacity: 0, y: 6, scale: 0.98 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 6, scale: 0.98 }}
transition={{ duration: 0.2 }}
className="size-full overflow-hidden rounded-3xl m-0 p-0"
>
<Image
src={images[activeItem].image}
className="size-full object-cover dark:mix-blend-lighten"
alt={images[activeItem].alt}
width={1207}
height={929}
/>
</motion.div>
</AnimatePresence>
<div className="relative rounded-3xl bg-linear-to-br from-primary/50 via-accent/40 to-secondary/50 p-px shadow-lg shadow-primary/10">
<div className="bg-background relative flex overflow-hidden rounded-[calc(1.5rem-1px)] m-0 p-0">
<div className="w-15 absolute inset-0 right-0 ml-auto border-l border-primary/15 bg-[repeating-linear-gradient(-45deg,color-mix(in_oklab,var(--color-primary)_25%,var(--color-border)),color-mix(in_oklab,var(--color-primary)_25%,var(--color-border))_1px,transparent_1px,transparent_8px)]"></div>
<div className="aspect-76/59 bg-background relative w-full rounded-[calc(1.5rem-1px)] m-0 p-0">
<AnimatePresence mode="wait">
<motion.div
key={`${activeItem}-id`}
initial={{ opacity: 0, y: 6, scale: 0.98 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 6, scale: 0.98 }}
transition={{ duration: 0.2 }}
className="size-full overflow-hidden rounded-[calc(1.5rem-1px)] m-0 p-0"
>
<Image
src={images[activeItem].image}
className="size-full object-cover dark:hidden"
alt={images[activeItem].alt}
width={1207}
height={929}
quality={100}
sizes="(max-width: 768px) 100vw, 50vw"
/>
<Image
src={images[activeItem].imageDark}
className="hidden size-full object-cover dark:block"
alt={images[activeItem].alt}
width={1207}
height={929}
quality={100}
sizes="(max-width: 768px) 100vw, 50vw"
/>
</motion.div>
</AnimatePresence>
</div>
<BorderBeam
duration={6}
size={200}
borderWidth={1}
colorFrom="#FCC61D"
colorTo="#01796F"
/>
</div>
<BorderBeam
duration={6}
size={200}
className="from-transparent via-yellow-700 to-transparent dark:via-white/50"
/>
</div>
</div>
</div>
+68 -63
View File
@@ -72,9 +72,11 @@ export default function HeroSection() {
<Image
src="/assets/main-pic/main-pic.jpg"
alt="background"
className="hidden size-full dark:block"
width="1150"
height="613"
className="hidden size-full object-cover dark:block"
width={1150}
height={613}
quality={100}
sizes="100vw"
/>
</AnimatedGroup>
@@ -121,9 +123,7 @@ export default function HeroSection() {
as="p"
className="mx-auto mt-8 max-w-2xl text-balance text-base md:text-lg leading-relaxed tracking-normal"
>
داده‌ها، کاربران و فرآیندهای شما را از هر پلتفرمی (وب، CRM، تلگرام، سرویس‌های سفارشی) به‌صورت سریع، دقیق و امن یکپارچه کنید.
فرآیندهای کسب‌وکاری شما با سرعت به هم متصل می‌شوند.
با ابزارهای یکپارچه‌سازی، اتوماسیون و ارتباطات ما، داده‌ها، کاربران و فرآیندهای کسب‌وکارتان را از هر سیستم یا پلتفرمی، از وب‌سایت و CRM تا تلگرام و سرویس‌های سفارشی، دقیق، سریع و قابل اعتماد به هم متصل کنید.
</TextEffect>
<AnimatedGroup
@@ -144,11 +144,7 @@ export default function HeroSection() {
key={1}
className="bg-foreground/10 rounded-[calc(var(--radius-xl)+0.125rem)] border p-0.5"
>
<Button
asChild
size="lg"
className="rounded-xl px-5 text-base"
>
<Button asChild size="lg">
<Link href="#link">
<span className="text-nowrap">مشاهده راهکارها</span>
<ChevronDown />
@@ -172,78 +168,87 @@ export default function HeroSection() {
...transitionVariants,
}}
>
<div className="relative mt-8 overflow-hidden px-2 sm:mt-12 md:mt-20 p-2">
<div className="ring-background bg-background relative max-w-5xl mx-auto overflow-hidden rounded-2xl border ring-1 aspect-[3/2]">
<Image
src="/assets/main-pic/Untitled-1 copy-min.jpg"
alt="یکپارچه‌سازی سیستم‌ها و شبکه دیجیتال"
fill
className="object-cover rounded-2xl inline-block my-0"
quality={100}
priority
/>
<div className="relative mt-8 overflow-visible px-2 sm:mt-12 md:mt-20 p-2">
<div className="relative max-w-4xl mx-auto flex items-center justify-center">
<div className="relative rounded-2xl overflow-hidden">
{/* Glow effect */}
<div className="absolute inset-0 rounded-2xl bg-gradient-to-r from-blue-500/20 via-purple-500/20 to-pink-500/20 blur-3xl -z-10 scale-110 animate-pulse" />
<div className="absolute inset-0 rounded-2xl bg-gradient-to-r from-blue-400/30 via-purple-400/30 to-pink-400/30 blur-2xl -z-10 scale-105" />
<div className="relative bg-background rounded-2xl border ring-1 ring-border/50 shadow-2xl shadow-blue-500/10 dark:shadow-blue-500/20">
<Image
src="/assets/main-pic/1.jpg"
alt="یکپارچه‌سازی سیستم‌ها و شبکه دیجیتال"
width={1920}
height={1280}
className="object-contain rounded-2xl w-full h-auto max-h-[600px] md:max-h-[700px] dark:hidden"
quality={100}
priority
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 90vw, 1200px"
/>
<Image
src="/assets/main-pic/1dark.jpg"
alt="یکپارچه‌سازی سیستم‌ها و شبکه دیجیتال"
width={1920}
height={1280}
className="hidden object-contain rounded-2xl w-full h-auto max-h-[600px] md:max-h-[700px] dark:block"
quality={100}
priority
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 90vw, 1200px"
/>
</div>
</div>
</div>
</div>
</AnimatedGroup>
</div>
</section>
<section className="bg-background pb-16 pt-16 md:pb-32">
<div className="group relative m-auto max-w-5xl px-6">
<div className="absolute inset-0 z-10 flex scale-95 items-center justify-center opacity-0 duration-500 group-hover:scale-100 group-hover:opacity-100">
<Link
href="/"
className="block text-sm duration-150 hover:text-accent transition-colors"
>
<span> اتصال به سایر سیستم ها </span>
<ChevronRight className="ml-1 inline-block size-3" />
</Link>
<div className="m-auto max-w-5xl px-6">
<div className="text-center">
<p className="text-sm font-medium text-muted-foreground">
یکپارچه با ابزارهای محبوب
</p>
<h2 className="mt-2 text-lg font-semibold md:text-xl">
اتصال به سایر سیستم‌ها
</h2>
</div>
<div className="group-hover:blur-xs mx-auto mt-12 grid max-w-4xl grid-cols-4 gap-x-12 gap-y-8 transition-all duration-500 group-hover:opacity-50 sm:gap-x-16 sm:gap-y-14">
<LogoItem
icon="logos:telegram"
alt="Telegram Logo"
size="md"
className="translate-x-1"
/>
<div className="mx-auto mt-10 flex max-w-3xl flex-wrap items-center justify-center gap-4 sm:gap-5">
<LogoItem icon="logos:telegram" alt="Telegram Logo" size="md" />
<LogoItem
icon="simple-icons:n8n"
alt="n8n Logo"
size="lg"
className="translate-x-4 "
iconClassName="text-black"
/>
<LogoItem
icon="skill-icons:wordpress"
alt="WordPress Logo"
size="md"
className="-translate-x-2"
/>
<LogoItem icon="logos:google-icon" alt="Google Logo" size="md" />
<LogoItem
icon="logos:whatsapp-icon"
alt="WhatsApp Logo"
size="md"
/>
<LogoItem
icon="logos:google-icon"
alt="Google Logo"
icon="logos:microsoft-windows-icon"
alt="Microsoft Windows Logo"
size="md"
className="translate-x-5"
/>
<div className="col-span-4 flex justify-center gap-x-12 sm:gap-x-40">
<LogoItem
icon="logos:whatsapp-icon"
alt="WhatsApp Logo"
size="md"
className="translate-x-2"
/>
<LogoItem
icon="logos:microsoft-windows-icon"
alt="Microsoft Windows Logo"
size="md"
className="translate-x-2"
/>
<LogoItem
icon="skill-icons:github-dark"
alt="github Logo"
size="md"
className="translate-x-4"
/>
</div>
<LogoItem icon="logos:github-icon" alt="GitHub Logo" size="md" />
</div>
<div className="mt-8 text-center">
<Link
href="/"
className="inline-flex items-center text-sm text-muted-foreground transition-colors hover:text-accent"
>
<span>مشاهده همه یکپارچه‌سازی‌ها</span>
<ChevronRight className="ml-1 inline-block size-3" />
</Link>
</div>
</div>
</section>
+2 -9
View File
@@ -61,10 +61,7 @@ export function LoginForm({
/>
</Field>
<Field>
<Button
type="submit"
className="w-full h-10 sm:h-11 text-sm sm:text-base"
>
<Button type="submit" size="lg" className="w-full">
ورود
</Button>
</Field>
@@ -72,11 +69,7 @@ export function LoginForm({
یا ادامه با
</FieldSeparator>
<Field className="">
<Button
variant="outline"
type="button"
className="w-full h-10 sm:h-11 text-sm sm:text-base"
>
<Button variant="outline" type="button" size="lg" className="w-full">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
+3 -3
View File
@@ -18,7 +18,7 @@ export default function Pricing() {
</p>
<p className="text-lg leading-relaxed tracking-normal text-foreground font-medium">
راه‌حل‌های ما از <span className="text-primary font-bold">۲ میلیون تومان</span> شروع می‌شوند و بر اساس نیازهای دقیق شما محاسبه می‌گردند.
</p>
</p>
</div>
</div>
</div>
@@ -174,10 +174,10 @@ export default function Pricing() {
ما آماده‌ایم تا بر اساس نیازهای شما بهترین پیشنهاد را ارائه دهیم.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Button asChild size="lg" className="text-base px-8 py-6">
<Button asChild size="lg">
<Link href="#chat">شروع گفتگو با تیم</Link>
</Button>
<Button asChild size="lg" variant="outline" className="text-base px-8 py-6">
<Button asChild size="lg" variant="outline">
<Link href="/about">درباره ما</Link>
</Button>
</div>
+2 -2
View File
@@ -41,11 +41,11 @@ export function ScrollToTop({ className }: { className?: string }) {
)}
>
<Button
size="icon"
size="icon-lg"
variant="outline"
onClick={scrollToTop}
aria-label="برگشت به بالا"
className="size-10 rounded-md shadow-md"
className="shadow-md"
>
<ChevronUp className="size-5" />
</Button>
+2 -4
View File
@@ -18,8 +18,7 @@ export function ThemeToggle() {
return (
<Button
variant="ghost"
size="sm"
className="size-9 rounded-lg"
size="icon"
aria-label="Toggle theme"
>
<Sun className="size-4" />
@@ -30,9 +29,8 @@ export function ThemeToggle() {
return (
<Button
variant="ghost"
size="sm"
size="icon"
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
className="size-9 rounded-lg transition-all duration-300 hover:bg-muted"
aria-label="Toggle theme"
>
<Sun className="size-4 rotate-0 scale-100 transition-all duration-300 dark:-rotate-90 dark:scale-0" />
+1 -1
View File
@@ -57,7 +57,7 @@ export const BorderBeam = ({
delay = 0,
duration = 6,
colorFrom = "#FCC61D",
colorTo = "#0046FF",
colorTo = "#01796F",
transition,
style,
reverse = false,
+12 -10
View File
@@ -5,25 +5,27 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg font-medium transition-colors disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-accent hover:text-foreground dark:hover:bg-[#FCC61D] dark:hover:text-foreground transition-colors",
default:
"bg-primary text-primary-foreground hover:bg-accent hover:text-foreground dark:hover:bg-[#FCC61D] dark:hover:text-foreground",
destructive:
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"border bg-background shadow-xs hover:bg-accent hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-[#FCC61D] dark:hover:text-foreground transition-colors",
"border border-primary text-primary bg-transparent hover:bg-accent hover:text-foreground hover:border-accent dark:bg-input/30 dark:border-input dark:text-foreground dark:hover:bg-[#FCC61D] dark:hover:text-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-accent hover:text-foreground dark:hover:bg-[#FCC61D] dark:hover:text-foreground transition-colors",
"bg-secondary text-secondary-foreground hover:bg-accent hover:text-foreground dark:hover:bg-[#FCC61D] dark:hover:text-foreground",
ghost:
"hover:bg-accent hover:text-foreground dark:hover:bg-[#FCC61D] dark:hover:text-foreground transition-colors",
link: "text-primary underline-offset-4 hover:text-accent hover:underline dark:hover:text-[#FCC61D] transition-colors",
"hover:bg-accent hover:text-foreground dark:hover:bg-[#FCC61D] dark:hover:text-foreground",
link:
"text-primary underline-offset-4 hover:text-accent hover:underline dark:hover:text-[#FCC61D]",
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
default: "h-9 px-4 text-sm has-[>svg]:px-3",
sm: "h-8 gap-1.5 px-3 text-sm has-[>svg]:px-2.5",
lg: "h-11 px-8 text-base has-[>svg]:px-6",
icon: "size-9",
"icon-sm": "size-8",
"icon-lg": "size-10",
@@ -51,7 +53,7 @@ function Button({
return (
<Comp
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
className={cn(buttonVariants({ variant, size }), className)}
{...props}
/>
)
+31 -13
View File
@@ -6,26 +6,44 @@ interface LogoItemProps {
alt: string;
size?: "sm" | "md" | "lg";
className?: string;
iconClassName?: string;
}
const sizeMap = {
sm: "h-8",
md: "h-10",
lg: "h-12",
const iconSizeMap = {
sm: "size-6",
md: "size-7",
lg: "size-8",
};
export function LogoItem({ icon, alt, size = "md", className }: LogoItemProps) {
const tileSizeMap = {
sm: "size-12",
md: "size-14",
lg: "size-16",
};
export function LogoItem({
icon,
alt,
size = "md",
className,
iconClassName,
}: LogoItemProps) {
return (
<div className={cn("flex", className)}>
<Icon
icon={icon}
<div className={cn("flex items-center justify-center", className)}>
<div
className={cn(
"mx-auto w-fit dark:invert",
sizeMap[size]
"flex items-center justify-center rounded-2xl border border-border/50 bg-white shadow-sm transition-all duration-300",
"hover:-translate-y-1 hover:shadow-md",
"dark:border-white/10 dark:bg-white/95 dark:shadow-lg dark:shadow-black/25",
tileSizeMap[size]
)}
aria-label={alt}
/>
>
<Icon
icon={icon}
className={cn("shrink-0", iconSizeMap[size], iconClassName)}
aria-label={alt}
/>
</div>
</div>
);
}