From 826481f1d5779f0a6af0ca44434ce7d919bc4948 Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 22 May 2026 02:18:05 +0200 Subject: [PATCH] fix(picker-shell): footer buttons scale with picker size (xs/sm/md/lg) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The footer buttons (Clear / Cancel / Done) were hardcoded to control-height-sm + font-size-sm + space-3 padding regardless of the host picker's size. At xs the buttons looked oversized next to a compact picker; at lg they looked undersized. - picker-shell.css: refactored button rules to consume four `--_picker-shell-*` CSS vars (control-h, padding-x, font, gap, row-spacing). Defaults match the previous sm sizing. Added explicit xs/md/lg variant blocks keyed off `[data-popover-content][data-picker-size='X']`. - date-picker-content / date-range-picker-content / time-picker-content / time-range-picker-content / color-picker-content: forward the picker's resolvedSize to the popover via a separate `data-picker-size` attr, independent from the popover chrome's clamped `data-size`. This decouples the popover's own chrome (which still clamps xs → sm because the popover layer has no xs variant) from the picker-shell button scaling, which now uses the picker's true size. Verified at /uix/components/date-picker: - xs → 22 / 12px (control-height-xxs, font-size-xs) - sm → 30 / 14px (default) - md → 36 / 16px - lg → 44 / 16px Also fixes the horizontal scroll at xs that surfaced the issue. The date-picker.css `[data-popover-content]:has(...)` rule lets the popover grow to max-content when it hosts a calendar / grid. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../color-picker/color-picker-content.svelte | 1 + .../date-picker/date-picker-content.svelte | 1 + .../date-picker/date-picker-month-view.svelte | 6 ++- .../date-picker/date-picker-year-view.svelte | 6 ++- .../components/date-picker/date-picker.css | 16 ++++++- .../date-range-picker-content.svelte | 1 + .../components/picker-shell/picker-shell.css | 47 ++++++++++++++++--- .../time-picker/time-picker-content.svelte | 1 + .../time-range-picker-content.svelte | 1 + 9 files changed, 71 insertions(+), 9 deletions(-) diff --git a/src/uix/eidos/components/color-picker/color-picker-content.svelte b/src/uix/eidos/components/color-picker/color-picker-content.svelte index d92975667..1b20e276d 100644 --- a/src/uix/eidos/components/color-picker/color-picker-content.svelte +++ b/src/uix/eidos/components/color-picker/color-picker-content.svelte @@ -24,6 +24,7 @@ {sideOffset} {collisionPadding} size={resolvedSize} + data-picker-size={resolvedSize} data-color-picker-content="" > {#snippet children(snippetProps)} diff --git a/src/uix/eidos/components/date-picker/date-picker-content.svelte b/src/uix/eidos/components/date-picker/date-picker-content.svelte index d01ab5fd4..f017cbdb4 100644 --- a/src/uix/eidos/components/date-picker/date-picker-content.svelte +++ b/src/uix/eidos/components/date-picker/date-picker-content.svelte @@ -67,6 +67,7 @@ {sideOffset} {collisionPadding} size={popoverSize} + data-picker-size={resolvedSize} style={composedStyle} > {#snippet children(snippetProps)} diff --git a/src/uix/eidos/components/date-picker/date-picker-month-view.svelte b/src/uix/eidos/components/date-picker/date-picker-month-view.svelte index d083f58f3..7a4afca1e 100644 --- a/src/uix/eidos/components/date-picker/date-picker-month-view.svelte +++ b/src/uix/eidos/components/date-picker/date-picker-month-view.svelte @@ -3,7 +3,8 @@ import { CalendarDate } from '$libs/days'; import { MonthGrid } from '../month-grid'; import type { MonthValue, MonthPlaceholder } from '$soma/components/month-grid'; - import type { MonthGridProviderSnippetProps } from '../month-grid'; + import type { MonthGridProviderSnippetProps, MonthGridSize } from '../month-grid'; + import { getDatePickerVisualContext } from './context.svelte'; import type { HTMLAttributes } from 'svelte/elements'; // Month-grid view for `kind='month'`. Internally renders the standalone @@ -20,6 +21,8 @@ const _props: MonthViewProps = $props(); const provider = DatePickerProvider.require(); + const visual = getDatePickerVisualContext(); + const size = $derived((visual?.getSize() ?? 'md') as MonthGridSize); const anchor = $derived(provider.opts.value.current ?? provider.opts.placeholder.current); @@ -53,6 +56,7 @@ placeholder={monthPlaceholder} {onValueChange} {onPlaceholderChange} + {size} locale={provider.opts.locale.current} disabled={provider.opts.disabled.current} readonly={provider.opts.readonly.current} diff --git a/src/uix/eidos/components/date-picker/date-picker-year-view.svelte b/src/uix/eidos/components/date-picker/date-picker-year-view.svelte index f890e0e64..ace8a90ca 100644 --- a/src/uix/eidos/components/date-picker/date-picker-year-view.svelte +++ b/src/uix/eidos/components/date-picker/date-picker-year-view.svelte @@ -3,7 +3,8 @@ import { CalendarDate } from '$libs/days'; import { YearGrid } from '../year-grid'; import type { YearValue, YearPlaceholder } from '$soma/components/year-grid'; - import type { YearGridProviderSnippetProps } from '../year-grid'; + import type { YearGridProviderSnippetProps, YearGridSize } from '../year-grid'; + import { getDatePickerVisualContext } from './context.svelte'; import type { HTMLAttributes } from 'svelte/elements'; // Year-grid view for `kind='year'`. Internally renders the standalone @@ -20,6 +21,8 @@ const _props: YearViewProps = $props(); const provider = DatePickerProvider.require(); + const visual = getDatePickerVisualContext(); + const size = $derived((visual?.getSize() ?? 'md') as YearGridSize); const anchor = $derived(provider.opts.value.current ?? provider.opts.placeholder.current); @@ -52,6 +55,7 @@ placeholder={yearPlaceholder} {onValueChange} {onPlaceholderChange} + {size} locale={provider.opts.locale.current} disabled={provider.opts.disabled.current} readonly={provider.opts.readonly.current} diff --git a/src/uix/eidos/components/date-picker/date-picker.css b/src/uix/eidos/components/date-picker/date-picker.css index 952d1a0a4..f07874932 100644 --- a/src/uix/eidos/components/date-picker/date-picker.css +++ b/src/uix/eidos/components/date-picker/date-picker.css @@ -197,7 +197,21 @@ /* ── Year-view + Month-view (kind='year' / 'month') ──────────────────────── */ /* DatePicker.MonthView and DatePicker.YearView now delegate to the standalone MonthGrid / YearGrid primitives, so their visuals come from - `month-grid.css` and `year-grid.css`. No local rules needed. */ + `month-grid.css` and `year-grid.css`. */ + +/* The popover content uses a size-based base width (popover-min-width-{sm,md,…}) + that can be narrower than the picker-shell footer's intrinsic content + (Clear / Cancel / Save buttons + gaps). Let the popover grow to max-content + when it hosts a date-picker calendar / grid so the inner row never overflows + and produces a horizontal scrollbar. */ +[data-popover-content]:has( + > [data-date-picker-calendar], + > [data-month-grid], + > [data-year-grid] + ) { + inline-size: max-content; + min-inline-size: max-content; +} @media (prefers-reduced-motion: reduce) { [data-date-picker-trigger] { diff --git a/src/uix/eidos/components/date-range-picker/date-range-picker-content.svelte b/src/uix/eidos/components/date-range-picker/date-range-picker-content.svelte index 2f8190469..31811502a 100644 --- a/src/uix/eidos/components/date-range-picker/date-range-picker-content.svelte +++ b/src/uix/eidos/components/date-range-picker/date-range-picker-content.svelte @@ -67,6 +67,7 @@ {sideOffset} {collisionPadding} size={popoverSize} + data-picker-size={resolvedSize} style={composedStyle} > {#snippet children(snippetProps)} diff --git a/src/uix/eidos/components/picker-shell/picker-shell.css b/src/uix/eidos/components/picker-shell/picker-shell.css index 348d991bb..b32699ed3 100644 --- a/src/uix/eidos/components/picker-shell/picker-shell.css +++ b/src/uix/eidos/components/picker-shell/picker-shell.css @@ -8,11 +8,20 @@ */ [data-picker-footer] { + /* Size-scaling vars. Defaults match `sm`. The host picker scopes these + * via `[data-popover-content][data-picker-size='X']` so xs/md/lg tune + * the button row to match the picker. */ + --_picker-shell-control-h: var(--control-height-sm); + --_picker-shell-padding-x: var(--space-3); + --_picker-shell-font: var(--font-size-sm); + --_picker-shell-gap: var(--space-2); + --_picker-shell-row-spacing: var(--space-3); + display: flex; align-items: center; - gap: var(--space-2); - padding-block-start: var(--space-3); - margin-block-start: var(--space-3); + gap: var(--_picker-shell-gap); + padding-block-start: var(--_picker-shell-row-spacing); + margin-block-start: var(--_picker-shell-row-spacing); border-block-start: var(--border-width) solid var(--color-border-subtle); } @@ -25,14 +34,14 @@ [data-picker-cancel], [data-picker-close] { inline-size: max-content; - min-block-size: var(--control-height-sm); - padding-inline: var(--space-3); + min-block-size: var(--_picker-shell-control-h); + padding-inline: var(--_picker-shell-padding-x); border-radius: var(--radius-md); border: var(--border-width) solid transparent; background: transparent; color: var(--color-content-primary); font: inherit; - font-size: var(--font-size-sm); + font-size: var(--_picker-shell-font); line-height: var(--leading-ui); cursor: pointer; transition: @@ -41,6 +50,32 @@ color var(--duration-fast) var(--ease-default); } +/* Size variants — host pickers emit `data-picker-size` on the popover + * content so portaled descendants can scale. The button row inherits + * the picker's size; popover chrome (padding / min-width) keeps its + * own size variant via [data-size]. */ +[data-popover-content][data-picker-size='xs'] [data-picker-footer] { + --_picker-shell-control-h: var(--control-height-xxs); + --_picker-shell-padding-x: var(--space-2); + --_picker-shell-font: var(--font-size-xs); + --_picker-shell-gap: var(--space-1-5); + --_picker-shell-row-spacing: var(--space-2); +} + +[data-popover-content][data-picker-size='md'] [data-picker-footer] { + --_picker-shell-control-h: var(--control-height-md); + --_picker-shell-padding-x: var(--space-3-5); + --_picker-shell-font: var(--font-size-md); +} + +[data-popover-content][data-picker-size='lg'] [data-picker-footer] { + --_picker-shell-control-h: var(--control-height-lg); + --_picker-shell-padding-x: var(--space-4); + --_picker-shell-font: var(--font-size-md); + --_picker-shell-gap: var(--space-2-5); + --_picker-shell-row-spacing: var(--space-4); +} + [data-picker-clear]:hover, [data-picker-cancel]:hover { background: var(--color-surface-overlay); diff --git a/src/uix/eidos/components/time-picker/time-picker-content.svelte b/src/uix/eidos/components/time-picker/time-picker-content.svelte index c25fa34f2..3f78a6d92 100644 --- a/src/uix/eidos/components/time-picker/time-picker-content.svelte +++ b/src/uix/eidos/components/time-picker/time-picker-content.svelte @@ -63,6 +63,7 @@ {sideOffset} {collisionPadding} size={resolvedSize} + data-picker-size={resolvedSize} style={composedStyle} > {#snippet children(snippetProps)} diff --git a/src/uix/eidos/components/time-range-picker/time-range-picker-content.svelte b/src/uix/eidos/components/time-range-picker/time-range-picker-content.svelte index 2fe3ea991..4d50ea965 100644 --- a/src/uix/eidos/components/time-range-picker/time-range-picker-content.svelte +++ b/src/uix/eidos/components/time-range-picker/time-range-picker-content.svelte @@ -63,6 +63,7 @@ {sideOffset} {collisionPadding} size={resolvedSize} + data-picker-size={resolvedSize} style={composedStyle} > {#snippet children(snippetProps)}