Basics
Buttons
Action hierarchy, sizing, and states for common WAfM workflows.
Bookmark toggle
Keep the accessible label stable: pressed means bookmarked. Supports controlled or uncontrolled state; the application owns persistence.
Not bookmarked
Usage and props
Import shared components from @wafm/ui. Keep workflow state and data fetching in the application.
Button
import { Button } from '@wafm/ui/components/button'
<Button variant="outline" size="sm" disabled={isSaving}>Save changes</Button>- variant
- default (primary), secondary, outline, ghost, destructive, or link.
- size
- default, xs, sm, lg, icon, icon-xs, icon-sm, or icon-lg.
- disabled
- Prevents activation. Explain the reason when it is not obvious.
- render / nativeButton
- Use render={<Link to="/" />} and nativeButton={false} role="link" for a router link. Icon-only buttons require an accessible label.
IconButton
import { PencilSimpleIcon } from '@phosphor-icons/react'
import { IconButton } from '@wafm/ui/components/icon-button'
<IconButton aria-label="Edit profile" variant="outline" onClick={onEdit}><PencilSimpleIcon aria-hidden="true" /></IconButton>- aria-label
- Required action name for assistive technology; label the action, not the icon.
- size / variant
- Uses Button variants and icon, icon-xs, icon-sm, or icon-lg sizes. Defaults to type="button".
Link
import { Link } from '@wafm/ui/components/link'
<Link href="/help">Read the guide</Link>- href / render
- Use a real href for navigation. render composes with your router link.
- target
- For a new tab, add rel="noreferrer" and announce that it opens a new tab. Links have no disabled state; omit unavailable destinations.
ButtonGroup
import { ButtonGroup } from '@wafm/ui/components/button-group'
import { Button } from '@wafm/ui/components/button'
<ButtonGroup aria-label="Editing actions">
<Button variant="outline">Edit</Button>
<Button variant="outline">Duplicate</Button>
</ButtonGroup>- aria-label
- Required description of the related actions. Each control remains in the normal Tab order.
- orientation
- horizontal (default) or vertical. This groups actions; it does not manage selection.
BookmarkToggle
import { BookmarkToggle } from '@wafm/ui/components/bookmark-toggle'
<BookmarkToggle aria-label="Bookmark participant" pressed={bookmarked} onPressedChange={setBookmarked} />- pressed / onPressedChange
- Controlled toggle state. Use defaultPressed for uncontrolled state. Persistence belongs to the application.
- aria-label / disabled
- Keep the required label stable; aria-pressed announces selection. disabled prevents pointer and keyboard activation.
DropdownMenu
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from '@wafm/ui/components/dropdown-menu'
import { Button } from '@wafm/ui/components/button'
<DropdownMenu>
<DropdownMenuTrigger render={<Button />}>Actions</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem onClick={onRename}>Rename</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>- open / onOpenChange
- Optional controlled open state on the root. The menu supports keyboard navigation, typeahead, Escape, outside dismissal, and focus restoration.
- DropdownMenuItem
- onClick supports pointer and keyboard activation. disabled prevents activation while keeping the action discoverable with arrow keys. variant="destructive" marks a destructive action; the app owns confirmation.
- DropdownMenuCheckboxItem
- Use checked and onCheckedChange for a menu preference; the menu closes after selection by default.
- DropdownMenuContent
- Portalled content supports align and side, with viewport collision handling. Group, Label, and Separator organise related actions.
AccountMenu
import { AccountMenu } from '@wafm/ui/components/account-menu'
<AccountMenu name="Eva Muster" initials="EM" triggerLabel="Open account menu" profileLabel="Profile and settings" signOutLabel="Sign out" onProfileSelect={onProfile} onSignOutSelect={onSignOut} />- name / email / initials / imageSrc
- Identity display with optional email and image. Initials are the fallback for a missing or failed image.
- triggerLabel / profileLabel / signOutLabel
- Caller-provided labels support the application language.
- onProfileSelect / onSignOutSelect
- Required callbacks. Routing and authentication remain in the application; this component performs neither itself.