feat: redesign landing page with brand system and section blocks
Build & Push Docker Image / build (push) Has been cancelled
Build & Push Docker Image / build (push) Has been cancelled
Refresh the homepage around new hero/services/roadmap sections, shared design tokens, and updated brand assets. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+102
-16
@@ -2,37 +2,119 @@ import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { type as typeScale } from "@/lib/typography"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
/**
|
||||
* Unified button system
|
||||
*
|
||||
* Variants (marketing hierarchy):
|
||||
* - default → Primary (main CTA / conversion)
|
||||
* - secondary → Secondary (bordered supporting action)
|
||||
* - ghost → Ghost (minimal / nav)
|
||||
*
|
||||
* Aliases kept for shadcn / existing blocks:
|
||||
* - outline → same as secondary
|
||||
*
|
||||
* Utility variants:
|
||||
* - destructive, link
|
||||
*
|
||||
* Surfaces:
|
||||
* - default → normal page canvas
|
||||
* - brand → teal / dark marketing panels (inverse contrast)
|
||||
*
|
||||
* Sizes: sm | default (medium) | lg | icon-sm | icon | icon-lg
|
||||
*
|
||||
* Persian text: padding and height are sized for IRANSansX so glyphs
|
||||
* are not clipped; labels use `type.button` / `type["button-lg"]`.
|
||||
*/
|
||||
const buttonVariants = cva(
|
||||
"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",
|
||||
[
|
||||
"inline-flex items-center justify-center whitespace-nowrap",
|
||||
"rounded-[var(--btn-radius)]",
|
||||
"transition-[color,background-color,border-color,box-shadow,opacity,transform]",
|
||||
"duration-200 ease-out",
|
||||
"outline-none select-none shrink-0",
|
||||
"disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none",
|
||||
"[&_svg]:pointer-events-none [&_svg]:shrink-0",
|
||||
"[&_svg:not([class*='size-'])]:size-[var(--btn-icon-size)]",
|
||||
"focus-visible:ring-[3px] focus-visible:ring-ring/40 focus-visible:border-ring",
|
||||
"active:scale-[0.98]",
|
||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
].join(" "),
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
// Primary — brand neon rim via shadow-glow-*
|
||||
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 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",
|
||||
"bg-primary text-primary-foreground shadow-glow-sm hover:bg-primary-hover hover:shadow-glow-primary active:bg-primary-active",
|
||||
// Secondary (bordered) — preferred name
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-accent hover:text-foreground dark:hover:bg-[#FCC61D] dark:hover:text-foreground",
|
||||
"border border-surface bg-transparent text-foreground shadow-surface-sm hover:border-primary/45 hover:bg-accent hover:text-accent-foreground hover:shadow-glow-sm active:bg-accent/80 dark:border-white/18 dark:hover:border-primary/50 dark:hover:bg-accent",
|
||||
// Alias of secondary (shadcn / imported blocks)
|
||||
outline:
|
||||
"border border-surface bg-transparent text-foreground shadow-surface-sm hover:border-primary/45 hover:bg-accent hover:text-accent-foreground hover:shadow-glow-sm active:bg-accent/80 dark:border-white/18 dark:hover:border-primary/50 dark:hover:bg-accent",
|
||||
// Ghost
|
||||
ghost:
|
||||
"hover:bg-accent hover:text-foreground dark:hover:bg-[#FCC61D] dark:hover:text-foreground",
|
||||
"bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground active:bg-accent/80 dark:hover:bg-white/8",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90 hover:shadow-md focus-visible:ring-destructive/25 dark:focus-visible:ring-destructive/40 active:bg-destructive/85",
|
||||
link:
|
||||
"text-accent underline-offset-4 hover:underline",
|
||||
"bg-transparent text-primary underline-offset-4 hover:underline hover:text-primary-hover active:text-primary-active",
|
||||
},
|
||||
surface: {
|
||||
default: "",
|
||||
/** Inverse contrast for brand / dark marketing panels */
|
||||
brand: "",
|
||||
},
|
||||
size: {
|
||||
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",
|
||||
sm: cn(
|
||||
"h-[var(--btn-height-sm)] gap-[var(--btn-gap-sm)] px-[var(--btn-px-sm)] [--btn-icon-size:0.875rem] has-[>svg]:px-[calc(var(--btn-px-sm)-0.125rem)]",
|
||||
typeScale.button
|
||||
),
|
||||
default: cn(
|
||||
"h-[var(--btn-height-md)] gap-[var(--btn-gap-md)] px-[var(--btn-px-md)] [--btn-icon-size:1rem] has-[>svg]:px-[calc(var(--btn-px-md)-0.25rem)]",
|
||||
typeScale.button
|
||||
),
|
||||
lg: cn(
|
||||
"h-[var(--btn-height-lg)] gap-[var(--btn-gap-lg)] px-[var(--btn-px-lg)] [--btn-icon-size:1.25rem] has-[>svg]:px-[calc(var(--btn-px-lg)-0.25rem)]",
|
||||
typeScale["button-lg"]
|
||||
),
|
||||
"icon-sm":
|
||||
"size-[var(--btn-height-sm)] gap-0 p-0 [--btn-icon-size:0.875rem]",
|
||||
icon: "size-[var(--btn-height-md)] gap-0 p-0 [--btn-icon-size:1rem]",
|
||||
"icon-lg":
|
||||
"size-[var(--btn-height-lg)] gap-0 p-0 [--btn-icon-size:1.25rem]",
|
||||
},
|
||||
},
|
||||
compoundVariants: [
|
||||
{
|
||||
variant: "default",
|
||||
surface: "brand",
|
||||
className:
|
||||
"bg-white text-brand-dark shadow-glow-sm hover:bg-brand-light hover:text-brand-dark hover:shadow-glow-primary active:bg-brand-light/90",
|
||||
},
|
||||
{
|
||||
variant: ["secondary", "outline"],
|
||||
surface: "brand",
|
||||
className:
|
||||
"border-white/40 bg-transparent text-white shadow-none hover:border-white/60 hover:bg-white/10 hover:text-white hover:shadow-[0_0_0_1px_rgb(255_255_255_/_0.35),0_0_18px_-4px_rgb(255_255_255_/_0.35)] active:bg-white/15",
|
||||
},
|
||||
{
|
||||
variant: "ghost",
|
||||
surface: "brand",
|
||||
className:
|
||||
"text-white hover:bg-white/10 hover:text-white active:bg-white/15",
|
||||
},
|
||||
{
|
||||
variant: "link",
|
||||
surface: "brand",
|
||||
className: "text-white hover:text-white/90",
|
||||
},
|
||||
],
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
surface: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
@@ -42,6 +124,7 @@ function Button({
|
||||
className,
|
||||
variant,
|
||||
size,
|
||||
surface,
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<"button"> &
|
||||
@@ -53,7 +136,10 @@ function Button({
|
||||
return (
|
||||
<Comp
|
||||
data-slot="button"
|
||||
className={cn(buttonVariants({ variant, size }), className)}
|
||||
data-variant={variant ?? "default"}
|
||||
data-surface={surface ?? "default"}
|
||||
data-size={size ?? "default"}
|
||||
className={cn(buttonVariants({ variant, size, surface }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user