MC Button
A clickable interactive element.
Overview
The mc-button is a core component of the MicroClub UI library. Built on the @base-ui/react button primitive, it combines world-class accessibility with MicroClub's signature styling via Tailwind CSS v4.
As a part of the MicroClub library, this component is designed for instant deployment into your codebase, giving you full ownership of its look and behavior.
Preview
import { McButton } from '../ui/mc-button';export default function McButtonDemo() { return <McButton>Button</McButton>;}Add to Your Project
Deploy the mc-button directly to your components/ui directory:
Manual Setup
If you prefer manual control:
npm install clsx tailwind-merge @base-ui/react class-variance-authority lucide-react
pnpm add clsx tailwind-merge @base-ui/react class-variance-authority lucide-react
yarn add clsx tailwind-merge @base-ui/react class-variance-authority lucide-react
bun add clsx tailwind-merge @base-ui/react class-variance-authority lucide-react
'use client';import * as React from 'react';import { Button as ButtonPrimitive } from '@base-ui/react/button';import { cva, type VariantProps } from 'class-variance-authority';import { LinkIcon, Loader2 } from 'lucide-react';import { cn } from '@/lib/utils';const buttonVariants = cva( 'group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding whitespace-nowrap transition-all outline-none select-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0', { variants: { variant: { primary: 'bg-primary text-primary-foreground hover:bg-secondary-foreground hover:text-secondary active:bg-secondary-foreground active:text-secondary active:ring-4 active:ring-secondary disabled:bg-muted disabled:text-muted-foreground', secondary: 'bg-secondary text-secondary-foreground hover:bg-accent hover:text-accent-foreground active:bg-accent active:text-accent-foreground active:ring-4 active:ring-secondary disabled:bg-muted disabled:text-muted-foreground', tertiary: 'bg-primary-foreground text-primary hover:text-accent-foreground active:text-accent-foreground active:ring-4 active:ring-secondary disabled:bg-muted disabled:text-muted-foreground', link: 'bg-transparent p-0 text-foreground underline hover:text-accent-foreground active:text-accent-foreground disabled:text-muted-foreground', }, size: { sm: 'px-3.5 py-2 text-sm font-medium', md: 'px-4 py-2.5 text-sm font-medium', lg: 'px-[1.125rem] py-2.5 text-base font-medium', xl: 'px-5 py-3 text-base font-medium', }, icon: { none: '', leading: 'gap-2', trailing: 'gap-2', dot: 'gap-2', only: '', }, destructive: { true: '', false: '', }, }, compoundVariants: [ { variant: 'link', className: 'px-0 py-0', }, { icon: 'only', size: 'sm', className: 'p-2', }, { icon: 'only', size: 'md', className: 'p-2.5', }, { icon: 'only', size: 'lg', className: 'p-3', }, { icon: 'only', size: 'xl', className: 'p-3.5', }, // Destructive overrides { destructive: true, variant: 'primary', className: 'bg-destructive text-destructive-foreground hover:bg-destructive/90 active:bg-destructive/90 active:ring-4 active:ring-destructive/20', }, { destructive: true, variant: 'secondary', className: 'bg-destructive/10 text-destructive hover:bg-destructive/20 active:ring-4 active:ring-destructive/20', }, { destructive: true, variant: 'tertiary', className: 'text-destructive hover:bg-destructive/10 active:ring-4 active:ring-destructive/20', }, { destructive: true, variant: 'link', className: 'text-destructive hover:text-destructive/80 active:text-destructive/80', }, ], defaultVariants: { variant: 'primary', size: 'md', icon: 'none', destructive: false, }, });export interface McButtonProps extends Omit<ButtonPrimitive.Props, 'icon'>, Omit<VariantProps<typeof buttonVariants>, 'icon'> { iconDefinition?: React.ReactNode; icon?: 'none' | 'leading' | 'trailing' | 'dot' | 'only'; isLoading?: boolean;}function McButton({ className, variant = 'primary', size, icon = 'none', destructive, iconDefinition, isLoading, children, disabled, ...props}: McButtonProps) { const isLink = variant === 'link'; const effectiveIcon = isLink && (icon === 'leading' || icon === 'trailing') && !iconDefinition ? ( <LinkIcon /> ) : ( iconDefinition ); return ( <ButtonPrimitive data-slot="button" disabled={disabled || isLoading} className={cn( buttonVariants({ variant, size, icon: icon === 'none' ? 'none' : icon, destructive, className, }), isLoading && 'gap-2' )} {...props} > {isLoading ? ( <Loader2 className="size-4 animate-spin shrink-0" /> ) : ( <> {icon === 'dot' && ( <span data-slot="dot" className="size-2.5 shrink-0 rounded-full bg-current" /> )} {icon === 'leading' && effectiveIcon && ( <span data-slot="leading-icon" className="size-4 shrink-0 [&_svg]:size-full"> {effectiveIcon} </span> )} </> )} {icon !== 'only' && children} {icon === 'only' && !isLoading && effectiveIcon} {icon === 'trailing' && !isLoading && effectiveIcon && ( <span data-slot="trailing-icon" className="size-4 shrink-0 [&_svg]:size-full"> {effectiveIcon} </span> )} </ButtonPrimitive> );}export { McButton, buttonVariants };Ensure your @/lib/utils or equivalent classname helper is correctly imported.
Usage
import { McButton } from '@/components/ui/mc-button';
export default function Example() {
return <McButton variant="primary">Click me</McButton>;
}Examples
Variants
<McButton variant="primary">Primary</McButton>
<McButton variant="secondary">Secondary</McButton>
<McButton variant="tertiary">Tertiary</McButton>
<McButton variant="link">Link</McButton>Sizes
<McButton size="sm">Small</McButton>
<McButton size="md">Medium</McButton>
<McButton size="lg">Large</McButton>
<McButton size="xl">Extra Large</McButton>Icons Support
The mc-button features a unified icon system using the icon and iconDefinition props.
import { Mail, ArrowRight, Plus } from "lucide-react"
<McButton icon="leading" iconDefinition={<Mail />}>
Send Email
</McButton>
<McButton icon="trailing" iconDefinition={<ArrowRight />}>
Get Started
</McButton>
<McButton icon="dot">
Status Online
</McButton>
<McButton icon="only" iconDefinition={<Plus />} />States
Handle loading and destructive actions with dedicated boolean props.
<McButton isLoading>Processing...</McButton>
<McButton destructive variant="primary">
Delete Account
</McButton>Link Variant
The link variant automatically includes a LinkIcon when an icon mode is selected but no iconDefinition is provided.
<McButton variant="link" icon="leading">
View Documentation
</McButton>API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "primary" | "secondary" | "tertiary" | "link" | "primary" | The visual style of the button |
size | "sm" | "md" | "lg" | "xl" | "md" | The size of the button |
icon | "none" | "leading" | "trailing" | "dot" | "only" | "none" | The placement mode for icons |
iconDefinition | ReactNode | undefined | The icon to render (except for "dot" mode) |
destructive | boolean | false | Applies a destructive theme to the current variant |
isLoading | boolean | false | Shows a spinner and disables interactions |
render | ReactElement | ((props: HTMLProps) => ReactElement) | undefined | Polymorphic render prop from Base UI |
className | string | undefined | Additional CSS classes |
All other props from @base-ui/react/button are supported.