Canary UI Component Library

Welcome to Canary UI

A comprehensive React component library for building high-fidelity prototypes. All components match the Canary design system for consistent, professional interfaces.

Foundation Project

This is a template repository. Clone it to start building your prototype with pre-built Canary UI components.

Quick Start

tsx
import { CanaryButton, ButtonType } from "@canary-ui/components";

export default function MyComponent() {
  return (
    <CanaryButton type={ButtonType.PRIMARY}>
      Click me!
    </CanaryButton>
  );
}

New in v0.6.0

CanaryBadge

3 newUrgentPendingLarge badge
tsx
<CanaryBadge type={BadgeType.INFO} label="3 new" />
<CanaryBadge type={BadgeType.URGENT} label="Urgent" />
<CanaryBadge type={BadgeType.WARNING} label="Pending" />

CanaryDivider

Content above

Content below

OR

Content after labeled divider

Left
Right
tsx
<CanaryDivider />
<CanaryDivider text="OR" />
<CanaryDivider direction={DividerDirection.VERTICAL} />

CanaryNote

Your changes have been saved successfully.
This action will affect all properties in your group.
This guest has an outstanding balance of $245.00.
tsx
<CanaryNote color={NoteColor.OK}>Saved successfully.</CanaryNote>
<CanaryNote color={NoteColor.WARNING}>Affects all properties.</CanaryNote>
<CanaryNote color={NoteColor.DANGER}>Outstanding balance.</CanaryNote>

CanaryTimestamp

DateTime:
With timezone:
Relative:
Secondary:
tsx
<CanaryTimestamp date={new Date()} format={TimestampFormat.DATETIME} />
<CanaryTimestamp date={new Date()} format={TimestampFormat.RELATIVE_TIME} />
<CanaryTimestamp date={new Date()} color={TimestampColor.SECONDARY} />

CanaryFormLabel + CanaryValidationError

Email address is required

Phone number recommended for guest communication

tsx
<CanaryFormLabel label="Email Address" isRequired />
<CanaryValidationError error="Email address is required" />
<CanaryValidationError warning="Phone recommended" />

CanaryProfileImage

JD
TW
AB
KL
MN
tsx
<CanaryProfileImage size={ProfileImageSize.TINY} initials="JD" />
<CanaryProfileImage size={ProfileImageSize.SMALL} initials="TW" />
<CanaryProfileImage size={ProfileImageSize.LARGE} initials="MN" />

CanaryTooltip + CanaryTooltipIcon

Hover me (top)
This is a tooltip
Hover me (bottom)
Bottom tooltip
Guest ID
i
Unique identifier assigned at check-in
Warning
i
Action cannot be undone
tsx
<CanaryTooltip content="This is a tooltip" position={TooltipPosition.TOP}>
  <span>Hover me</span>
</CanaryTooltip>
<CanaryTooltipIcon content="Help text" color={TooltipIconColor.PRIMARY} />

CanaryExpand

Room Details
Guest Preferences
Late checkout requested. Extra pillows. Non-smoking room.
tsx
const [expanded, setExpanded] = useState(false);
<CanaryExpand
  isExpanded={expanded}
  onToggle={() => setExpanded(!expanded)}
  header={<span>Room Details</span>}
>
  <p>Room 301 - Deluxe King Suite</p>
</CanaryExpand>

CanaryCounter

Guests:
2
Nights:
1
tsx
const [count, setCount] = useState(1);
<CanaryCounter value={count} onChange={setCount} minValue={0} maxValue={10} />

CanaryProgressBar

Continuous (65%)

65%

Steps (2 of 4)

tsx
<CanaryProgressBar variant={ProgressBarVariant.CONTINUOUS} progress={65} showLabel />
<CanaryProgressBar variant={ProgressBarVariant.STEPS} totalSteps={4} currentStep={2} />

CanarySteps

Horizontal

Guest Info
2
Room Selection
3
Payment
4
Confirmation

Vertical

Check-in
Completed at 2:30 PM
2
ID Verification
In progress
3
Payment Auth
Pending
tsx
<CanarySteps
  steps={[
    { label: "Guest Info" },
    { label: "Room Selection" },
    { label: "Payment" },
    { label: "Confirmation" },
  ]}
  currentStep={1}
/>

CanaryOverflowMenu

Click the three dots
tsx
<CanaryOverflowMenu
  items={[
    { id: "edit", label: "Edit guest", onClick: () => {} },
    { id: "message", label: "Send message", onClick: () => {} },
    { id: "divider", label: "", isDivider: true },
    { id: "delete", label: "Remove guest", isDanger: true },
  ]}
/>

CanaryDialog

tsx
const [open, setOpen] = useState(false);
<CanaryButton onClick={() => setOpen(true)}>Open Dialog</CanaryButton>
<CanaryDialog
  isOpen={open}
  onClose={() => setOpen(false)}
  title="Confirm Check-out"
  stretch={DialogStretch.NORMAL}
  footer={<CanaryButton onClick={() => setOpen(false)}>Confirm</CanaryButton>}
>
  <p>Are you sure you want to check out Room 301?</p>
</CanaryDialog>

CanarySideSheet

tsx
const [open, setOpen] = useState(false);
<CanaryButton onClick={() => setOpen(true)}>Open Side Sheet</CanaryButton>
<CanarySideSheet
  isOpen={open}
  onClose={() => setOpen(false)}
  size="medium"
  header={<h3>Guest Details</h3>}
>
  <p>Side sheet content here</p>
</CanarySideSheet>

CanarySettingsCard

Property Information
Basic hotel details and contact information
Name: Hotel Canary
Address: 123 Main Street, New York, NY
Phone: +1 (555) 123-4567
tsx
const [state, setState] = useState(SettingsCardState.VIEW);
<CanarySettingsCard
  title="Property Information"
  subtitle="Basic hotel details"
  state={state}
  onStateChange={setState}
  onSave={() => alert('Saved!')}
>
  {state === SettingsCardState.VIEW
    ? <p>Hotel Canary - 123 Main St</p>
    : <CanaryInput label="Hotel Name" value="Hotel Canary" />}
</CanarySettingsCard>

CanaryAutocomplete

Select Country
tsx
const [value, setValue] = useState("");
<CanaryAutocomplete
  label="Select Country"
  value={value}
  onChange={setValue}
  options={[
    { value: "us", label: "United States" },
    { value: "ca", label: "Canada" },
    { value: "uk", label: "United Kingdom" },
  ]}
  placeholder="Type to search..."
/>

Canary UI Component Library - Built with Next.js, React, and Tailwind CSS

Clone this project to start building your prototype