mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
54 lines
1.6 KiB
TypeScript
54 lines
1.6 KiB
TypeScript
/**
|
|
* IconButton — the shared icon-only button primitive (arch 09 §4 + §5).
|
|
*
|
|
* A square button holding a single `<Icon>` (the controlled-vocabulary glyph
|
|
* primitive) and nothing else. Because there is no visible text, an accessible
|
|
* name is required: `label` becomes `aria-label` (APG button pattern) and, unless
|
|
* overridden via `title`, the hover tooltip.
|
|
*
|
|
* Always ghost — borderless toolbar utility; the transparent border holds the
|
|
* box size. Sizes are the control scale: `md` = `--control-height` (32px
|
|
* square, the default), `lg` = `--control-height-lg` (40px — beside a lg
|
|
* Button), `sm` = 24px (only nested inside another 32px control, e.g. a search
|
|
* field's clear button).
|
|
*
|
|
* Hover follows the same one-elevation-step rule as Button, via the same
|
|
* `--control-hover-fill` custom property; an open disclosure trigger
|
|
* (`aria-expanded="true"`) holds the `--layer-02` fill.
|
|
*/
|
|
|
|
import type { ComponentPropsWithRef } from 'react';
|
|
import styles from './IconButton.module.css';
|
|
|
|
export interface IconButtonProps extends ComponentPropsWithRef<'button'> {
|
|
/** Accessible name (required — there is no visible text). */
|
|
label: string;
|
|
size?: 'sm' | 'md' | 'lg';
|
|
}
|
|
|
|
export function IconButton({
|
|
label,
|
|
size = 'md',
|
|
className,
|
|
type = 'button',
|
|
title,
|
|
...rest
|
|
}: IconButtonProps) {
|
|
return (
|
|
<button
|
|
type={type}
|
|
aria-label={label}
|
|
title={title ?? label}
|
|
className={[
|
|
styles.iconButton,
|
|
size === 'sm' && styles.sm,
|
|
size === 'lg' && styles.lg,
|
|
className,
|
|
]
|
|
.filter(Boolean)
|
|
.join(' ')}
|
|
{...rest}
|
|
/>
|
|
);
|
|
}
|