Completes the IconButton extraction by adding `size` and `radius` props and converting the two remaining outliers that were left out of the first pass: the desktop header logout button (+layout) and the home search submit button (+page), both 36px with the medium radius. IconButton now drives every icon button in the app (except profile/history's, which the shared-HistoryList change removes separately). Defaults stay 32px / small radius, so the four already-converted sites are untouched. Rendered size, radius, and variant match the old markup — no behaviour change, no version bump. This also sets up applying the --tap-min touch floor in one place once the touch-target change lands. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
81 lines
2.2 KiB
Svelte
81 lines
2.2 KiB
Svelte
<script lang="ts">
|
|
import type { Snippet } from 'svelte';
|
|
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
|
|
// Shared square icon button — extracted from the near-identical
|
|
// `.icon-btn` copies across the app so the size, hover, and variant
|
|
// treatment live in one place. Callers pass the lucide icon as the child
|
|
// and any button attributes (onclick, disabled, aria-label, title,
|
|
// data-testid) straight through. `size`/`radius` cover the two larger
|
|
// header/search buttons without forcing the 32px default everywhere.
|
|
type Variant = 'plain' | 'primary' | 'danger';
|
|
|
|
let {
|
|
variant = 'plain',
|
|
size = 32,
|
|
radius = 'sm',
|
|
children,
|
|
...rest
|
|
}: {
|
|
variant?: Variant;
|
|
size?: number;
|
|
radius?: 'sm' | 'md';
|
|
children: Snippet;
|
|
} & HTMLButtonAttributes = $props();
|
|
</script>
|
|
|
|
<!-- `type="button"` precedes the spread so a caller can still override it
|
|
(e.g. a submit button) while the default never submits a form. -->
|
|
<button
|
|
type="button"
|
|
class="icon-btn {variant} radius-{radius}"
|
|
style:--ib-size="{size}px"
|
|
{...rest}
|
|
>
|
|
{@render children()}
|
|
</button>
|
|
|
|
<style>
|
|
.icon-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: var(--ib-size, 32px);
|
|
height: var(--ib-size, 32px);
|
|
padding: 0;
|
|
background: transparent;
|
|
color: var(--text-muted);
|
|
border: 1px solid transparent;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.radius-sm {
|
|
border-radius: var(--radius-sm);
|
|
}
|
|
|
|
.radius-md {
|
|
border-radius: var(--radius-md);
|
|
}
|
|
|
|
.icon-btn:hover:not(:disabled) {
|
|
background: var(--surface-elevated);
|
|
color: var(--text);
|
|
}
|
|
|
|
.icon-btn.primary {
|
|
background: var(--primary);
|
|
color: var(--primary-contrast);
|
|
border-color: var(--primary);
|
|
}
|
|
|
|
.icon-btn.primary:hover:not(:disabled) {
|
|
background: var(--primary-hover);
|
|
border-color: var(--primary-hover);
|
|
}
|
|
|
|
.icon-btn.danger:hover:not(:disabled) {
|
|
color: var(--danger);
|
|
background: var(--surface-elevated);
|
|
}
|
|
</style>
|