بلاک‌ها · بخش‌های صفحه

فراخوان روی منظره

پیش‌نمایش تمام‌صفحه

بخشِ فراخوانِ پایانی و فوتر روی منظرهٔ دره و تپه‌های لایه‌لایه (SVG با توکن‌ها): جملهٔ برند، فرمِ ایمیلِ شیشه‌ای با خطا و موفقیت، و ستون‌های لینک.

۱. نصب کامپوننت‌ها

این بلاک از ۴ آیتم دیگ استفاده می‌کند: buttoncontrol-sizeiconsinput

پکیج‌منیجر پیش‌فرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیج‌منیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/input.json
pnpm dlx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/input.json
yarn dlx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/input.json
bunx --bun digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/input.json

۲. کد صفحه

کد را در app/<route>/page.tsx بگذارید و متن‌ها، داده و هندلرها را با محصول خودتان عوض کنید.

cta-landscape.tsx
"use client"

import * as React from "react"

import { ArrowUpIcon, CheckCircleIcon, SparklesIcon } from "@/components/icons"
import { Button } from "@/components/ui/button"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { Input } from "@/components/ui/input"

const COLUMNS = [
  { title: "محصول", links: ["چطور کار می‌کند", "نمونه‌ها", "امنیت", "تعرفه"] },
  { title: "توسعه‌دهندگان", links: ["مستندات", "مرجعِ API", "وضعیتِ سرویس"] },
  { title: "شرکت", links: ["درباره ما", "تماس"] },
]

/**
 * فراخوانِ پایانی روی منظره: دره و تپه‌های لایه‌لایه با مه، جملهٔ برند وسطِ آسمان و فرمِ
 * ایمیلِ شیشه‌ای؛ فوتر روی تپه‌های تیرهٔ پایین می‌نشیند. منظره با SVG و توکن‌های رنگ کشیده
 * شده، پس با تم و حالتِ تیره هماهنگ می‌ماند و عکس لازم ندارد.
 */
export default function CtaLandscapeBlock() {
  const [email, setEmail] = React.useState("")
  const [state, setState] = React.useState<"idle" | "error" | "done">("idle")

  const submit = (event: React.FormEvent) => {
    event.preventDefault()
    setState(/^\S+@\S+\.\S+$/.test(email) ? "done" : "error")
  }

  return (
    <ControlSizeProvider size="xl" asChild>
      <footer className="relative isolate flex w-full flex-col overflow-hidden bg-[linear-gradient(180deg,var(--dig-slate-500)_0%,var(--dig-slate-300)_38%,var(--dig-green-200)_60%)] text-[var(--dig-white)] dark:bg-[linear-gradient(180deg,var(--dig-slate-950)_0%,var(--dig-slate-800)_40%,var(--dig-green-900)_62%)]">
        <svg aria-hidden viewBox="0 0 1440 900" preserveAspectRatio="xMidYMax slice" className="absolute inset-0 -z-10 size-full">
          <defs>
            <linearGradient id="cl-mist" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0" stopColor="var(--dig-white)" stopOpacity="0" />
              <stop offset="1" stopColor="var(--dig-white)" stopOpacity="0.35" />
            </linearGradient>
          </defs>
          <circle cx="1080" cy="250" r="110" className="fill-[var(--dig-amber-100)] dark:fill-[var(--dig-slate-600)]" />
          <path d="M0 470 C 180 380 320 420 470 360 S 760 300 900 380 S 1200 330 1440 400 V900 H0Z" fill="var(--dig-slate-400)" className="dark:fill-[var(--dig-slate-700)]" />
          <path d="M0 470 H1440 V560 H0Z" fill="url(#cl-mist)" />
          <path d="M0 540 C 220 470 380 520 560 470 S 900 430 1060 500 S 1300 470 1440 520 V900 H0Z" fill="var(--dig-green-700)" className="dark:fill-[var(--dig-green-900)]" />
          <path d="M0 620 C 260 560 420 600 640 560 S 1000 540 1180 600 S 1360 590 1440 610 V900 H0Z" fill="var(--dig-green-800)" className="dark:fill-[var(--dig-green-950)]" />
          <path d="M0 700 C 300 650 520 690 760 660 S 1200 650 1440 690 V900 H0Z" fill="var(--dig-green-950)" className="dark:fill-[var(--dig-graphite-950)]" />
          {Array.from({ length: 48 }, (_, index) => {
            const x = (index * 97) % 1440
            const h = 18 + ((index * 37) % 30)
            return <path key={index} d={`M${x} 700 q ${(index % 2 ? 6 : -6)} ${-h / 2} ${(index % 3) - 1} ${-h}`} strokeWidth="2" className="stroke-[var(--dig-green-800)] dark:stroke-[var(--dig-green-900)]" fill="none" strokeLinecap="round" />
          })}
        </svg>

        <section aria-labelledby="cl-title" className="mx-auto flex w-full max-w-3xl flex-col items-center gap-8 px-4 pt-28 pb-40 text-center sm:px-6 sm:pt-36 sm:pb-56">
          <h2 id="cl-title" className="text-3xl leading-snug font-black text-balance [text-shadow:0_2px_24px_var(--dig-slate-900)] sm:text-4xl">
            سامانه‌ای که با تیمِ شما بزرگ می‌شود، نه اینکه جایش را بگیرند
          </h2>
          {state === "done" ? (
            <p role="status" className="flex items-center gap-2 rounded-full bg-[color-mix(in_oklab,var(--dig-graphite-950)_55%,transparent)] px-5 py-3 font-semibold backdrop-blur-md">
              <CheckCircleIcon aria-hidden className="size-5 text-[var(--dig-green-300)]" />
              در فهرست هستید؛ به‌زودی خبرتان می‌کنیم.
            </p>
          ) : (
            <form noValidate onSubmit={submit} className="flex w-full max-w-md flex-col gap-2">
              <div className="flex items-center gap-2 rounded-full border border-[color-mix(in_oklab,var(--dig-white)_25%,transparent)] bg-[color-mix(in_oklab,var(--dig-graphite-950)_45%,transparent)] p-1.5 backdrop-blur-md">
                <Input
                  type="email"
                  dir="ltr"
                  aria-label="ایمیلِ کاری"
                  placeholder="name@company.ir"
                  value={email}
                  onValueChange={setEmail}
                  aria-invalid={state === "error"}
                  radius="full"
                  className="flex-1 border-0 bg-transparent text-[var(--dig-white)] shadow-none placeholder:text-[var(--dig-neutral-300)]"
                />
                <Button type="submit" radius="full" className="shrink-0 [--primary-foreground:var(--dig-graphite-950)] [--primary:var(--dig-white)]">
                  <SparklesIcon />
                  دسترسیِ زودهنگام
                </Button>
              </div>
              {state === "error" && (
                <p role="alert" className="text-sm font-semibold text-[var(--dig-red-200)]">
                  ایمیل را کامل بنویسید، مثلاً name@company.ir
                </p>
              )}
            </form>
          )}
        </section>

        <div className="mx-auto grid w-full max-w-6xl grid-cols-2 gap-8 px-4 pb-10 sm:px-6 md:grid-cols-[1fr_auto_auto_auto] md:gap-16">
          <div className="col-span-2 flex items-start md:col-span-1">
            <span aria-hidden className="flex size-12 items-center justify-center rounded-xl bg-[var(--dig-white)] text-[var(--dig-green-900)] [&>svg]:size-6">
              <SparklesIcon />
            </span>
          </div>
          {COLUMNS.map((column) => (
            <nav key={column.title} aria-label={column.title} className="flex flex-col gap-3 text-sm">
              <span className="font-bold">{column.title}</span>
              {column.links.map((link) => (
                <a key={link} href="#" className="text-[var(--dig-neutral-300)] hover:text-[var(--dig-white)]">
                  {link}
                </a>
              ))}
            </nav>
          ))}
        </div>
        <div className="mx-auto flex w-full max-w-6xl items-center justify-between gap-4 border-t border-[color-mix(in_oklab,var(--dig-white)_15%,transparent)] px-4 py-6 text-sm text-[var(--dig-neutral-400)] sm:px-6">
          <span>© ۱۴۰۵ آزمایشگاهِ دره. همهٔ حقوق محفوظ است.</span>
          <a href="#top" className="flex items-center gap-1 hover:text-[var(--dig-white)]">
            بالا
            <ArrowUpIcon aria-hidden className="size-4" />
          </a>
        </div>
      </footer>
    </ControlSizeProvider>
  )
}