From 8cee24dd487ee10d77a99862deeef740bfbbbf53 Mon Sep 17 00:00:00 2001 From: mochi Date: Tue, 3 Nov 2026 16:03:04 -0500 Subject: [PATCH] refactor(controls)!: remove CloseButton A close is an IconButton with an X, styled by where it sits, so a preset only got in the way. Toasts keep their filled hover in toast.css, and a Section's close stays in its header row. BREAKING: CloseButton and CloseButtonProps are no longer exported. --- docs/architecture.md | 13 +++++----- docs/controls.md | 10 -------- .../solid-stage/src/controls/CloseButton.tsx | 25 ------------------- packages/solid-stage/src/index.ts | 1 - packages/solid-stage/src/overlay/toast.tsx | 7 +++--- packages/solid-stage/src/theme/toast.css | 15 ++++++++--- 6 files changed, 22 insertions(+), 49 deletions(-) delete mode 100644 packages/solid-stage/src/controls/CloseButton.tsx diff --git a/docs/architecture.md b/docs/architecture.md index 12192ea..b7fd0f7 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1426,11 +1426,11 @@ visual classes + children. - **No click "press" animation** — the focus ring (now shown on click too) already marks the clicked control, so the old scale-punch was removed as a redundant holdover. -- **`CloseButton`** (exported) — an `IconButton` preset for a close (×), subtle and - mouse-only, used by the toast dismiss. A `Section`'s close isn't a separate control: `onClose` - appends a close action to its `headerActions` row, so every button there behaves alike. TabMenu's tab close - stays bespoke: it carries the modified-dot + hover-reveal behaviours and sits in - a glyph-button family with the `+` / scroll arrows, so it isn't a plain close. +- **There is no close button control.** A close is an `IconButton` with an `X`, styled by + where it sits. A `Section`'s `onClose` appends one to its `headerActions` row, so every + button there behaves alike. A toast's dismiss has its own skin in `toast.css`. TabMenu's + tab close stays bespoke: it carries the modified-dot + hover-reveal behaviours and sits in + a glyph-button family with the `+` / scroll arrows. ### Per-field notes - **Label** — static, display-only descriptive text (section blurb, status @@ -1786,8 +1786,7 @@ Like the modal it needs a one-time `` inside `.stage-root`; the host inside a positioned region (e.g. a `CanvasStage` viewport, which is `position: relative; overflow: hidden`) scopes + clips toasts to that region rather than the screen corner. -- The dismiss × is the shared **`CloseButton`** control (see §6), an `IconButton` - preset, so it has the same skin and tooltip as the other icon buttons. +- The dismiss × is an `IconButton` with its own skin in `toast.css` (see §6). - **Overflow cap:** `limit` (default 5) caps the *real* toasts shown; the excess collapses into one pinned "+N more…" indicator whose × clears all the hidden ones at once. `hide` (`'oldest'` default / `'newest'`) picks which end collapses diff --git a/docs/controls.md b/docs/controls.md index 6666877..8c47046 100644 --- a/docs/controls.md +++ b/docs/controls.md @@ -586,16 +586,6 @@ the flex grow chain): The stretch relies on a definite-width anchor, so `grow` is meaningful in the `1fr` `left` / `right` `Toolbar` cells (not the content-sized `center` cell). -### `CloseButton` - -An `IconButton` preset: an × in the subtle, mouse-only skin, with a tooltip. Exported so -your custom dismissable UI matches the toast dismiss. A `Section`'s `onClose` adds its own -close to the same row as its `headerActions`, so don't add one by hand. - -```tsx - -``` - ### `InfoIcon` The `(i)` info bubble used by fields' `info` prop. Rendered automatically when a diff --git a/packages/solid-stage/src/controls/CloseButton.tsx b/packages/solid-stage/src/controls/CloseButton.tsx deleted file mode 100644 index a85a8a5..0000000 --- a/packages/solid-stage/src/controls/CloseButton.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import type { JSX } from 'solid-js'; -import { IconButton } from './IconButton'; -import { X } from '../icons'; - -export interface CloseButtonProps { - readonly onClick: () => void; - /** Tooltip and accessible name. Defaults to "Close". */ - readonly label?: string | undefined; -} - -/** - * An {@link IconButton} preset for closing a custom dismissable surface: an `x` in the - * subtle, mouse-only skin (the keyboard close is Escape or the surface's own affordance). - */ -export function CloseButton(props: CloseButtonProps): JSX.Element { - return ( - - ); -} diff --git a/packages/solid-stage/src/index.ts b/packages/solid-stage/src/index.ts index 4a9910f..3bb2307 100644 --- a/packages/solid-stage/src/index.ts +++ b/packages/solid-stage/src/index.ts @@ -56,7 +56,6 @@ export { } from './controls/createFieldValidation'; export { FieldBar, type FieldBarProps } from './controls/FieldBar'; export { Label, type LabelProps, type LabelAlign, type LabelContent } from './controls/Label'; -export { CloseButton, type CloseButtonProps } from './controls/CloseButton'; export { focusField } from './controls/focusField'; export type { TouchSizingProps } from './controls/touchSizing'; diff --git a/packages/solid-stage/src/overlay/toast.tsx b/packages/solid-stage/src/overlay/toast.tsx index 9fb177d..a8b97c3 100644 --- a/packages/solid-stage/src/overlay/toast.tsx +++ b/packages/solid-stage/src/overlay/toast.tsx @@ -1,6 +1,7 @@ import { For, Show, createEffect, createSignal, onCleanup, onMount, type JSX } from 'solid-js'; import { Dynamic } from 'solid-js/web'; -import { CloseButton } from '../controls/CloseButton'; +import { IconButton } from '../controls/IconButton'; +import { X } from '../icons'; /** * Toast body: a plain string, or a thunk returning JSX for rich content (a link, @@ -169,7 +170,7 @@ export function StageToastHost(props: StageToastHostProps): JSX.Element {
+{hiddenCount()} more… - +
); @@ -264,7 +265,7 @@ function ToastBubble(props: { readonly toast: ToastEntry }): JSX.Element { {message()} {/* The × demands immediate feedback, so it removes outright (no fade-out); auto-dismiss and the `dismiss(id)` API still play the exit animation. */} - remove(props.toast.id)} /> + remove(props.toast.id)} subtle tabbable={false} /> ); } diff --git a/packages/solid-stage/src/theme/toast.css b/packages/solid-stage/src/theme/toast.css index 031dcf6..a2501d2 100644 --- a/packages/solid-stage/src/theme/toast.css +++ b/packages/solid-stage/src/theme/toast.css @@ -130,9 +130,18 @@ line-height: 1.4; } -/* The dismiss ×, with the hand cursor of the other meta-action icons. Nudged up so it - aligns with the first line of a multi-line message rather than centring on the block. */ +/* The dismiss ×: its hover adds a fill as well as brightening, since a toast has no header + row to read as a group. Nudged up so it aligns with the first line of a multi-line + message rather than centring on the block. */ .stage-toast .stage-icon-button { margin-top: -2px; - cursor: pointer; + border: none; +} + +.stage-toast .stage-icon-button:hover:not(:disabled) { + background: var(--stage-color-control-hover-bg); +} + +.stage-toast .stage-icon-button:active:not(:disabled) { + background: var(--stage-color-section-header-hover-bg); } -- knotmirror 0.0.1