Todos os componentes

Pricing Overview

Pricing Overview é um componente de layout para React e Tailwind CSS, da biblioteca UI Layouts, com licença MIT. Copia e cola no teu projeto.

UI Layouts MIT layout

O que é

Pricing Overview é um componente de layout para React e Tailwind CSS, da biblioteca UI Layouts, com licença MIT. Copia e cola no teu projeto.

Como usar

Abre o componente no catálogo, carrega em copiar, e cola no teu projeto. O prompt copiado leva o código e as regras para o teu agente o reproduzir sem alterar nada.

Abrir no catálogo

Cinco cópias grátis por mês. Sem cartão.

Precisa de

npm i motion

Licença

Este componente vem de UI Layouts e é redistribuído sob a licença MIT, que permite uso comercial. O aviso de copyright viaja com o código.

Pricing Overview

../../packages/blocks/src/pricing-section/pricing-overview.tsx
'use client'
import React, { useId, useState, useRef } from 'react'
import { Calendar, Check } from 'lucide-react'
import { motion } from 'motion/react'
import NumberFlow from '@number-flow/react'
import { Button, Switch } from '@repo/shadcn'
import { cn } from '@/lib/utils'
import { TimelineAnimation } from '@/components/ui/timeline-animation'

export const PricingOverview = () => {
  const [billing, setBilling] = useState<'monthly' | 'yearly'>('monthly')
  const id = useId()
  const timelineRef = useRef<HTMLDivElement>(null)

  // Calculate prices based on billing cycle (20% discount for yearly)
  const landingPagePrice = billing === 'monthly' ? 1699 : 1500
  const productDesignPrice = billing === 'monthly' ? 3099 : 2300
  const partnershipPrice = billing === 'monthly' ? 4099 : 3100

  return (
    <section
      ref={timelineRef}
      className="py-20 px-6 max-w-5xl mx-auto font-dmSans"
    >
      <div className="text-left mb-12">
        <TimelineAnimation
          animationNum={1}
          timelineRef={timelineRef}
          as="h1"
          className="text-4xl md:text-5xl font-bold tracking-tight text-balance mb-4"
        >
          Flexible plans for every business
        </TimelineAnimation>
        <TimelineAnimation
          animationNum={2}
          timelineRef={timelineRef}
          as="p"
          className="text-neutral-500 text-pretty max-w-lg leading-relaxed"
        >
          Your product design partner. Unlock instant, world-class design with a
          simple monthly fee.
        </TimelineAnimation>
      </div>

      <TimelineAnimation
        animationNum={3}
        timelineRef={timelineRef}
        className="flex flex-col items-start gap-6 mb-6"
      >
        <TimelineAnimation
          animationNum={4}
          timelineRef={timelineRef}
          className="flex items-center gap-4"
        >
          <span
            className={cn(
              'text-md transition-colors',
              billing === 'monthly'
                ? 'text-neutral-900 font-bold'
                : 'text-neutral-400'
            )}
          >
            Monthly
          </span>
          <Switch
            id={id}
            // @ts-ignore
            checked={billing === 'yearly'}
            className="bg-neutral-300 rounded-full"
            thumbsClassName="rounded-full"
            // @ts-ignore
            onCheckedChange={(checked) =>
              setBilling(checked ? 'yearly' : 'monthly')
            }
          />
          <div className="flex items-center gap-2">
            <span
              className={cn(
                'text-sm transition-colors ',
                billing === 'yearly'
                  ? 'text-neutral-900 font-bold'
                  : 'text-neutral-400'
              )}
            >
              Yearly
            </span>
            <span className="px-2 py-0.5 bg-[#C7FF33] text-[9px] font-bold rounded-full uppercase tracking-tighter">
              Save 20%
            </span>
          </div>
        </TimelineAnimation>
      </TimelineAnimation>

      <TimelineAnimation
        animationNum={4}
        timelineRef={timelineRef}
        className="grid md:grid-cols-2 gap-6"
      >
        {/* Landing Page Card */}
        <TimelineAnimation
          animationNum={5}
          timelineRef={timelineRef}
          className="bg-gray-100 border border-neutral-200 rounded-xl p-2 flex flex-col hover:shadow-md transition-shadow"
        >
          <div className="bg-white p-1.5 rounded-lg">
            <div className="p-4 bg-neutral-100 border border-zinc-100 rounded-lg space-y-2">
              <h3 className="text-3xl font-bold mb-2">Landing Page</h3>
              <p className="text-neutral-500 text-sm font-medium">
                Get a high quality landing page for your product.
              </p>
              <div className="flex items-baseline gap-1">
                <span className="text-3xl font-bold flex items-center">
                  $
                  <NumberFlow
                    value={landingPagePrice}
                    format={{ useGrouping: false }}
                  />
                </span>
                <span className="text-neutral-400 text-sm">
                  /{billing === 'monthly' ? 'fixed' : 'fixed (yearly rate)'}
                </span>
              </div>
              <TimelineAnimation animationNum={6} timelineRef={timelineRef}>
                <Button className="w-full gap-2 mt-2 text-lg" size="lg">
                  Book a call <Calendar className="size-4" />
                </Button>
              </TimelineAnimation>
            </div>
          </div>

          <div className="grid grid-cols-2 gap-y-4 p-4">
            {[
              'Design concepts',
              'Tailored solution',
              'Responsive design',
              'Revisions',
              'Up to 4 subpages',
              'Post-launch support',
            ].map((item, i) => (
              <div
                key={i}
                className="flex items-center gap-2 text-md text-neutral-600 font-medium"
              >
                <svg
                  xmlns="http://www.w3.org/2000/svg"
                  viewBox="0 0 24 24"
                  width="24"
                  height="24"
                  fill="#000000"
                  stroke="#fff"
                  stroke-width="1.5"
                >
                  <path d="M18.9905 19H19M18.9905 19C18.3678 19.6175 17.2393 19.4637 16.4479 19.4637C15.4765 19.4637 15.0087 19.6537 14.3154 20.347C13.7251 20.9374 12.9337 22 12 22C11.0663 22 10.2749 20.9374 9.68457 20.347C8.99128 19.6537 8.52349 19.4637 7.55206 19.4637C6.76068 19.4637 5.63218 19.6175 5.00949 19C4.38181 18.3776 4.53628 17.2444 4.53628 16.4479C4.53628 15.4414 4.31616 14.9786 3.59938 14.2618C2.53314 13.1956 2.00002 12.6624 2 12C2.00001 11.3375 2.53312 10.8044 3.59935 9.73817C4.2392 9.09832 4.53628 8.46428 4.53628 7.55206C4.53628 6.76065 4.38249 5.63214 5 5.00944C5.62243 4.38178 6.7556 4.53626 7.55208 4.53626C8.46427 4.53626 9.09832 4.2392 9.73815 3.59937C10.8044 2.53312 11.3375 2 12 2C12.6625 2 13.1956 2.53312 14.2618 3.59937C14.9015 4.23907 15.5355 4.53626 16.4479 4.53626C17.2393 4.53626 18.3679 4.38247 18.9906 5C19.6182 5.62243 19.4637 6.75559 19.4637 7.55206C19.4637 8.55858 19.6839 9.02137 20.4006 9.73817C21.4669 10.8044 22 11.3375 22 12C22 12.6624 21.4669 13.1956 20.4006 14.2618C19.6838 14.9786 19.4637 15.4414 19.4637 16.4479C19.4637 17.2444 19.6182 18.3776 18.9905 19Z" />
                  <path d="M9 12.8929C9 12.8929 10.2 13.5447 10.8 14.5C10.8 14.5 12.6 10.75 15 9.5" />
                </svg>
                {item}
              </div>
            ))}
          </div>
        </TimelineAnimation>

        {/* Product Design Card */}
        <TimelineAnimation
          animationNum={7}
          timelineRef={timelineRef}
          className="bg-gray-100 border border-neutral-200 rounded-xl p-2 flex flex-col hover:shadow-md transition-shadow"
        >
          <div className="bg-white p-1.5 rounded-lg">
            <div className="p-4 bg-neutral-100 border border-zinc-100 rounded-lg relative overflow-hidden space-y-2">
              <div className="absolute top-0 right-0 size-32 bg-red-500/40 blur-3xl -mr-16 -mt-16 group-hover:bg-[#FF7777]/20 transition-colors" />
              <h3 className="text-3xl font-bold mb-2">Product design</h3>
              <p className="text-neutral-500 text-sm">
                Let's bring your idea to life.
              </p>
              <div className="flex items-baseline gap-1">
                <span className="text-3xl font-bold flex items-center">
                  $
                  <NumberFlow
                    value={productDesignPrice}
                    format={{ useGrouping: false }}
                  />
                </span>
                <span className="text-neutral-400 text-sm">
                  /{billing === 'monthly' ? 'fixed' : 'fixed (yearly rate)'}
                </span>
              </div>
              <TimelineAnimation animationNum={8} timelineRef={timelineRef}>
                <Button className="w-full mt-2 gap-2 text-lg" size="lg">
                  Book a call <Calendar className="size-4" />
                </Button>
              </TimelineAnimation>
            </div>
          </div>

          <div className="grid grid-cols-2 gap-y-4 p-4">
            {[
              'Brainstorming',
              'Tailored solution',
              'UX-based strategy',
              'Design concepts',
              'Revisions',
              'Support',
              'On-time delivery',
              'Scaleable',
              'Async communication',
            ].map((item, i) => (
              <div
                key={i}
                className="flex items-center gap-2 text-md font-medium text-neutral-600"
              >
                <svg
                  xmlns="http://www.w3.org/2000/svg"
                  viewBox="0 0 24 24"
                  width="24"
                  height="24"
                  fill="#000000"
                  stroke="#fff"
                  stroke-width="1.5"
                >
                  <path d="M18.9905 19H19M18.9905 19C18.3678 19.6175 17.2393 19.4637 16.4479 19.4637C15.4765 19.4637 15.0087 19.6537 14.3154 20.347C13.7251 20.9374 12.9337 22 12 22C11.0663 22 10.2749 20.9374 9.68457 20.347C8.99128 19.6537 8.52349 19.4637 7.55206 19.4637C6.76068 19.4637 5.63218 19.6175 5.00949 19C4.38181 18.3776 4.53628 17.2444 4.53628 16.4479C4.53628 15.4414 4.31616 14.9786 3.59938 14.2618C2.53314 13.1956 2.00002 12.6624 2 12C2.00001 11.3375 2.53312 10.8044 3.59935 9.73817C4.2392 9.09832 4.53628 8.46428 4.53628 7.55206C4.53628 6.76065 4.38249 5.63214 5 5.00944C5.62243 4.38178 6.7556 4.53626 7.55208 4.53626C8.46427 4.53626 9.09832 4.2392 9.73815 3.59937C10.8044 2.53312 11.3375 2 12 2C12.6625 2 13.1956 2.53312 14.2618 3.59937C14.9015 4.23907 15.5355 4.53626 16.4479 4.53626C17.2393 4.53626 18.3679 4.38247 18.9906 5C19.6182 5.62243 19.4637 6.75559 19.4637 7.55206C19.4637 8.55858 19.6839 9.02137 20.4006 9.73817C21.4669 10.8044 22 11.3375 22 12C22 12.6624 21.4669 13.1956 20.4006 14.2618C19.6838 14.9786 19.4637 15.4414 19.4637 16.4479C19.4637 17.2444 19.6182 18.3776 18.9905 19Z" />
                  <path d="M9 12.8929C9 12.8929 10.2 13.5447 10.8 14.5C10.8 14.5 12.6 10.75 15 9.5" />
                </svg>
                {item}
              </div>
            ))}
          </div>
        </TimelineAnimation>
      </TimelineAnimation>

      {/* Partnership Card */}
      <TimelineAnimation
        animationNum={9}
        timelineRef={timelineRef}
        className="mt-6 bg-linear-to-b from-neutral-950 to-neutral-800 text-white rounded-3xl p-10 md:flex items-center gap-4 relative overflow-hidden"
      >
        <div className="flex-1 space-y-3 relative z-10">
          <div className="flex items-center gap-2 text-lime-400 text-sm font-medium">
            <div className="size-2 rounded-full bg-lime-500 animate-pulse" />
            Limited availability
          </div>
          <h3 className="text-3xl font-bold">Product Partnership</h3>
          <p className="text-neutral-400 text-pretty">
            Get full-product team, according to your needs. No long-term
            commitment.
          </p>
          <div className="flex items-baseline gap-1">
            <span className="text-4xl font-bold flex items-center">
              $
              <NumberFlow
                value={partnershipPrice}
                format={{ useGrouping: false }}
              />
            </span>
            <span className="text-neutral-500 text-sm">
              /{billing === 'monthly' ? 'month' : 'yearly'}
            </span>
          </div>
          <TimelineAnimation animationNum={10} timelineRef={timelineRef}>
            <Button
              variant="outline"
              className="w-fit text-black bg-white hover:bg-neutral-100 gap-2 px-8 text-lg"
            >
              Book a call <Calendar className="size-5" />
            </Button>
          </TimelineAnimation>
        </div>
        <div className="flex-1 grid grid-cols-1 sm:grid-cols-2 gap-y-6 gap-2 relative z-10">
          {[
            'Development lead',
            'Flexible communication',
            'No hire costs',
            '1 workstream',
            'Pause or resume anytime',
            'Revisions',
          ].map((item, i) => (
            <TimelineAnimation
              key={i}
              animationNum={11 + i}
              timelineRef={timelineRef}
              className="flex items-center gap-3 text-sm text-neutral-300"
            >
              <svg
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 24 24"
                width="24"
                height="24"
                fill="#000000"
                stroke="#fff"
                stroke-width="1.5"
              >
                <path d="M18.9905 19H19M18.9905 19C18.3678 19.6175 17.2393 19.4637 16.4479 19.4637C15.4765 19.4637 15.0087 19.6537 14.3154 20.347C13.7251 20.9374 12.9337 22 12 22C11.0663 22 10.2749 20.9374 9.68457 20.347C8.99128 19.6537 8.52349 19.4637 7.55206 19.4637C6.76068 19.4637 5.63218 19.6175 5.00949 19C4.38181 18.3776 4.53628 17.2444 4.53628 16.4479C4.53628 15.4414 4.31616 14.9786 3.59938 14.2618C2.53314 13.1956 2.00002 12.6624 2 12C2.00001 11.3375 2.53312 10.8044 3.59935 9.73817C4.2392 9.09832 4.53628 8.46428 4.53628 7.55206C4.53628 6.76065 4.38249 5.63214 5 5.00944C5.62243 4.38178 6.7556 4.53626 7.55208 4.53626C8.46427 4.53626 9.09832 4.2392 9.73815 3.59937C10.8044 2.53312 11.3375 2 12 2C12.6625 2 13.1956 2.53312 14.2618 3.59937C14.9015 4.23907 15.5355 4.53626 16.4479 4.53626C17.2393 4.53626 18.3679 4.38247 18.9906 5C19.6182 5.62243 19.4637 6.75559 19.4637 7.55206C19.4637 8.55858 19.6839 9.02137 20.4006 9.73817C21.4669 10.8044 22 11.3375 22 12C22 12.6624 21.4669 13.1956 20.4006 14.2618C19.6838 14.9786 19.4637 15.4414 19.4637 16.4479C19.4637 17.2444 19.6182 18.3776 18.9905 19Z" />
                <path d="M9 12.8929C9 12.8929 10.2 13.5447 10.8 14.5C10.8 14.5 12.6 10.75 15 9.5" />
              </svg>
              {item}
            </TimelineAnimation>
          ))}
        </div>
      </TimelineAnimation>
    </section>
  )
}
./components/ui/timeline-animation.tsx
import type { Variants } from 'motion/react';
import { type HTMLMotionProps, motion, useInView } from 'motion/react';
import type React from 'react';

type TimelineContentProps<T extends keyof HTMLElementTagNameMap> = {
  children?: React.ReactNode;
  animationNum: number;
  className?: string;
  timelineRef: React.RefObject<HTMLElement | null>;
  as?: T;
  customVariants?: Variants;
  once?: boolean;
} & HTMLMotionProps<T>;

export const TimelineAnimation = <T extends keyof HTMLElementTagNameMap = 'div'>({
  children,
  animationNum,
  timelineRef,
  className,
  as,
  customVariants,
  once = true,
  ...props
}: TimelineContentProps<T>) => {
  const defaultSequenceVariants = {
    visible: (i: number) => ({
      filter: 'blur(0px)',
      y: 0,
      opacity: 1,
      transition: {
        delay: i * 0.5,
        duration: 0.5,
      },
    }),
    hidden: {
      filter: 'blur(20px)',
      y: 0,
      opacity: 0,
    },
  };

  const sequenceVariants = customVariants || defaultSequenceVariants;

  const isInView = useInView(timelineRef, {
    once,
  });

  const MotionComponent = motion[as || 'div'] as React.ElementType;

  return (
    <MotionComponent
      initial='hidden'
      animate={isInView ? 'visible' : 'hidden'}
      custom={animationNum}
      variants={sequenceVariants}
      className={className}
      {...props}
    >
      {children}
    </MotionComponent>
  );
};

Componentes parecidos