Todos os componentes

Header 1

Header 1 é um componente de layout para React e Tailwind CSS, da biblioteca SmoothUI, com licença MIT. Copia e cola no teu projeto.

SmoothUI MIT layout

O que é

Header 1 é um componente de layout para React e Tailwind CSS, da biblioteca SmoothUI, 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 lucide-react motion

Licença

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

Header 1

index.tsx
"use client";

import { ExternalLink } from "lucide-react";
import { useEffect, useRef, useState } from "react";
import { AnimatedGroup, AnimatedText, Button, HeroHeader } from "@/components/smoothui/shared";
import styles from "./hero-grid.module.css";

const CELL_SIZE = 120; // px
const COLORS = [
  "oklch(0.72 0.2 352.53)", // blue
  "#A764FF",
  "#4B94FD",
  "#FD4B4E",
  "#FF8743",
];

function getRandomColor() {
  return COLORS[Math.floor(Math.random() * COLORS.length)];
}

function SubGrid() {
  const [cellColors, setCellColors] = useState<(string | null)[]>([
    null,
    null,
    null,
    null,
  ]);
  // Add refs for leave timeouts
  const leaveTimeouts = useRef<(NodeJS.Timeout | null)[]>([
    null,
    null,
    null,
    null,
  ]);

  function handleHover(cellIdx: number) {
    // Clear any pending timeout for this cell
    const timeout = leaveTimeouts.current[cellIdx];
    if (timeout) {
      clearTimeout(timeout);
      leaveTimeouts.current[cellIdx] = null;
    }
    setCellColors((prev) =>
      prev.map((c, i) => (i === cellIdx ? getRandomColor() : c))
    );
  }
  function handleLeave(cellIdx: number) {
    // Add a small delay before removing the color
    leaveTimeouts.current[cellIdx] = setTimeout(() => {
      setCellColors((prev) => prev.map((c, i) => (i === cellIdx ? null : c)));
      leaveTimeouts.current[cellIdx] = null;
    }, 120);
  }
  // Cleanup on unmount
  useEffect(
    () => () => {
      for (const t of leaveTimeouts.current) {
        if (t) {
          clearTimeout(t);
        }
      }
    },
    []
  );

  return (
    <div className={styles.subgrid} style={{ pointerEvents: "none" }}>
      {[0, 1, 2, 3].map((cellIdx) => (
        <button
          className={styles.cell}
          key={cellIdx}
          onMouseEnter={() => handleHover(cellIdx)}
          onMouseLeave={() => handleLeave(cellIdx)}
          style={{
            background: cellColors[cellIdx] || "transparent",
            pointerEvents: "auto",
          }}
          type="button"
        />
      ))}
    </div>
  );
}

function InteractiveGrid() {
  const containerRef = useRef<HTMLDivElement>(null);
  const [grid, setGrid] = useState({ columns: 0, rows: 0 });

  useEffect(() => {
    function updateGrid() {
      if (containerRef.current) {
        const { width, height } = containerRef.current.getBoundingClientRect();
        setGrid({
          columns: Math.ceil(width / CELL_SIZE),
          rows: Math.ceil(height / CELL_SIZE),
        });
      }
    }
    updateGrid();
    window.addEventListener("resize", updateGrid);
    return () => window.removeEventListener("resize", updateGrid);
  }, []);

  const total = grid.columns * grid.rows;

  return (
    <div
      aria-hidden="true"
      className="pointer-events-none absolute inset-0 z-0"
      ref={containerRef}
      style={{ height: "100%", width: "100%" }}
    >
      <div
        className={styles.mainGrid}
        style={
          {
            "--grid-cell-size": `${CELL_SIZE}px`,
            gridTemplateColumns: `repeat(${grid.columns}, 1fr)`,
            gridTemplateRows: `repeat(${grid.rows}, 1fr)`,
            height: "100%",
            width: "100%",
          } as React.CSSProperties
        }
      >
        {Array.from({ length: total }, (_, idx) => (
          <SubGrid key={`subgrid-${grid.columns}-${grid.rows}-${idx}`} />
        ))}
      </div>
    </div>
  );
}

export function HeroGrid() {
  return (
    <div className="relative">
      <HeroHeader />
      <main>
        <section className="relative overflow-hidden py-36">
          {/* Interactive animated grid background */}
          <InteractiveGrid />
          <AnimatedGroup
            className="pointer-events-none flex flex-col items-center gap-6 text-center"
            preset="blur-slide"
          >
            <div>
              <AnimatedText
                as="h1"
                className="mb-6 text-pretty font-bold text-2xl tracking-tight lg:text-5xl"
              >
                Build your next project with{" "}
                <span className="text-brand">Smoothui</span>
              </AnimatedText>
              <AnimatedText
                as="p"
                className="mx-auto max-w-3xl text-muted-foreground lg:text-xl"
                delay={0.15}
              >
                Smoothui gives you the building blocks to create stunning,
                animated interfaces in minutes.
              </AnimatedText>
            </div>
            <AnimatedGroup
              className="pointer-events-auto mt-6 flex justify-center gap-3"
              preset="slide"
            >
              <Button
                className="shadow-sm transition-shadow hover:shadow"
                variant="outline"
              >
                Get Started
              </Button>
              <Button className="group" variant="candy">
                Learn more{" "}
                <ExternalLink className="ml-2 h-4 transition-transform group-hover:translate-x-0.5" />
              </Button>
            </AnimatedGroup>
          </AnimatedGroup>
        </section>
      </main>
    </div>
  );
}

export default HeroGrid;
hero-grid.module.css
/* @reference "../../../../app/styles/smoothui.css"; */

.mainGrid {
  position: absolute;
  top: 0;
  left: 0;
  display: grid;
  width: 100%;
  height: 100%;
  pointer-events: auto;
  border-top: 1px solid var(--color-smooth-200);
  border-right: 1px solid var(--color-smooth-200);
  opacity: 1;
  transition: opacity 300ms;
  will-change: transform;
}

.subgrid {
  position: relative;
  display: grid;
  grid-template-rows: repeat(2, 1fr);
  grid-template-columns: repeat(2, 1fr);
  width: var(--grid-cell-size);
  height: var(--grid-cell-size);
  pointer-events: auto;
  user-select: none;
  border-top: 1px solid var(--color-smooth-200);
  border-right: 1px solid var(--color-smooth-200);
}

.cell {
  width: 100%;
  height: 100%;
  pointer-events: auto;
  transition: background 3000ms cubic-bezier(0.23, 1, 0.32, 1);
}

.cell:nth-child(1) {
  border-right: 1px solid var(--color-smooth-200);
}
.cell:nth-child(3) {
  border-top: 1px solid var(--color-smooth-200);
  border-right: 1px solid var(--color-smooth-200);
}
.cell:nth-child(4) {
  border-top: 1px solid var(--color-smooth-200);
}

/* Reduce overlay size of hero text/button containers */
.heroContent {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  max-width: 600px;
  padding: 0;
  margin: 0 auto;
  background: none;
  box-shadow: none;
}

.heroButtons {
  display: flex;
  gap: 1rem;
  width: auto;
  padding: 0;
  margin-top: 1.5rem;
  background: none;
  box-shadow: none;
}

Componentes parecidos