Files
MealQuest/web/src/ui/IconButton.tsx
T

40 lines
1.3 KiB
TypeScript
Raw Normal View History

2026-10-03 16:34:59 -04:00
import type { ComponentPropsWithRef } from 'react'
import styles from './IconButton.module.css'
export type IconButtonSize = 40 | 62
export type IconButtonTone = 'surface' | 'neutral' | 'accent' | 'teal'
2026-10-03 16:34:59 -04:00
export type IconButtonProps = Omit<ComponentPropsWithRef<'button'>, 'aria-label'> & {
// Accessible name; an icon alone never names a button.
label: string
size?: IconButtonSize
tone?: IconButtonTone
// Shows the label as an 11 px caption under the circle, which then names
// the button.
showLabel?: boolean
2026-10-03 16:34:59 -04:00
}
// Round icon button, named by its label and showing it only with showLabel.
// The 40 px size keeps a 44 px hit box (see the CSS); the 62 px size is the
// round action button with --shadow-md.
2026-10-03 16:34:59 -04:00
export function IconButton({
label,
size = 40,
tone = 'surface',
showLabel = false,
2026-10-03 16:34:59 -04:00
type = 'button',
className,
children,
...rest
}: IconButtonProps) {
const classes = [styles.button, styles[`size${size}`], showLabel && styles.labelled, className].filter(Boolean).join(' ')
2026-10-03 16:34:59 -04:00
return (
<button type={type} aria-label={showLabel ? undefined : label} className={classes} {...rest}>
2026-10-03 16:34:59 -04:00
<span className={[styles.face, styles[tone]].join(' ')} aria-hidden="true">
{children}
</span>
{showLabel && label}
2026-10-03 16:34:59 -04:00
</button>
)
}