Kickresume UI

Command Palette

Search for a command to run...

Button

Displays a button or a component that looks like a button.

Loading…
"use client"

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

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

export default function ButtonDemo() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-3">
      <Button>
        Build my resume
        <ArrowRightIcon data-icon="inline-end" />
      </Button>
      <Button variant="dark">See examples</Button>
      <Button variant="gradient-glow">
        <SparklesIcon data-icon="inline-start" />
        Upgrade
      </Button>
    </div>
  )
}

Installation

pnpm dlx shadcn@latest add @kickresume/button

Usage

import { Button } from "@/components/ui/button"
<div className="flex flex-wrap items-center justify-center gap-3">
  <Button>
    Build my resume
    <ArrowRightIcon data-icon="inline-end" />
  </Button>
  <Button variant="dark">See examples</Button>
  <Button variant="gradient-glow">
    <SparklesIcon data-icon="inline-start" />
    Upgrade
  </Button>
</div>

Cursor

Tailwind v4 switched from cursor: pointer to cursor: default for the button component.

If you want to keep the cursor: pointer behavior, add the following code to your CSS file:

You can also enable this during project setup with npx shadcn@latest init --pointer.

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

Examples

Kickresume brand variants

The complete design-system matrix includes five filled and five outline variants:

  • fills: coral, white, dark, gradient, gradient-glow
  • outlines: coral-ol, white-ol, dark-ol, gradient-ol, gradient-glow-ol

Filled variants use the dark focus ring. The coral outline uses the source gray focus ring; the other outlines use coral. Every brand variant implements its source hover, focus, pressed, and disabled colors. Hover and focus preserve the button's geometry, so surrounding layout does not shift between states.

Loading…
"use client"

import { Button } from "@/components/ui/button"
import { cn } from "@/lib/utils"

const filledVariants = [
  { value: "coral", label: "Coral" },
  { value: "white", label: "White" },
  { value: "dark", label: "Dark Blue" },
  { value: "gradient", label: "Gradient" },
  { value: "gradient-glow", label: "Gradient Glow" },
] as const

const outlineVariants = [
  { value: "coral-ol", label: "Coral Outline" },
  { value: "white-ol", label: "White Outline", dark: true },
  { value: "dark-ol", label: "Dark Blue Outline" },
  { value: "gradient-ol", label: "Gradient Outline" },
  { value: "gradient-glow-ol", label: "Gradient Glow Outline" },
] as const

const compatibilityVariants = [
  { value: "primary", label: "Primary" },
  { value: "outline-primary", label: "Outline Primary" },
  { value: "outline-dark", label: "Outline Dark" },
  { value: "secondary", label: "Secondary" },
  { value: "upgrade", label: "Upgrade" },
  { value: "ghost-dark", label: "Ghost Dark" },
  { value: "dashed-primary", label: "Dashed Primary" },
  { value: "text-primary", label: "Text Primary" },
  { value: "text-secondary", label: "Text Secondary" },
  { value: "upgrade-inverted", label: "Upgrade Inverted", dark: true },
  { value: "text-dark", label: "Text Dark" },
  { value: "link-dark", label: "Link Dark" },
  { value: "light", label: "Light", dark: true },
] as const

export default function ButtonBrandVariants() {
  return (
    <div className="flex w-full flex-col gap-8">
      <div className="flex flex-col gap-3">
        <p className="text-sm font-medium">Filled variants</p>
        <div className="grid grid-cols-[repeat(auto-fit,minmax(min(100%,12rem),1fr))] gap-3">
          {filledVariants.map(({ value, label }) => (
            <div
              key={value}
              className="flex min-h-20 items-center justify-center rounded-md border p-3"
            >
              <Button variant={value}>{label}</Button>
            </div>
          ))}
        </div>
      </div>

      <div className="flex flex-col gap-3">
        <p className="text-sm font-medium">Outline variants</p>
        <div className="grid grid-cols-[repeat(auto-fit,minmax(min(100%,12rem),1fr))] gap-3">
          {outlineVariants.map(({ value, label, ...variant }) => (
            <div
              key={value}
              className={cn(
                "flex min-h-20 items-center justify-center rounded-md border p-3",
                "dark" in variant && "bg-[var(--kr-blue-dark)]"
              )}
            >
              <Button variant={value}>{label}</Button>
            </div>
          ))}
        </div>
      </div>

      <div className="flex flex-col gap-3">
        <p className="text-sm font-medium">Ghost treatment</p>
        <div className="grid grid-cols-[repeat(auto-fit,minmax(min(100%,12rem),1fr))] gap-3">
          {outlineVariants.map(({ value, label, ...variant }) => (
            <div
              key={value}
              className={cn(
                "flex min-h-20 items-center justify-center rounded-md border p-3",
                "dark" in variant && "bg-[var(--kr-blue-dark)]"
              )}
            >
              <Button variant={value} ghost>
                {label.replace(" Outline", "")} Ghost
              </Button>
            </div>
          ))}
        </div>
      </div>

      <div className="flex flex-col gap-3">
        <p className="text-sm font-medium">Compatibility variants</p>
        <div className="grid grid-cols-[repeat(auto-fit,minmax(min(100%,12rem),1fr))] gap-3">
          {compatibilityVariants.map(({ value, label, ...variant }) => (
            <div
              key={value}
              className={cn(
                "flex min-h-28 flex-col items-center justify-center gap-2 rounded-md border p-3",
                "dark" in variant && "bg-[var(--kr-blue-dark)]"
              )}
            >
              <Button className="w-full max-w-40" variant={value}>
                {label}
              </Button>
              <Button className="w-full max-w-40" variant={value} disabled>
                Disabled
              </Button>
            </div>
          ))}
        </div>
      </div>
    </div>
  )
}

Compatibility API

The compatibility layer keeps the established Kickresume UI-kit names while the standard shadcn variants remain available:

VariantIntended treatment
primarySolid coral action
outline-primaryCoral outline action
outline-darkCharcoal outline action
secondarySolid Kickresume blue action
upgradePremium gradient action
ghost-darkDark ghost/outline action
dashed-primaryDashed coral outline action
text-primaryCoral text action
text-secondaryKickresume blue text action
upgrade-invertedWhite premium action for dark surfaces
text-darkCharcoal text action
link-darkCharcoal underlined action
lightWhite action for dark surfaces

The legacy aliases dashed, text, and secondary-blue remain supported for source compatibility. The live matrix above shows both normal and disabled states for all compatibility variants without replacing the ten generic brand variants.

Use ghost to remove a brand variant's fill and border while retaining its text color. Use outline to convert coral, white, dark, gradient, or gradient-glow to the corresponding -ol variant.

<Button variant="coral" outline>
  Coral Outline
</Button>
<Button variant="gradient-glow-ol" ghost>
  Premium Ghost
</Button>

Size

Use the size prop to change the size of the button.

Loading…
"use client"

import { ArrowUpRightIcon } from "lucide-react"

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

export default function ButtonSize() {
  return (
    <div className="flex flex-col items-start gap-8 sm:flex-row">
      <div className="flex items-start gap-2">
        <Button size="xs" variant="outline">
          Extra Small
        </Button>
        <Button size="icon-xs" aria-label="Submit" variant="outline">
          <ArrowUpRightIcon data-icon="inline-start" />
        </Button>
      </div>
      <div className="flex items-start gap-2">
        <Button size="sm" variant="outline">
          Small
        </Button>
        <Button size="icon-sm" aria-label="Submit" variant="outline">
          <ArrowUpRightIcon data-icon="inline-start" />
        </Button>
      </div>
      <div className="flex items-start gap-2">
        <Button variant="outline">Default</Button>
        <Button size="icon" aria-label="Submit" variant="outline">
          <ArrowUpRightIcon data-icon="inline-start" />
        </Button>
      </div>
      <div className="flex items-start gap-2">
        <Button variant="outline" size="lg">
          Large
        </Button>
        <Button size="icon-lg" aria-label="Submit" variant="outline">
          <ArrowUpRightIcon />
        </Button>
      </div>
    </div>
  )
}

Default

Loading…
"use client"

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

export default function ButtonDefault() {
  return <Button>Button</Button>
}

Outline

Loading…
"use client"

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

export default function ButtonOutline() {
  return <Button variant="outline">Outline</Button>
}

Secondary

Loading…
"use client"

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

export default function ButtonSecondary() {
  return <Button variant="secondary">Secondary</Button>
}

Ghost

Loading…
"use client"

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

export default function ButtonGhost() {
  return <Button variant="ghost">Ghost</Button>
}

Destructive

Loading…
"use client"

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

export default function ButtonDestructive() {
  return <Button variant="destructive">Destructive</Button>
}
Loading…
"use client"

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

export default function ButtonLink() {
  return <Button variant="link">Link</Button>
}

Icon

Loading…
"use client"

import { CircleFadingArrowUpIcon } from "lucide-react"

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

export default function ButtonIcon() {
  return (
    <Button variant="outline" size="icon" aria-label="Upgrade">
      <CircleFadingArrowUpIcon data-icon="inline-start" />
    </Button>
  )
}

With Icon

Place an icon before or after the label — the button spaces its children automatically with gap.

Loading…
"use client"

import { GitBranchIcon, GitForkIcon } from "lucide-react"

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

export default function ButtonWithIcon() {
  return (
    <div className="flex gap-2">
      <Button variant="outline">
        <GitBranchIcon data-icon="inline-start" /> New Branch
      </Button>
      <Button variant="outline">
        Fork
        <GitForkIcon data-icon="inline-end" />
      </Button>
    </div>
  )
}

Rounded

Use the rounded-full class to make the button rounded.

Loading…
"use client"

import { ArrowUpIcon } from "lucide-react"

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

export default function ButtonRounded() {
  return (
    <div className="flex gap-2">
      <Button className="rounded-full">Get Started</Button>
      <Button
        variant="outline"
        size="icon"
        aria-label="Submit"
        className="rounded-full"
      >
        <ArrowUpIcon data-icon="inline-start" />
      </Button>
    </div>
  )
}

Spinner

Render a <Spinner /> component inside the button to show a loading state.

Loading…
"use client"

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

export default function ButtonSpinner() {
  return (
    <div className="flex gap-2">
      <Button variant="outline" disabled>
        <Spinner data-icon="inline-start" />
        Generating
      </Button>
      <Button variant="secondary" disabled>
        Downloading
        <Spinner data-icon="inline-end" />
      </Button>
    </div>
  )
}

Button Group

To create a button group, use the ButtonGroup component. See the Button Group documentation for more details.

Loading…
"use client"

import * as React from "react"
import {
  ArchiveIcon,
  ArrowLeftIcon,
  CalendarPlusIcon,
  ClockIcon,
  ListFilterIcon,
  MailCheckIcon,
  MoreHorizontalIcon,
  TagIcon,
  Trash2Icon,
} from "lucide-react"

import { Button } from "@/components/ui/button"
import { ButtonGroup } from "@/components/ui/button-group"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuSeparator,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

export default function ButtonGroupDemo() {
  const [label, setLabel] = React.useState("personal")

  return (
    <ButtonGroup>
      <ButtonGroup className="hidden sm:flex">
        <Button
          variant="outline"
          size="icon-sm"
          className="max-md:size-10"
          aria-label="Go Back"
        >
          <ArrowLeftIcon data-icon="inline-start" />
        </Button>
      </ButtonGroup>
      <ButtonGroup>
        <Button variant="outline">Archive</Button>
        <Button variant="outline">Report</Button>
      </ButtonGroup>
      <ButtonGroup>
        <Button variant="outline">Snooze</Button>
        <DropdownMenu>
          <DropdownMenuTrigger
            render={
              <Button
                variant="outline"
                size="icon-sm"
                className="max-md:size-10"
                aria-label="More Options"
              />
            }
          >
            <MoreHorizontalIcon data-icon="inline-start" />
          </DropdownMenuTrigger>
          <DropdownMenuContent align="end" className="w-40">
            <DropdownMenuGroup>
              <DropdownMenuItem>
                <MailCheckIcon />
                Mark as Read
              </DropdownMenuItem>
              <DropdownMenuItem>
                <ArchiveIcon />
                Archive
              </DropdownMenuItem>
            </DropdownMenuGroup>
            <DropdownMenuSeparator />
            <DropdownMenuGroup>
              <DropdownMenuItem>
                <ClockIcon />
                Snooze
              </DropdownMenuItem>
              <DropdownMenuItem>
                <CalendarPlusIcon />
                Add to Calendar
              </DropdownMenuItem>
              <DropdownMenuItem>
                <ListFilterIcon />
                Add to List
              </DropdownMenuItem>
              <DropdownMenuSub>
                <DropdownMenuSubTrigger>
                  <TagIcon />
                  Label As...
                </DropdownMenuSubTrigger>
                <DropdownMenuSubContent>
                  <DropdownMenuRadioGroup
                    value={label}
                    onValueChange={setLabel}
                  >
                    <DropdownMenuRadioItem value="personal">
                      Personal
                    </DropdownMenuRadioItem>
                    <DropdownMenuRadioItem value="work">
                      Work
                    </DropdownMenuRadioItem>
                    <DropdownMenuRadioItem value="other">
                      Other
                    </DropdownMenuRadioItem>
                  </DropdownMenuRadioGroup>
                </DropdownMenuSubContent>
              </DropdownMenuSub>
            </DropdownMenuGroup>
            <DropdownMenuSeparator />
            <DropdownMenuGroup>
              <DropdownMenuItem variant="destructive">
                <Trash2Icon />
                Trash
              </DropdownMenuItem>
            </DropdownMenuGroup>
          </DropdownMenuContent>
        </DropdownMenu>
      </ButtonGroup>
    </ButtonGroup>
  )
}

You can use the buttonVariants helper to make a link look like a button.

Do not use <Button render={<a />} nativeButton={false} /> for links. The Base UI Button component always applies role="button", which overrides the semantic link role on <a> elements. Use buttonVariants with a plain <a> tag instead.

Loading…
"use client"

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

export default function ButtonRender() {
  return (
    <a
      href="#"
      className={buttonVariants({ variant: "secondary", size: "sm" })}
    >
      Login
    </a>
  )
}

API Reference

Button

The Button component is a wrapper around the button element that adds a variety of styles and functionality.

PropTypeDefault
variantshadcn: "default" | "outline" | "secondary" | "ghost" | "destructive" | "link"; brand: the ten variants above; compatibility: "primary" | "outline-primary" | "outline-dark" | "dashed-primary" | "text-primary" | "text-secondary" | "upgrade" | "upgrade-inverted" | "ghost-dark" | "text-dark" | "link-dark" | "light"; legacy aliases: "dashed" | "secondary-blue" | "text""default"
size"default" | "xs" | "sm" | "lg" | "xl" | "icon" | "icon-xs" | "icon-sm" | "icon-lg""default"
outlinebooleanfalse
ghostbooleanfalse

The default is 14/20 with 13px horizontal and 6px vertical padding, a fixed 34px height, a 120px minimum width, and a 10px radius. Below 768px its fixed height becomes 40px. sm uses 14/20 with 23px × 4px padding; lg uses 16/24 with 31px × 9px padding.