Button
buttonuiInteractive button component with variants (default, destructive, outline, secondary, ghost, link), sizes, and composite ButtonGroup container for segmented toolbars and split buttons.
Installation
$pnpm dlx shadcn-svelte@latest add https://uipkge.dev/r/svelte/button.json$npx shadcn-svelte@latest add https://uipkge.dev/r/svelte/button.json$yarn dlx shadcn-svelte@latest add https://uipkge.dev/r/svelte/button.json$bunx shadcn-svelte@latest add https://uipkge.dev/r/svelte/button.jsonManifest:
https://uipkge.dev/r/svelte/button.jsonInstalls to:src/lib/components/ui/button/Examples
Loading interactive previews…
Loading interactive previews…
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
variant | 'default''destructive''outline''secondary''ghost''link' | default | optional |
size | 'default''sm''lg''xs''icon''icon-sm''icon-lg' | default | optional |
childemitting a <button> — the Svelte counterpart of React's `asChild`. Use it to give an <a> full button styling: spread `props` onto your element. | Snippet<[{ props: Record<string, unknown> }]> | — | optional |
ref`bind:this` on your own element there. | HTMLButtonElement | null | — | optional |
npm dependencies
Used by
alert-modalauth-mfaauth-password-resetauth-sign-inauth-sign-upbento-01calendarcareers-job-boardcarouselcart-drawerchangelogchat-threadchat-two-panecheckout-flowcode-blockcoming-sooncontact-01contact-uscookie-consent-barcta-01cta-final-bandcustomer-reviewsdashboard-layoutdata-tabledata-table-pagedate-pickerdialogempty-statesenterprise-workspace-switchererror-404error-500error-pagesevent-calendarevent-calendar-dashboardfaq-01faq-two-columnfeature-alternating-rowsfeatures-01flash-sale-offer-barfooterfooter-01form-layoutsfrequently-bought-togetherganttheaderheader-01header-mega-menuherohero-01inboxkanban-task-boardlogin-01login-02logo-cloud-grayscalelogosnewsletternotification-centernotifications-popoveronboarding-checklistonboarding-wizardorder-confirmationorder-management-pagepayment-formpricingpricing-01pricing-matrix-plan-cardsproduct-detail-pageproduct-grid-pageproduct-qa-communityproject-roadmaprange-calendarrecord-detail-pageregister-01rich-text-editorsearch-results-pagesettings-pageshortcuts-dialogsidebarstats-bandteam-sectiontechnical-specs-sheettestimonialtheme-customizetime-pickertourtransferuser-profile-pagewaitlist
Files installed (4)
src/lib/components/ui/button/Button.svelte1.6 kB
<script lang="ts" module> import type { Snippet } from 'svelte' import type { HTMLButtonAttributes } from 'svelte/elements' import type { ButtonVariants } from './button.variants' export interface ButtonProps extends HTMLButtonAttributes { variant?: ButtonVariants['variant'] size?: ButtonVariants['size'] /** Render your own element with the button's props and styles instead of * emitting a <button> — the Svelte counterpart of React's `asChild`. Use it * to give an <a> full button styling: spread `props` onto your element. */ child?: Snippet<[{ props: Record<string, unknown> }]> /** The rendered <button>, via `bind:ref`. Stays null in `child` mode — use * `bind:this` on your own element there. */ ref?: HTMLButtonElement | null } </script> <script lang="ts"> import { cn } from '$lib/utils' import { buttonVariants } from './button.variants' let { class: className, variant = 'default', size = 'default', type = 'button', child, children, ref = $bindable(null), ...restProps }: ButtonProps = $props() const mergedProps = $derived({ 'data-uipkge': '', 'data-slot': 'button', 'data-variant': variant, 'data-size': size, class: cn(buttonVariants({ variant, size }), className), ...restProps, }) </script> {#if child} <!-- Default type="button" avoids accidental form submits. `child` leaves type to the consumer's element (e.g. <a>) and must not receive it. --> {@render child({ props: mergedProps })} {:else} <button bind:this={ref} {type} {...mergedProps}>{@render children?.()}</button> {/if}src/lib/components/ui/button/ButtonGroup.svelte3.4 kB
<script lang="ts" module> import type { HTMLAttributes } from 'svelte/elements' export interface ButtonGroupProps extends HTMLAttributes<HTMLDivElement> { orientation?: 'horizontal' | 'vertical' attached?: boolean ref?: HTMLDivElement | null } </script> <script lang="ts"> import { cn } from '$lib/utils' let { class: className, orientation = 'horizontal', attached = true, children, ref = $bindable(null), ...restProps }: ButtonGroupProps = $props() </script> <div bind:this={ref} role="group" data-uipkge="" data-slot="button-group" data-orientation={orientation} data-attached={attached ? '' : undefined} class={cn( 'inline-flex items-center', orientation === 'vertical' ? 'flex-col items-stretch' : 'flex-row', attached && [ '[&>[data-slot=button]]:relative [&>[data-slot=button]:focus-visible]:z-20 [&>[data-slot=button]:hover]:z-10', '[&>button]:relative [&>button:focus-visible]:z-20 [&>button:hover]:z-10', orientation === 'horizontal' && [ '[&>[data-slot=button]]:rounded-none [&>button]:rounded-none', '[&>[data-slot=button]:first-child]:rounded-l-md [&>button:first-child]:rounded-l-md', '[&>[data-slot=button]:last-child]:rounded-r-md [&>button:last-child]:rounded-r-md', '[&>[data-slot=button]:only-child]:rounded-md [&>button:only-child]:rounded-md', '[&>[data-slot=button]:not(:first-child)]:-ml-px [&>button:not(:first-child)]:-ml-px', '[&>[data-slot=button][data-variant=default]:not(:first-child)]:border-primary-foreground/20 [&>[data-slot=button][data-variant=default]:not(:first-child)]:border-l', '[&>[data-slot=button]:not([data-variant]):not(:first-child)]:border-primary-foreground/20 [&>[data-slot=button]:not([data-variant]):not(:first-child)]:border-l', '[&>[data-slot=button][data-variant=secondary]:not(:first-child)]:border-border [&>[data-slot=button][data-variant=secondary]:not(:first-child)]:border-l', '[&>[data-slot=button][data-variant=destructive]:not(:first-child)]:border-l [&>[data-slot=button][data-variant=destructive]:not(:first-child)]:border-white/20', ], orientation === 'vertical' && [ '[&>[data-slot=button]]:rounded-none [&>button]:rounded-none', '[&>[data-slot=button]:first-child]:rounded-t-md [&>button:first-child]:rounded-t-md', '[&>[data-slot=button]:last-child]:rounded-b-md [&>button:last-child]:rounded-b-md', '[&>[data-slot=button]:only-child]:rounded-md [&>button:only-child]:rounded-md', '[&>[data-slot=button]:not(:first-child)]:-mt-px [&>button:not(:first-child)]:-mt-px', '[&>[data-slot=button][data-variant=default]:not(:first-child)]:border-primary-foreground/20 [&>[data-slot=button][data-variant=default]:not(:first-child)]:border-t', '[&>[data-slot=button]:not([data-variant]):not(:first-child)]:border-primary-foreground/20 [&>[data-slot=button]:not([data-variant]):not(:first-child)]:border-t', '[&>[data-slot=button][data-variant=secondary]:not(:first-child)]:border-border [&>[data-slot=button][data-variant=secondary]:not(:first-child)]:border-t', '[&>[data-slot=button][data-variant=destructive]:not(:first-child)]:border-t [&>[data-slot=button][data-variant=destructive]:not(:first-child)]:border-white/20', ], ], !attached && (orientation === 'vertical' ? 'gap-1' : 'gap-1.5'), className, )} {...restProps} > {@render children?.()} </div>src/lib/components/ui/button/button.variants.ts2.7 kB
import type { VariantProps } from 'class-variance-authority' import { cva } from 'class-variance-authority' /** * Variant definitions live in their own file (rather than the package * `index.ts`) so `Button.vue` can `import { buttonVariants } from * './button.variants'` without creating a circular dependency through the * index. See card.variants.ts for the same pattern + the SSR symptom that * motivated the split. */ export const buttonVariants = cva( "inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,background-color,border-color,box-shadow,opacity,transform,scale] duration-150 active:scale-[0.97] active:duration-100 touch-manipulation disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", { variants: { variant: { default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90', destructive: 'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', outline: 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50', secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80', ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50', link: 'text-primary underline-offset-4 hover:underline', }, size: { default: 'h-9 px-4 py-2 has-[>svg]:px-3', sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5', lg: 'h-10 rounded-md px-6 has-[>svg]:px-4', xs: 'h-7 rounded-md gap-1 px-2 has-[>svg]:px-1.5 text-xs', icon: 'size-9', 'icon-sm': 'size-8', 'icon-lg': 'size-10', // Square counterpart of `xs` (h-7). Dense data toolbars run at 28px and // had no icon-only tier to match, so icon controls sat 4px taller than // the buttons beside them. Icons inside carry their own size class. 'icon-xs': 'size-7', // Row-action tier: the hover-revealed copy control inside a two-line // table cell. Anything taller than the 20px text line it sits on makes // the row grow when the control appears. 'icon-2xs': 'size-5', }, }, defaultVariants: { variant: 'default', size: 'default', }, }, ) export type ButtonVariants = VariantProps<typeof buttonVariants>src/lib/components/ui/button/index.ts0.2 kB
export { default as Button, type ButtonProps } from './Button.svelte' export { default as ButtonGroup, type ButtonGroupProps } from './ButtonGroup.svelte' export { buttonVariants, type ButtonVariants } from './button.variants'
Raw manifest:https://uipkge.dev/r/svelte/button.json