Lote 0 of the component-coherence audit (FASE 2): - New IconButton primitive (composes Button: inherits variant/size/intent/ color/rounded/shape/loading + Button's morfo + sema, no new morfo; aria-label required; glyph as children). New svg/ glyphs: SvgX, SvgPlus, SvgMinus. - Button: rounded='full' 9999px -> var(--radius-full) (theme-driven radius, value-identical). - Migrate icon-only Button consumers to IconButton: carousel prev/next (pilot), tree-grid expand, table row-detail, search-field clear, combobox clear. Pattern: glyph->children, label->aria-label, drop iconOnly. - carousel.css: fix arrow positioning (specificity vs Button's base position:relative, exposed by the IconButton import-order flip) + the press-squeeze jump (centre via the translate property so the contact press transform:scale composes instead of replacing the centring). - Doc: COMPONENT_COHERENCE_AUDIT - indicator-chevron vs discrete IconButton distinction (tree-view/accordion stay out); 5F width closed (full-width); Lote 0 hand-off + pending select-family faults. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>active-uix
parent
e16c169f37
commit
189932d939
@ -0,0 +1,48 @@
|
||||
# IconButton
|
||||
|
||||
The canonical **icon-only button**. A first-class public primitive (as in
|
||||
MUI / Radix / Chakra), not an ad-hoc `<Button>` flag.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { IconButton } from '$uix/eidos/components/icon-button';
|
||||
import { SvgX } from '$uix/eidos/components/svg';
|
||||
</script>
|
||||
|
||||
<IconButton aria-label="Close" variant="ghost" rounded="full">
|
||||
<SvgX />
|
||||
</IconButton>
|
||||
```
|
||||
|
||||
## What it is
|
||||
|
||||
A thin composition of `<Button>`. It **inherits Button's whole API** —
|
||||
`variant`, `size`, `intent`, `color`, `rounded`, `shape`, `block`,
|
||||
`loading`, the spinner — plus Button's **morfo + sema** (no new morfo). It
|
||||
adds only the two things icon-only needs:
|
||||
|
||||
- the **square layout** (Button's internal icon-only mode), and
|
||||
- a **required `aria-label`** — the visible glyph carries no text, so the
|
||||
accessible name must be explicit.
|
||||
|
||||
## Doctrine
|
||||
|
||||
- **Use `<IconButton>` for every icon-only button.** Authoring icon-only
|
||||
chrome by hand (a raw `<button>` with bespoke border/bg/hover/focus) is
|
||||
the composition violation this primitive exists to remove (see
|
||||
`COMPONENT_COHERENCE_AUDIT.md` dim. 1B). Compose it via soma's `child`
|
||||
snippet from a component trigger, exactly like the `<Button>` consumers.
|
||||
- **Glyph-agnostic.** Pass any `svg/` primitive (`SvgX`, `SvgChevron`,
|
||||
`SvgPlus`/`SvgMinus`, …) or a lucide `Icon` as children. Prefer `svg/`
|
||||
when the glyph animates or couples to state; lucide `icon/` for static
|
||||
content (see `svg/README.md`).
|
||||
- **`aria-label` is required.** Without it an icon-only control has no
|
||||
accessible name. The prop is required at the type level.
|
||||
|
||||
## Relationship to `<Button>`
|
||||
|
||||
`<Button>` owns the click flow, `data-state`, the `contact-activate`
|
||||
sema event and the visual recipe. `<IconButton>` is the same button,
|
||||
square, with a glyph instead of a text label. The icon-only layout is an
|
||||
internal Button mode — consumers reach it through `<IconButton>`, not a
|
||||
public `iconOnly` flag.
|
||||
@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* Eidos `<IconButton>` — the canonical icon-only button.
|
||||
*
|
||||
* Composes `<Button>` so it inherits the full visual + behavioural API
|
||||
* (variant / size / intent / color / rounded / shape / block / loading)
|
||||
* plus Button's morfo + sema — no new morfo. It adds only what icon-only
|
||||
* needs: the square layout (Button's icon-only mode) and a REQUIRED
|
||||
* `aria-label`, since the visible glyph carries no text.
|
||||
*
|
||||
* <IconButton aria-label="Close" variant="ghost"><SvgX /></IconButton>
|
||||
*
|
||||
* Glyph-agnostic: the `children` glyph renders in Button's icon slot
|
||||
* (visible); the body stays empty and the accessible name comes from
|
||||
* `aria-label`. When `loading`, Button swaps the glyph for its spinner.
|
||||
*/
|
||||
import { Button } from '$uix/eidos/components/button';
|
||||
import type { IconButtonProps } from './types';
|
||||
|
||||
let { 'aria-label': ariaLabel, children, ...rest }: IconButtonProps = $props();
|
||||
</script>
|
||||
|
||||
<Button {...rest} aria-label={ariaLabel} iconOnly icon={children} />
|
||||
@ -0,0 +1,18 @@
|
||||
// IconButton — eidos icon-only button primitive.
|
||||
//
|
||||
// import { IconButton } from '$uix/eidos/components/icon-button';
|
||||
//
|
||||
// <IconButton aria-label="Close" variant="ghost" rounded="full">
|
||||
// <SvgX />
|
||||
// </IconButton>
|
||||
//
|
||||
// Composes <Button> (inherits variant/size/intent/color/rounded/shape/
|
||||
// block/loading + Button's morfo + sema; no new morfo). The glyph is the
|
||||
// children; `aria-label` is required (the glyph carries no text).
|
||||
import IconButtonComponent from './icon-button.svelte';
|
||||
|
||||
export const IconButton = IconButtonComponent;
|
||||
|
||||
export default IconButton;
|
||||
|
||||
export type { IconButtonProps } from './types';
|
||||
@ -0,0 +1,25 @@
|
||||
import type { Snippet } from 'svelte';
|
||||
import type { ButtonProps } from '$uix/eidos/components/button';
|
||||
|
||||
/**
|
||||
* Props for the eidos `<IconButton>` — the canonical icon-only button.
|
||||
*
|
||||
* Composes `<Button>`, so it inherits the full visual + behavioural API
|
||||
* (variant / size / intent / color / rounded / shape / block / loading)
|
||||
* plus Button's morfo + sema — there is NO new morfo. It adds only what
|
||||
* icon-only needs: the square layout and a REQUIRED `aria-label`, since the
|
||||
* visible glyph carries no text label.
|
||||
*
|
||||
* The glyph is the `children` — any `svg/` primitive or lucide `Icon`:
|
||||
*
|
||||
* <IconButton aria-label="Close" variant="ghost"><SvgX /></IconButton>
|
||||
*
|
||||
* `icon` / `endIcon` are omitted (IconButton owns the single glyph slot);
|
||||
* the icon-only layout is internal, so `iconOnly` is not part of the API.
|
||||
*/
|
||||
export type IconButtonProps = Omit<ButtonProps, 'icon' | 'endIcon' | 'children' | 'iconOnly'> & {
|
||||
/** Accessible name — REQUIRED. The visible glyph carries no text label. */
|
||||
'aria-label': string;
|
||||
/** The glyph to render (an `svg/` primitive or a lucide `Icon`). */
|
||||
children: Snippet;
|
||||
};
|
||||
Loading…
Reference in new issue