Button

Your user's gateway to interactive functionalities.

Installation

Copy-Paste the component code in a Button.tsx file.

// Button.tsx

import React from "react";
import { cloneElement } from "@lib/utils";
import { button, buttonIcon as icon, type ButtonProps as ButtonVariantsProps, type ButtonIconProps } from "@tailus/themer"

export type Root = typeof Root;
export type Icon = typeof Icon;
export type Label = typeof Label;

export interface ButtonProps extends React.HTMLAttributes<HTMLButtonElement | HTMLAnchorElement>, ButtonVariantsProps {
  disabled?: boolean;
  href?: string;
}

export interface IconProps extends React.HTMLAttributes<HTMLElement>, ButtonIconProps{}

export const Icon: React.FC<IconProps> = (({
  className,
  children,
  size = "md",
  type = "leading"
}) => {
  return (
    <>
      {
        cloneElement(children as React.ReactElement, icon({size, type, className}))
      }
    </>
  )
})

export const Label = React.forwardRef<HTMLElement, React.HTMLAttributes<HTMLElement>>(({
  className,
  children,
  ...props
}, forwardedRef) => {
  return (
    <span className={className} {...props} ref={forwardedRef}>{children}</span>
  )
})

export const Root = React.forwardRef<
  HTMLButtonElement & HTMLAnchorElement, ButtonProps
  >((
    {
      className,
      intent = "primary",
      variant = "solid",
      size = "md",
      disabled,
      href,
      children,
      ...props
    }, forwardedRef) => {

      const Component = href ? 'a' : 'button';
      const iconOnly = React.Children.toArray(children).some(child => 
          React.isValidElement(child) && child.type === Icon && child.props.type === 'only'
      );
      const buttonSize = iconOnly ? 'iconOnlyButtonSize' : 'size';

      return (
        <Component ref={forwardedRef} href={href} className={button[variant as keyof typeof button]({intent, [buttonSize]:size, className})} {...props} disabled={disabled}>
          {children}
        </Component>
      )
    });

Root.displayName = 'Root';
Icon.displayName = "Icon";
Label.displayName = "Label";

export default {
  Root: Root,
  Icon: Icon,
  Label: Label
}

Usage

Import the Button parts you need to use in your component and build your Button.

import Button from "@tailus-ui/Button";
const MyComponent = () => (
    <Button.Root>
        <Button.Label>Click me</Button.Label>
    </Button.Root>
)

Reference

Root

The parent container of the Button component

Prop
Type
Default
size
enum
md
variant
enum
solid
intent
enum
primary
href
string
-

Label

The text label of the Button component

Icon

The icon of the Button component

Prop
Type
Default
size
enum
md
type
enum
leading

Examples

Intents


primary

secondary

accent

info

danger

warning

success

gray

neutral

solid

outlined

soft

ghost

Sizes


With Icon

import Button from "@tailus-ui/Button";

const MyComponent = () => (
    <div className="flex gap-3 flex-wrap">
        <Button.Root variant="outlined" intent="gray">
            <Button.Icon size="sm" type="leading">
                <Sparkles />
            </Button.Icon>
            <Button.Label>Leading</Button.Label>
        </Button.Root>
        <Button.Root variant="outlined" intent="gray">
            <Button.Icon size="sm" type="only">
                <Sparkles />
            </Button.Icon>
        </Button.Root>
        <Button.Root variant="outlined" intent="gray">
            <Button.Label>Trailing</Button.Label>
            <Button.Icon size="sm" type="trailing">
                <Sparkles />
            </Button.Icon>
        </Button.Root>
    </div>
)

Loading

import Button from "@tailus-ui/Button";

const MyComponent = () => (
    <Button.Root className="pointer-events-none">
        <Button.Icon size="md" type="leading">
            <svg className="animate-spin" xmlns="http://www.w3.org/2000/svg" width="1.5em" height="1.5em" viewBox="0 0 50 50">
                <path fill="currentColor" d="M41.9 23.9c-.3-6.1-4-11.8-9.5-14.4c-6-2.7-13.3-1.6-18.3 2.6c-4.8 4-7 10.5-5.6 16.6c1.3 6 6 10.9 11.9 12.5c7.1 2 13.6-1.4 17.6-7.2c-3.6 4.8-9.1 8-15.2 6.9c-6.1-1.1-11.1-5.7-12.5-11.7c-1.5-6.4 1.5-13.1 7.2-16.4c5.9-3.4 14.2-2.1 18.1 3.7c1 1.4 1.7 3.1 2 4.8c.3 1.4.2 2.9.4 4.3c.2 1.3 1.3 3 2.8 2.1c1.3-.8 1.2-2.5 1.1-3.8c0-.4.1.7 0 0" />
            </svg>
        </Button.Icon>
        <Button.Label>Loading</Button.Label>
    </Button.Root>
)

Disabled

import Button from "@tailus-ui/Button";

const MyComponent = () => (
    <Button.Root disabled>
        <Button.Label>Click me</Button.Label>
    </Button.Root>
)