Vai al contenuto

Pulsante

Un pulsante cliccabile per l'invio dei moduli, le barre degli strumenti e le conferme definitive — con bordi spessi e un'ombra che si incava al clic.

import { ArrowUpIcon } from "lucide-react"

import { Button } from "@/components/ui/button"

Il pulsante innesca un’azione — inviare, confermare, eliminare. Costruito sul componente Base UI Button, rifinito secondo la ricetta neobrutalista: bordi spessi, un’ombra netta sfalsata e una pressione attiva che spinge il pulsante dentro di essa.

Quando usarlo:

  • Invii di moduli e CTA principalidefault porta l’azione principale; secondary e outline per tutto ciò che le sta accanto.
  • Toolbar e azioni a icona — le taglie icon creano pulsanti quadrati; ButtonGroup li allinea in file segmentate.
  • Conferme distruttivedestructive per eliminare e rimuovere, di solito dietro una finestra di dialogo.

Installazione

pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/button.json
npx shadcn@latest add https://neobrutalism.com/r/base/button.json
yarn dlx shadcn@latest add https://neobrutalism.com/r/base/button.json
bunx --bun shadcn@latest add https://neobrutalism.com/r/base/button.json

Installa le seguenti dipendenze:

pnpm add @base-ui/react
npm install @base-ui/react
yarn add @base-ui/react
bun add @base-ui/react

Copia e incolla il seguente codice nel tuo progetto.

components/ui/button.tsx
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { Slot } from "radix-ui"

import { cn } from "@/lib/utils"

const buttonVariants = cva(
  cn(
    "group/button font-head font-medium inline-flex cursor-pointer items-center justify-center gap-2 rounded whitespace-nowrap select-none transition-all duration-200",
    "disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-60",
    "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary aria-invalid:border-destructive",
    // Icons keep their own size; we only set a default when none is given so
    // Neobrutalism's h-4/size-4 icons aren't overridden.
    "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
  ),
  {
    variants: {
      variant: {
        default:
          "border-2 border-black bg-primary text-primary-foreground shadow-md transition duration-200 hover:-translate-x-0.5 hover:-translate-y-0.5 hover:bg-primary-hover hover:shadow-lg active:translate-x-1 active:translate-y-1 active:shadow-none",
        secondary:
          "border-2 border-black bg-secondary text-secondary-foreground shadow-md transition duration-200 hover:-translate-x-0.5 hover:-translate-y-0.5 hover:bg-secondary-hover hover:shadow-lg active:translate-x-1 active:translate-y-1 active:shadow-none",
        destructive:
          "border-2 border-black bg-destructive text-destructive-foreground shadow-md transition duration-200 hover:-translate-x-0.5 hover:-translate-y-0.5 hover:bg-destructive/90 hover:shadow-lg active:translate-x-1 active:translate-y-1 active:shadow-none",
        outline:
          "border-2 bg-transparent shadow-md transition duration-200 hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-lg active:translate-x-1 active:translate-y-1 active:shadow-none",
        ghost: "bg-transparent hover:bg-accent",
        link: "bg-transparent hover:underline",
      },
      size: {
        default: "px-4 py-1.5 text-base",
        xs: "px-2 py-0.5 text-xs",
        sm: "px-3 py-1 text-sm",
        lg: "px-6 py-2 text-base lg:px-8 lg:py-3 lg:text-lg",
        icon: "p-2",
        "icon-xs": "p-1",
        "icon-sm": "p-1.5",
        "icon-lg": "p-3",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

function Button({
  className,
  variant = "default",
  size = "default",
  asChild = false,
  ...props
}: React.ComponentProps<"button"> &
  VariantProps<typeof buttonVariants> & {
    asChild?: boolean
  }) {
  const Comp = asChild ? Slot.Root : "button"

  return (
    <Comp
      data-slot="button"
      data-variant={variant}
      data-size={size}
      className={cn(buttonVariants({ variant, size, className }))}
      {...props}
    />
  )
}

export { Button, buttonVariants }

Aggiorna i percorsi di import in base alla struttura del tuo progetto.

Utilizzo

import { Button } from "@/components/ui/button"
<Button variant="outline">Button</Button>

Cursor

Tailwind v4 è passato da cursor: pointer a cursor: default per il componente pulsante.

Se vuoi mantenere il comportamento cursor: pointer, aggiungi il seguente codice al tuo file CSS:

Puoi anche abilitarlo durante la configurazione del progetto con npx shadcn@latest init --pointer.

globals.css
@layer base {
  button:not(:disabled),
  [role="button"]:not(:disabled) {
    cursor: pointer;
  }
}

Esempi

Dimensione

Usa la prop size per cambiare la dimensione del pulsante.

import { ArrowUpRightIcon } from "lucide-react"

import { Button } from "@/components/ui/button"

Default

import { Button } from "@/components/ui/button"

export function ButtonDefault() {

Outline

import { Button } from "@/components/ui/button"

export function ButtonOutline() {

Secondary

import { Button } from "@/components/ui/button"

export function ButtonSecondary() {

Ghost

import { Button } from "@/components/ui/button"

export function ButtonGhost() {

Destructive

import { Button } from "@/components/ui/button"

export function ButtonDestructive() {
import { Button } from "@/components/ui/button"

export function ButtonLink() {

Icon

import { CircleFadingArrowUpIcon } from "lucide-react"

import { Button } from "@/components/ui/button"

Con icona

Ricorda di aggiungere all'icona l'attributo data-icon="inline-start" o data-icon="inline-end" per una spaziatura corretta.

import { IconGitBranch, IconGitFork } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"

Arrotondato

Usa la classe rounded-full per rendere il pulsante arrotondato.

import { ArrowUpIcon } from "lucide-react"

import { Button } from "@/components/ui/button"

Spinner

Rendi un componente <Spinner /> dentro il pulsante per mostrare uno stato di caricamento. Ricorda di aggiungere allo spinner l'attributo data-icon="inline-start" o data-icon="inline-end" per una spaziatura corretta.

import { Button } from "@/components/ui/button"
import { Spinner } from "@/components/ui/spinner"

Button Group

Per creare un gruppo di pulsanti, usa il componente ButtonGroup. Consulta la documentazione Button Group per maggiori dettagli.

"use client"

import * as React from "react"

Puoi usare l'helper buttonVariants per far sembrare un link un pulsante.

Non usare <Button render={<a />} nativeButton={false} /> per i link. Il componente Button di Base UI applica sempre role="button", che sovrascrive il ruolo semantico di link sugli elementi <a>. Usa invece buttonVariants con un semplice tag <a>.

import { buttonVariants } from "@/components/ui/button"

export function ButtonRender() {

RTL

Per abilitare il supporto RTL in Neobrutalism, consulta la guida alla configurazione RTL.

"use client"

import { ArrowRightIcon, PlusIcon } from "lucide-react"

Accessibilità

Il pulsante segue il pattern WAI-ARIA Button. Per impostazione predefinita rende un <button> nativo; quando render lo sostituisce con un altro elemento, Base UI collega role="button", tabindex e l’attivazione da tastiera così che il sostituto si comporti come un vero pulsante per le tecnologie assistive (vedi la nota “Come link” qui sopra per l’unico caso in cui quel ruolo è sbagliato).

Interazioni da tastiera:

TastoAzione
Tab / Shift + TabSposta il focus sul pulsante / via dal pulsante
Space / EnterAttiva il pulsante

Riferimento API

Button

Il componente Button è un wrapper attorno all'elemento button che aggiunge una varietà di stili e funzionalità.

PropTypeDefault
variant"default" | "outline" | "ghost" | "destructive" | "secondary" | "link""default"
size"default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg""default"