V1.0

Base Components

Compact Button

Buttons that are ideal for when space is limited.

Installation

Install the following dependencies:

terminal
npm install @radix-ui/react-slot

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

/components/ui/compact-button.tsx
// AlignUI CompactButton v0.0.0
 
import * 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 COMPACT_BUTTON_ROOT_NAME = 'CompactButtonRoot';
const COMPACT_BUTTON_ICON_NAME = 'CompactButtonIcon';
 
export const compactButtonVariants = tv({
  slots: {
    root: [
      // base
      'relative flex shrink-0 items-center justify-center outline-none',
      'transition duration-200 ease-out',
      // disabled
      'disabled:pointer-events-none disabled:border-transparent disabled:bg-transparent disabled:text-text-disabled-300 disabled:shadow-none',
      // focus
      'focus:outline-none',
    ],
    icon: '',
  },
  variants: {
    variant: {
      stroke: {
        root: [
          // base
          'border border-stroke-soft-200 bg-bg-white-0 text-text-sub-600 shadow-regular-xs',
          // hover
          'hover:border-transparent hover:bg-bg-weak-50 hover:text-text-strong-950 hover:shadow-none',
          // focus
          'focus-visible:border-transparent focus-visible:bg-bg-strong-950 focus-visible:text-text-white-0 focus-visible:shadow-none',
        ],
      },
      ghost: {
        root: [
          // base
          'bg-transparent text-text-sub-600',
          // hover
          'hover:bg-bg-weak-50 hover:text-text-strong-950',
          // focus
          'focus-visible:bg-bg-strong-950 focus-visible:text-text-white-0',
        ],
      },
      white: {
        root: [
          // base
          'bg-bg-white-0 text-text-sub-600 shadow-regular-xs',
          // hover
          'hover:bg-bg-weak-50 hover:text-text-strong-950',
          // focus
          'focus-visible:bg-bg-strong-950 focus-visible:text-text-white-0',
        ],
      },
      modifiable: {},
    },
    size: {
      large: {
        root: 'size-6',
        icon: 'size-5',
      },
      medium: {
        root: 'size-5',
        icon: 'size-[18px]',
      },
    },
    fullRadius: {
      true: {
        root: 'rounded-full',
      },
      false: {
        root: 'rounded-md',
      },
    },
  },
  defaultVariants: {
    variant: 'stroke',
    size: 'large',
    fullRadius: false,
  },
});
 
type CompactButtonSharedProps = Omit<
  VariantProps<typeof compactButtonVariants>,
  'fullRadius'
>;
 
type CompactButtonProps = VariantProps<typeof compactButtonVariants> &
  React.ButtonHTMLAttributes<HTMLButtonElement> & {
    asChild?: boolean;
  };
 
const CompactButtonRoot = React.forwardRef<
  HTMLButtonElement,
  CompactButtonProps
>(
  (
    { asChild, variant, size, fullRadius, children, className, ...rest },
    forwardedRef,
  ) => {
    const uniqueId = React.useId();
    const Component = asChild ? Slot : 'button';
    const { root } = compactButtonVariants({ variant, size, fullRadius });
 
    const sharedProps: CompactButtonSharedProps = {
      variant,
      size,
    };
 
    const extendedChildren = recursiveCloneChildren(
      children as React.ReactElement[],
      sharedProps,
      [COMPACT_BUTTON_ICON_NAME],
      uniqueId,
      asChild,
    );
 
    return (
      <Component
        ref={forwardedRef}
        className={root({ class: className })}
        {...rest}
      >
        {extendedChildren}
      </Component>
    );
  },
);
CompactButtonRoot.displayName = COMPACT_BUTTON_ROOT_NAME;
 
function CompactButtonIcon<T extends React.ElementType>({
  variant,
  size,
  as,
  className,
  ...rest
}: PolymorphicComponentProps<T, CompactButtonSharedProps>) {
  const Component = as || 'div';
  const { icon } = compactButtonVariants({ variant, size });
 
  return <Component className={icon({ class: className })} {...rest} />;
}
CompactButtonIcon.displayName = COMPACT_BUTTON_ICON_NAME;
 
export { CompactButtonRoot as Root, CompactButtonIcon as Icon };

Update the import paths to match your project setup.

Examples

Stroke (Default)

Ghost

White

Modifiable

Text color inherits from the parent by default. No default color, background or hover colors are applied, allowing for full customization.

Here's an example with custom colors specified:

Size

Full Radius

Disabled

asChild

Composition

You can simplify component usage by creating a custom API that abstracts the core functionalities.

An example with a required icon prop:

API Reference

CompactButton.Root

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

PropTypeDefault
variant
"stroke"|"ghost"|"white"|"modifiable"
"stroke"
size
"large"|"medium"
"large"
fullRadius
boolean
false
asChild
boolean

CompactButton.Icon

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

PropTypeDefault
as
React.ElementType
div
© 2024 AlignUI Design System. All rights reserved.