# Content 16

> Content 16 variant for block.

A shadcn/ui block from the [@uiable](https://uiable.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/uiable/block-content-16).

- Page, with a live preview: https://shadcn.exchange/r/uiable/block-content-16
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/uiable/block-content-16-light-080b15e4204fab3a.png
- Registry item: https://uiable.com/r/block-content-16.json
- Categories: content
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://uiable.com/r/block-content-16.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `@tabler/icons-react`, `cn`, `framer-motion`, `lucide-react`
- Registry items: `@uiable/button`, `@uiable/input`, `@uiable/label`, `@uiable/separator`, `@uiable/textarea`, `@uiable/toggle-group`

## Files

### `src/components/uiable/blocks/content/content-16/content-16.tsx`

```tsx
"use client"

import {
  useState,
  useEffect,
  useRef,
  useCallback,
  ChangeEvent,
  FormEvent,
  ComponentType,
  CSSProperties,
  RefObject,
} from "react"

// next
import Link from "next/link"

// shadcn
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Separator } from "@/components/ui/separator"
import { Textarea } from "@/components/ui/textarea"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"

// third-party
import { cn } from "cn"
import { motion, AnimatePresence } from "framer-motion"

// assets
import {
  IconBrandGithub,
  IconBrandLinkedin,
  IconBrandTwitter,
  IconBrandDribbble,
} from "@tabler/icons-react"
import {
  Mail,
  MapPin,
  Clock,
  Phone,
  ArrowUpRight,
  Check,
  ArrowRight,
  LucideIcon,
} from "lucide-react"

interface FormState {
  name: string
  email: string
  subject: string
  message: string
}

interface SocialLink {
  name: string
  href: string
  icon: ComponentType<{ className?: string; style?: CSSProperties }>
  brandColor?: string
  isGithub?: boolean
}

const TYPEWRITER_PHRASES = [
  "something together.",
  "future products.",
  "scalable systems.",
  "next-gen apps.",
] as const

const SUBJECT_OPTIONS = [
  "Full App Development",
  "Component System Integration",
  "UI/UX Design Scoping",
  "General Inquiry",
] as const

const SOCIAL_LINKS: SocialLink[] = [
  {
    name: "LINKEDIN",
    href: "#",
    icon: IconBrandLinkedin,
    brandColor: "#0A66C2",
  },
  {
    name: "TWITTER / X",
    href: "#",
    icon: IconBrandTwitter,
    brandColor: "#1DA1F2",
  },
  {
    name: "GITHUB",
    href: "#",
    icon: IconBrandGithub,
    isGithub: true,
  },
  {
    name: "DRIBBBLE",
    href: "#",
    icon: IconBrandDribbble,
    brandColor: "#EA4C89",
  },
]

const INITIAL_FORM: FormState = {
  name: "",
  email: "",
  subject: "General Inquiry",
  message: "",
}

function TypewriterHeadline() {
  const [phraseIndex, setPhraseIndex] = useState(0)
  const [currentText, setCurrentText] = useState("")
  const [isDeleting, setIsDeleting] = useState(false)

  useEffect(() => {
    const phrase = TYPEWRITER_PHRASES[phraseIndex]

    if (!isDeleting && currentText === phrase) {
      const t = setTimeout(() => setIsDeleting(true), 2400)
      return () => clearTimeout(t)
    }

    if (isDeleting && currentText === "") {
      const t = setTimeout(() => {
        setIsDeleting(false)
        setPhraseIndex((p) => (p + 1) % TYPEWRITER_PHRASES.length)
      }, 0)
      return () => clearTimeout(t)
    }

    const t = setTimeout(
      () => {
        setCurrentText(
          isDeleting
            ? phrase.substring(0, currentText.length - 1)
            : phrase.substring(0, currentText.length + 1)
        )
      },
      isDeleting ? 35 : 75
    )

    return () => clearTimeout(t)
  }, [currentText, isDeleting, phraseIndex])

  return (
    <h1 className="flex flex-col items-center justify-center text-center text-6xl sm:text-8xl lg:items-start lg:text-left lg:text-6xl xl:text-7xl">
      <span>Let's build</span>
      <div className="flex min-h-[1.5em] items-center justify-center pt-1 text-center text-4xl sm:text-5xl lg:justify-start lg:text-left lg:text-4xl xl:text-5xl">
        <span className="bg-gradient-to-r from-primary via-primary/80 to-primary/50 bg-clip-text pb-2 font-extrabold whitespace-nowrap text-transparent">
          {currentText}
        </span>
        <span className="ml-1.5 inline-block h-[0.8em] w-[3px] shrink-0 animate-pulse rounded-full bg-primary" />
      </div>
    </h1>
  )
}

interface ContactInfo {
  id: string
  icon: LucideIcon
  title: string
  value: string
  href?: string
  valueClassName?: string
  containerGap?: string
}

const CONTACT_INFO_DATA: ContactInfo[] = [
  {
    id: "email",
    icon: Mail,
    title: "Inquiries & Proposals",
    value: "build@aistudio-components.dev",
    href: "mailto:build@aistudio-components.dev",
    valueClassName:
      "relative w-fit transition-colors after:absolute after:bottom-0 after:left-1/2 after:h-px after:w-0 after:-translate-x-1/2 after:bg-primary after:transition-all after:duration-300 group-hover:text-primary group-hover:after:w-full",
  },
  {
    id: "location",
    icon: MapPin,
    title: "Global HQ",
    value: "San Francisco, CA • Distributed Engineering",
  },
  {
    id: "phone",
    icon: Phone,
    title: "Direct Desk",
    value: "+1 (415) 555-0199",
    href: "tel:+14155550199",
    valueClassName: "transition-colors group-hover:text-primary",
  },
  {
    id: "time",
    icon: Clock,
    title: "Pacific Desk Time",
    value: "06:00 PM IST",
    containerGap: "gap-1",
    valueClassName: "text-sm",
  },
]

function ContactInfoItem({ item }: { item: ContactInfo }) {
  const Icon = item.icon

  const wrapperClasses = cn(
    "-ml-2 flex items-center gap-4 rounded-lg p-2",
    item.href &&
      "group transition-colors hover:bg-muted/50 focus-visible:bg-muted/50 focus-visible:outline-none"
  )

  const Wrapper: any = item.href ? Link : "div"
  const props = item.href ? { href: item.href } : {}

  return (
    <Wrapper className={wrapperClasses} {...props}>
      <div
        aria-hidden="true"
        className={cn(
          "flex h-8 w-8 shrink-0 items-center justify-center rounded-full border border-primary/30 bg-primary/10 text-primary",
          item.href && "transition-colors group-hover:bg-primary/20"
        )}
      >
        <Icon className="h-4 w-4 stroke-[1.5]" />
      </div>

      <div className={cn("flex flex-col gap-1 p-1", item.containerGap)}>
        <span className="text-xs font-bold tracking-wider text-muted-foreground uppercase">
          {item.title}
        </span>
        <span
          className={cn(
            "text-base font-semibold text-foreground",
            item.valueClassName
          )}
        >
          {item.value}
        </span>
      </div>
    </Wrapper>
  )
}

interface FormFieldProps {
  id: string
  field: string
  label: string
  type?: string
  isTextarea?: boolean
  value: string
  focusedField: string | null
  onFocus: (field: string) => void
  onBlur: () => void
  onChange: (e: any) => void
  textareaRef?: RefObject<HTMLTextAreaElement | null>
}

function FormField({
  id,
  field,
  label,
  type = "text",
  isTextarea = false,
  value,
  focusedField,
  onFocus,
  onBlur,
  onChange,
  textareaRef,
}: FormFieldProps) {
  const isFocused = focusedField === field
  const isActive = isFocused || value.length > 0
  const baseClasses =
    "w-full rounded-t-sm border-x-0 border-t-0 border-b-2 border-border bg-muted/50 px-4 py-3.5 text-base font-medium text-foreground shadow-none transition-all focus:bg-background focus:outline-none focus-visible:ring-0"

  return (
    <div className="group relative flex flex-col">
      <Label
        htmlFor={id}
        className={cn(
          "pointer-events-none font-semibold transition-all duration-200",
          isActive
            ? "mb-1 text-xs text-primary"
            : "mb-1.5 text-sm text-muted-foreground"
        )}
      >
        {label}
      </Label>
      <div className="relative w-full">
        {isTextarea ? (
          <Textarea
            id={id}
            ref={textareaRef}
            value={value}
            onFocus={() => onFocus(field)}
            onBlur={onBlur}
            onChange={onChange}
            rows={3}
            required
            className={cn(
              baseClasses,
              "min-h-[50px] resize-none overflow-hidden"
            )}
          />
        ) : (
          <Input
            id={id}
            type={type}
            value={value}
            onFocus={() => onFocus(field)}
            onBlur={onBlur}
            onChange={onChange}
            required
            className={cn(baseClasses, "h-auto")}
          />
        )}
        <span
          className={cn(
            "absolute bottom-0 left-0 h-[2px] bg-primary transition-all duration-300",
            isFocused ? "w-full" : "w-0"
          )}
        />
      </div>
    </div>
  )
}

interface SuccessMessageProps {
  name: string
  email: string
  onReset: () => void
}

function SuccessMessage({ name, email, onReset }: SuccessMessageProps) {
  return (
    <motion.div
      key="success-state"
      initial="hidden"
      animate="visible"
      exit={{ opacity: 0, scale: 0.98, transition: { duration: 0.4 } }}
      variants={{
        hidden: { opacity: 0 },
        visible: {
          opacity: 1,
          transition: { staggerChildren: 0.1, delayChildren: 0.1 },
        },
      }}
      className="relative my-auto flex w-full flex-col gap-8 overflow-hidden rounded-3xl border border-primary/10 bg-gradient-to-br from-background/90 to-muted/90 p-10 shadow-2xl shadow-primary/5 backdrop-blur-lg sm:p-14"
    >
      {/* Ambient background glows */}
      <div className="absolute -top-32 -left-32 h-64 w-64 rounded-full bg-primary/10 blur-3xl" />
      <div className="absolute -right-32 -bottom-32 h-64 w-64 rounded-full bg-primary/10 blur-3xl" />

      <motion.div
        variants={{
          hidden: { opacity: 0, scale: 0.5, rotate: -20 },
          visible: {
            opacity: 1,
            scale: 1,
            rotate: 0,
            transition: { type: "spring", bounce: 0.5 },
          },
        }}
        className="relative z-10 flex h-16 w-16 items-center justify-center rounded-2xl border border-primary/30 bg-primary/10 text-primary shadow-[0_0_30px_-5px_hsl(var(--primary)/0.3)]"
      >
        <Check className="h-8 w-8 stroke-[2.5]" />
        <div
          className="absolute inset-0 animate-ping rounded-2xl border border-primary/20"
          style={{ animationDuration: "3s" }}
        />
      </motion.div>

      <div className="relative z-10 flex flex-col gap-4">
        <motion.span
          variants={{
            hidden: { opacity: 0, x: -20 },
            visible: { opacity: 1, x: 0 },
          }}
          className="flex items-center gap-2 text-xs font-bold tracking-widest text-primary uppercase"
        >
          <span className="h-1.5 w-1.5 animate-pulse rounded-full bg-primary" />
          Message Transmitted
        </motion.span>

        <motion.h2
          variants={{
            hidden: { opacity: 0, y: 20 },
            visible: { opacity: 1, y: 0 },
          }}
          className="text-3xl font-extrabold tracking-tight text-foreground sm:text-5xl"
        >
          Thank you,{" "}
          <span className="bg-gradient-to-r from-primary to-primary/60 bg-clip-text text-transparent">
            {name.split(" ")[0]}
          </span>
          .
        </motion.h2>

        <motion.p
          variants={{
            hidden: { opacity: 0, y: 20 },
            visible: { opacity: 1, y: 0 },
          }}
          className="max-w-xl text-base leading-relaxed text-muted-foreground sm:text-lg"
        >
          Your note has been securely logged. An engineer from our core team
          will review your inquiry and respond to{" "}
          <strong className="font-semibold text-foreground underline decoration-primary/30 underline-offset-4">
            {email}
          </strong>{" "}
          within 4 business hours.
        </motion.p>
      </div>

      <motion.div
        variants={{
          hidden: { opacity: 0, y: 20 },
          visible: { opacity: 1, y: 0 },
        }}
        className="relative z-10 pt-4"
      >
        <Button
          type="button"
          onClick={onReset}
          className="group relative flex h-12 w-fit items-center justify-center gap-3 overflow-hidden rounded-full bg-primary/10 px-8 text-sm font-bold tracking-wider text-primary uppercase transition-all duration-300 hover:bg-primary hover:text-primary-foreground hover:shadow-[0_0_20px_-3px_hsl(var(--primary)/0.4)] active:scale-95"
        >
          <span>Send another message</span>
          <ArrowRight className="h-4 w-4 transition-transform duration-300 group-hover:translate-x-1" />
        </Button>
      </motion.div>
    </motion.div>
  )
}
//  ------------------------------ | CONTACT 16 | ------------------------------  //

export default function Contact16() {
  const [isSubmitting, setIsSubmitting] = useState(false)
  const [submitted, setSubmitted] = useState(false)
  const [form, setForm] = useState<FormState>(INITIAL_FORM)
  const [focusedField, setFocusedField] = useState<string | null>(null)

  const textareaRef = useRef<HTMLTextAreaElement>(null)

  const handleFocus = useCallback((key: string) => setFocusedField(key), [])

  const handleBlur = useCallback(() => {
    setFocusedField(null)
  }, [])

  const handleFieldChange = useCallback(
    (key: keyof FormState) => (value: string) => {
      setForm((prev) => ({ ...prev, [key]: value }))
    },
    []
  )

  const handleMessageChange = useCallback(
    (e: ChangeEvent<HTMLTextAreaElement>) => {
      setForm((prev) => ({ ...prev, message: e.target.value }))
      const el = textareaRef.current
      if (el) {
        requestAnimationFrame(() => {
          el.style.height = "auto"
          el.style.height = `${el.scrollHeight}px`
        })
      }
    },
    []
  )

  const handleSubmit = useCallback((e: FormEvent<HTMLFormElement>) => {
    e.preventDefault()

    setIsSubmitting(true)
    setTimeout(() => {
      setIsSubmitting(false)
      setSubmitted(true)
    }, 1400)
  }, [])

  const handleReset = useCallback(() => {
    setSubmitted(false)
    setForm(INITIAL_FORM)
  }, [])

  const handleSubjectChange = useCallback((val: string[]) => {
    if (val.length > 0) setForm((prev) => ({ ...prev, subject: val[0] }))
  }, [])

  return (
    <div className="w-full">
      <section className="relative flex min-h-screen w-full flex-col justify-between overflow-hidden bg-background p-6 text-foreground transition-colors duration-500 sm:p-12 lg:p-20">
        <div className="relative mx-auto my-12 grid w-full max-w-7xl grid-cols-1 items-start gap-12 lg:my-20 lg:grid-cols-12 lg:gap-20">
          <motion.div
            initial={{ opacity: 0, y: 24 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.6, ease: [0.16, 1, 0.3, 1] }}
            className="flex flex-col gap-5 lg:col-span-5"
          >
            <div className="flex flex-col items-center justify-center gap-4 lg:items-start">
              <TypewriterHeadline />
              <p className="max-w-md text-center text-base text-muted-foreground sm:text-lg md:max-w-xl md:text-xl lg:max-w-2xl lg:text-left lg:text-base">
                Have a new project in mind, a complex technical challenge, or
                simply want to explore engineering collaboration? Drop us a
                line.
              </p>
            </div>

            <Separator />

            <div className="flex flex-col gap-8 pt-6">
              {CONTACT_INFO_DATA.map((item) => (
                <ContactInfoItem key={item.id} item={item} />
              ))}
            </div>

            <Separator />

            <div className="flex flex-col gap-4 pt-6">
              <span className="text-xs font-bold tracking-widest text-muted-foreground uppercase">
                Connect Directly
              </span>
              <div className="flex flex-wrap gap-x-6 gap-y-3">
                {SOCIAL_LINKS.map((s) => {
                  const Icon = s.icon
                  return (
                    <Link
                      key={s.name}
                      href={s.href}
                      target="_blank"
                      rel="noreferrer"
                      className="group relative flex items-center gap-2 py-1 text-xs font-bold tracking-widest text-foreground uppercase transition-colors hover:text-primary"
                    >
                      <div
                        className={`flex h-6 w-6 shrink-0 items-center justify-center rounded-md transition-transform group-hover:scale-110 ${s.isGithub ? "bg-foreground/10 text-foreground" : ""}`}
                        style={
                          !s.isGithub
                            ? { backgroundColor: `${s.brandColor}22` }
                            : undefined
                        }
                      >
                        <Icon
                          className="h-3.5 w-3.5"
                          style={
                            !s.isGithub ? { color: s.brandColor } : undefined
                          }
                        />
                      </div>
                      <span>{s.name}</span>
                      <span
                        className={`absolute bottom-0 left-1/2 h-[1.5px] w-0 -translate-x-1/2 transition-all duration-300 group-hover:w-full ${s.isGithub ? "bg-foreground" : ""}`}
                        style={
                          !s.isGithub
                            ? { backgroundColor: s.brandColor }
                            : undefined
                        }
                      />
                    </Link>
                  )
                })}
              </div>
            </div>
          </motion.div>

          <motion.div
            initial={{ opacity: 0, y: 32 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.7, delay: 0.1, ease: [0.16, 1, 0.3, 1] }}
            className="flex w-full flex-col gap-8 lg:col-span-7"
          >
            <AnimatePresence mode="wait">
              {submitted ? (
                <SuccessMessage
                  name={form.name}
                  email={form.email}
                  onReset={handleReset}
                />
              ) : (
                <motion.form
                  key="form-state"
                  onSubmit={handleSubmit}
                  noValidate
                  className="flex w-full flex-col gap-16 rounded-3xl border border-slate-300/50 bg-gradient-to-br from-background/90 to-muted/90 p-10 shadow-2xl shadow-primary/5 backdrop-blur-lg dark:border-slate-700/50"
                >
                  <FormField
                    id="contact-name"
                    field="name"
                    label="Your Full Name *"
                    value={form.name}
                    focusedField={focusedField}
                    onFocus={handleFocus}
                    onBlur={handleBlur}
                    onChange={(e: ChangeEvent<HTMLInputElement>) =>
                      handleFieldChange("name")(e.target.value)
                    }
                  />

                  <FormField
                    id="contact-email"
                    field="email"
                    label="Work Email Address *"
                    type="email"
                    value={form.email}
                    focusedField={focusedField}
                    onFocus={handleFocus}
                    onBlur={handleBlur}
                    onChange={(e: ChangeEvent<HTMLInputElement>) =>
                      handleFieldChange("email")(e.target.value)
                    }
                  />

                  <div className="flex flex-col gap-3">
                    <span className="text-xs font-semibold tracking-wider text-muted-foreground uppercase">
                      Project Nature / Inquiry Scope
                    </span>

                    <ToggleGroup
                      value={form.subject ? [form.subject] : []}
                      onValueChange={handleSubjectChange}
                      spacing={3}
                      className="flex flex-wrap"
                    >
                      {SUBJECT_OPTIONS.map((item) => (
                        <ToggleGroupItem
                          key={item}
                          value={item}
                          variant="outline"
                          className="h-auto rounded-full border-border/40 bg-muted/30 px-5 py-2.5 text-xs font-semibold tracking-wider text-muted-foreground uppercase shadow-none transition-all duration-300 hover:-translate-y-0.5 hover:border-primary/50 hover:bg-muted hover:text-foreground active:scale-95 data-[state=on]:border-primary data-[state=on]:bg-primary/10 data-[state=on]:text-primary data-[state=on]:shadow-[0_0_15px_-3px_hsl(var(--primary)/0.2)] data-[state=on]:hover:border-primary/80 data-[state=on]:hover:bg-primary/20"
                        >
                          {item}
                        </ToggleGroupItem>
                      ))}
                    </ToggleGroup>
                  </div>

                  <FormField
                    id="contact-message"
                    field="message"
                    label="Project Details & Requirements *"
                    isTextarea
                    value={form.message}
                    focusedField={focusedField}
                    onFocus={handleFocus}
                    onBlur={handleBlur}
                    onChange={handleMessageChange}
                    textareaRef={textareaRef}
                  />

                  <div className="pt-4">
                    <Button
                      type="submit"
                      disabled={isSubmitting}
                      className="group relative flex h-14 w-full items-center justify-center gap-2.5 rounded-none text-sm font-bold tracking-wider uppercase transition-all duration-300 hover:scale-[1.01] active:scale-[0.99]"
                    >
                      <span>
                        {isSubmitting
                          ? "Transmitting..."
                          : "Submit Transmission"}
                      </span>
                      <ArrowUpRight className="h-4 w-4 shrink-0 transform transition-transform duration-300 group-hover:translate-x-1 group-hover:-translate-y-1" />
                    </Button>
                  </div>
                </motion.form>
              )}
            </AnimatePresence>
          </motion.div>
        </div>
      </section>
    </div>
  )
}
```
