CoreUI is in beta - APIs and components may change.
CoreUI
Base Components

Fancy Button

Regular buttons with a fancy appearance.


Installation

Install the following dependencies:

npm install @radix-ui/react-slot

Create a fancy-button.tsx file and paste the following code into it.

components/ui/fancy-button.tsx
// CoreUI FancyButton v0.0.0import * as React from 'react';import { Slot } from '@radix-ui/react-slot';import { PolymorphicComponentProps } from '@/utils/polymorphic';import { recursiveCloneChildren } from '@/utils/recursive-clone-children';import { tv, type VariantProps } from '@/utils/tv';const FANCY_BUTTON_ROOT_NAME = 'FancyButtonRoot';const FANCY_BUTTON_ICON_NAME = 'FancyButtonIcon';export const fancyButtonVariants = tv({  slots: {    root: [      // base      'group text-label-sm relative inline-flex items-center justify-center whitespace-nowrap outline-none',      'transition duration-200 ease-out',      // focus      'focus:outline-none',      // disabled      'disabled:text-text-disabled-300 disabled:pointer-events-none',      'disabled:bg-bg-weak-50 disabled:bg-none disabled:shadow-none disabled:before:hidden disabled:after:hidden',    ],    icon: 'relative z-10 size-5 shrink-0',  },  variants: {    variant: {      neutral: {        root: 'bg-bg-strong-950 text-text-white-0 shadow-fancy-buttons-neutral',      },      primary: {        root: 'bg-primary-base text-static-white shadow-fancy-buttons-primary',      },      destructive: {        root: 'bg-error-base text-static-white shadow-fancy-buttons-error',      },      basic: {        root: [          // base          'bg-bg-white-0 text-text-sub-600 shadow-fancy-buttons-stroke',          // hover          'hover:bg-bg-weak-50 hover:text-text-strong-950 hover:shadow-none',        ],      },    },    size: {      medium: {        root: 'rounded-10 h-10 gap-3 px-3.5',        icon: '-mx-1',      },      small: {        root: 'h-9 gap-3 rounded-lg px-3',        icon: '-mx-1',      },      xsmall: {        root: 'h-8 gap-3 rounded-lg px-2.5',        icon: '-mx-1',      },    },  },  compoundVariants: [    {      variant: ['neutral', 'primary', 'destructive'],      class: {        root: [          // before          'before:pointer-events-none before:absolute before:inset-0 before:z-10 before:rounded-[inherit]',          'before:bg-gradient-to-b before:p-px',          'before:from-static-white/[.12] before:to-transparent',          // before mask          'before:[mask-image:linear-gradient(#fff_0_0),linear-gradient(#fff_0_0)] before:[mask-composite:exclude] before:[mask-clip:content-box,border-box]',          // after          'after:from-static-white after:absolute after:inset-0 after:rounded-[inherit] after:bg-gradient-to-b after:to-transparent',          'after:pointer-events-none after:opacity-[.16] after:transition after:duration-200 after:ease-out',          // hover          'hover:after:opacity-[.24]',        ],      },    },  ],  defaultVariants: {    variant: 'neutral',    size: 'medium',  },});type FancyButtonSharedProps = VariantProps<typeof fancyButtonVariants>;type FancyButtonProps = VariantProps<typeof fancyButtonVariants> &  React.ButtonHTMLAttributes<HTMLButtonElement> & {    asChild?: boolean;  };const FancyButtonRoot = React.forwardRef<HTMLButtonElement, FancyButtonProps>(  ({ asChild, children, variant, size, className, ...rest }, forwardedRef) => {    const uniqueId = React.useId();    const Component = asChild ? Slot : 'button';    const { root } = fancyButtonVariants({ variant, size });    const sharedProps: FancyButtonSharedProps = {      variant,      size,    };    const extendedChildren = recursiveCloneChildren(      children as React.ReactElement[],      sharedProps,      [FANCY_BUTTON_ICON_NAME],      uniqueId,      asChild,    );    return (      <Component        ref={forwardedRef}        className={root({ class: className })}        {...rest}      >        {extendedChildren}      </Component>    );  },);FancyButtonRoot.displayName = FANCY_BUTTON_ROOT_NAME;function FancyButtonIcon<T extends React.ElementType>({  className,  variant,  size,  as,  ...rest}: PolymorphicComponentProps<T, FancyButtonSharedProps>) {  const Component = as || 'div';  const { icon } = fancyButtonVariants({ variant, size });  return <Component className={icon({ class: className })} {...rest} />;}FancyButtonIcon.displayName = FANCY_BUTTON_ICON_NAME;export { FancyButtonRoot as Root, FancyButtonIcon as Icon };

Examples

Neutral (Default)

Primary

Destructive

Basic

With Icon

Size

Disabled

asChild

Composition

API Reference

FancyButton.Root

This component is based on the <button> element and supports all of its props. And adds:

PropTypeDefault
variant"neutral" | "primary" | "destructive" | "basic""neutral"
size"medium" | "small" | "xsmall""medium"
asChildboolean

FancyButton.Icon

The FancyButton.Icon component is polymorphic, allowing you to change the underlying HTML element using the as prop.

PropTypeDefault
asReact.ElementTypediv

On this page