Button
Source: packages/ui/src/components/button.tsx
Package: @brettjohnson/ui
Import
import { Button } from '@brettjohnson/ui';Variants
Three visual variants cover all primary use cases:
| Variant | Usage |
|---|---|
primary | Primary CTA — one per section. “Book a Speaking Engagement”, “Submit Inquiry”. |
secondary | Secondary action alongside a primary. “Download Media Kit”. |
ghost | Tertiary or navigation-adjacent actions. “Learn more”, “View all episodes”. |
<Button variant="primary">Book a Speaking Engagement</Button><Button variant="secondary">Download Media Kit</Button><Button variant="ghost">Learn more</Button>Primary
bg-brand-red text-white hover:bg-red-700Used once per visual section. The red background must not compete with other red elements in the same viewport zone.
Secondary
border border-white/20 bg-transparent text-white hover:bg-white/10Used when two actions of near-equal weight appear together (e.g., hero CTAs: “Book” + “Media Kit”).
Ghost
text-white/70 hover:text-white hover:bg-white/5Used for inline or navigation-adjacent actions where a border or filled background would add visual noise.
Sizes
| Size | Height | Padding | Font |
|---|---|---|---|
sm | 36px (h-9) | px-4 | text-sm |
md | 44px (h-11) | px-6 | text-base |
lg | 56px (h-14) | px-8 | text-lg |
<Button size="sm">Small</Button><Button size="md">Medium (default)</Button><Button size="lg">Large</Button>Default size is md. Use lg only in hero sections. Use sm in dense UI (table actions, inline forms).
Props
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> { variant?: 'primary' | 'secondary' | 'ghost'; size?: 'sm' | 'md' | 'lg'; className?: string;}The component extends all native <button> HTML attributes — onClick, disabled, type, aria-*, etc.
States
Disabled
<Button disabled>Unavailable</Button>Disabled buttons receive opacity-50 and pointer-events-none — no separate disabled styling is required.
Loading
The Button component does not currently include a built-in loading state. Wrap with a loading indicator at the call site:
<Button disabled={isSubmitting} variant="primary"> {isSubmitting ? 'Submitting…' : 'Submit Inquiry'}</Button>Focus
All buttons use focus-visible:ring-2 focus-visible:ring-offset-2. Primary buttons ring in brand-red; secondary and ghost ring in white.
Usage Rules
- One primary button per section. Multiple red buttons in the same view dilutes urgency.
- Button labels are verbs. “Book”, “Download”, “Submit”, “Inquire” — not “Click Here” or “Learn More About Speaking”.
- Never use
<a>styled as a button for navigating between pages. Use Next.js<Link>for internal navigation and wrap it in the ghost variant if it visually appears as a button.
Full Example
import { Button } from '@brettjohnson/ui';
export function HeroCTA() { return ( <div className="flex gap-4"> <Button variant="primary" size="lg"> Book a Speaking Engagement </Button> <Button variant="secondary" size="lg"> Download Media Kit </Button> </div> );}