diff --git a/src/uix/eidos/components/field/field-control-trigger.css b/src/uix/eidos/components/field/field-control-trigger.css
index 34112cb52..71933d032 100644
--- a/src/uix/eidos/components/field/field-control-trigger.css
+++ b/src/uix/eidos/components/field/field-control-trigger.css
@@ -14,6 +14,17 @@
* (which column / flex slot) stays with the host field.
*/
+/*
+ * When a field input hosts a control trigger, the input drops its padding on
+ * the trigger side (logical → works in both LTR and RTL): the trigger sits flush
+ * at the edge and provides its own inset (the centred glyph in its 1.75em box),
+ * instead of the input's full text padding adding a second, larger gap. One rule
+ * for every field — keys off the input archetype + the shared trigger marker.
+ */
+[data-archetype='input']:has([data-field-control-trigger]) {
+ padding-inline-end: 0;
+}
+
[data-field-control-trigger] {
display: inline-flex;
align-items: center;
diff --git a/src/uix/eidos/components/popover/popover.css b/src/uix/eidos/components/popover/popover.css
index aad143089..2e0815389 100644
--- a/src/uix/eidos/components/popover/popover.css
+++ b/src/uix/eidos/components/popover/popover.css
@@ -2,8 +2,15 @@
* Air relied on the Button recipe via .air-button class. Eidos applies a
* baseline button envelope here. Consumes Capa 2 directly (same as Dialog
* trigger). Consumers can override per-instance via inline tokens.
+ *
+ * `:not([data-field-control-trigger])` — a field's icon affordance (date/time/
+ * color picker calendar/clock/swatch button) opens its overlay via a popover, so
+ * it ALSO carries `data-popover-trigger`, but it owns a flush, border-less
+ * treatment in `field-control-trigger.css`. This generic button envelope must
+ * NOT win over it (the two are equal-specificity, so it would otherwise depend
+ * on CSS load order).
*/
-[data-popover-trigger] {
+[data-popover-trigger]:not([data-field-control-trigger]) {
display: inline-flex;
align-items: center;
justify-content: center;
@@ -21,17 +28,17 @@
border-color var(--duration-fast) var(--ease-default);
}
-[data-popover-trigger]:hover:not([disabled]):not([data-disabled]) {
+[data-popover-trigger]:not([data-field-control-trigger]):hover:not([disabled]):not([data-disabled]) {
background: var(--color-surface-overlay);
border-color: var(--color-border-strong);
}
-[data-popover-trigger][data-state='open'] {
+[data-popover-trigger]:not([data-field-control-trigger])[data-state='open'] {
background: var(--color-surface-overlay);
border-color: var(--color-border-strong);
}
-[data-popover-trigger]:focus-visible {
+[data-popover-trigger]:not([data-field-control-trigger]):focus-visible {
outline: var(--focus-ring-width) solid var(--focus-ring-color);
outline-offset: var(--focus-ring-offset);
}
diff --git a/src/uix/morfo/components/calendar.ts b/src/uix/morfo/components/calendar.ts
index 6774b14d4..a416475d3 100644
--- a/src/uix/morfo/components/calendar.ts
+++ b/src/uix/morfo/components/calendar.ts
@@ -244,7 +244,9 @@ export const calendarMorfo = {
{
name: 'GridRow',
kebab: 'grid-row',
- archetype: 'item',
+ // No archetype: a `
` week row is a structural container, NOT a menu
+ // `item`. `data-archetype='item'` would tint the WHOLE row on hover (the
+ // "hover over the entire week" bug). Each Day owns its own hover.
kind: 'public',
defaultElement: 'tr',
role: 'row',
@@ -265,7 +267,10 @@ export const calendarMorfo = {
{
name: 'Cell',
kebab: 'cell',
- archetype: 'item',
+ // No archetype: the cell is a display `gridcell` container, NOT an
+ // interactive menu `item`. `data-archetype='item'` would ship the
+ // listbox-row chrome (padding-block, cursor, hover band) — the
+ // clickable Day inside owns its own interaction styling.
kind: 'public',
defaultElement: 'td',
role: 'gridcell',
@@ -300,7 +305,11 @@ export const calendarMorfo = {
{
name: 'Day',
kebab: 'day',
- archetype: 'item',
+ // No archetype: the Day owns bespoke `[data-calendar-day]` styling for
+ // hover / selected / focused. `data-archetype='item'` would let the
+ // generic listbox-item rules (higher specificity) OVERRIDE the selected
+ // fill — the selected day showed the menu-item tint instead of the
+ // calendar accent. The day is a calendar cell, not a menu item.
kind: 'public',
defaultElement: 'div',
role: 'button',
diff --git a/src/uix/morfo/components/date-picker.ts b/src/uix/morfo/components/date-picker.ts
index 188f03b89..c25475205 100644
--- a/src/uix/morfo/components/date-picker.ts
+++ b/src/uix/morfo/components/date-picker.ts
@@ -124,7 +124,7 @@ export const datePickerMorfo = {
{
name: 'Trigger',
kebab: 'trigger',
- archetype: 'trigger',
+ archetype: 'field-trigger',
kind: 'public',
defaultElement: 'button',
role: 'button',
diff --git a/src/uix/morfo/components/month-grid.ts b/src/uix/morfo/components/month-grid.ts
index fccb4e833..7fdfb1e0d 100644
--- a/src/uix/morfo/components/month-grid.ts
+++ b/src/uix/morfo/components/month-grid.ts
@@ -180,7 +180,8 @@ export const monthGridMorfo = {
{
name: 'Cell',
kebab: 'cell',
- archetype: 'item',
+ // No archetype: display gridcell container, not a menu `item` (the
+ // inner month button owns the interaction styling).
kind: 'public',
defaultElement: 'button',
role: 'gridcell',
diff --git a/src/uix/morfo/components/range-calendar.ts b/src/uix/morfo/components/range-calendar.ts
index c47f97244..6aa62d189 100644
--- a/src/uix/morfo/components/range-calendar.ts
+++ b/src/uix/morfo/components/range-calendar.ts
@@ -219,7 +219,8 @@ export const rangeCalendarMorfo = {
{
name: 'GridRow',
kebab: 'grid-row',
- archetype: 'item',
+ // No archetype: structural `
` row, not a menu `item` (would tint the
+ // whole week on hover).
kind: 'public',
defaultElement: 'tr',
role: 'row',
@@ -240,7 +241,8 @@ export const rangeCalendarMorfo = {
{
name: 'Cell',
kebab: 'cell',
- archetype: 'item',
+ // No archetype: display `gridcell` container, not a menu `item` (would
+ // add the listbox-row padding + hover band). The Day inside is the item.
kind: 'public',
defaultElement: 'td',
role: 'gridcell',
@@ -279,7 +281,9 @@ export const rangeCalendarMorfo = {
{
name: 'Day',
kebab: 'day',
- archetype: 'item',
+ // No archetype: bespoke `[data-range-calendar-day]` styling owns hover /
+ // selected / range. `data-archetype='item'` would let generic listbox-item
+ // rules override the selected fill (showed the menu tint, not the accent).
kind: 'public',
defaultElement: 'div',
role: 'button',
diff --git a/src/uix/morfo/components/year-grid.ts b/src/uix/morfo/components/year-grid.ts
index a08a65197..ceaa5b7a0 100644
--- a/src/uix/morfo/components/year-grid.ts
+++ b/src/uix/morfo/components/year-grid.ts
@@ -179,7 +179,8 @@ export const yearGridMorfo = {
{
name: 'Cell',
kebab: 'cell',
- archetype: 'item',
+ // No archetype: display gridcell container, not a menu `item` (the
+ // inner year button owns the interaction styling).
kind: 'public',
defaultElement: 'button',
role: 'gridcell',
diff --git a/src/uix/morfo/types.ts b/src/uix/morfo/types.ts
index c52cec7e7..47c2e0d56 100644
--- a/src/uix/morfo/types.ts
+++ b/src/uix/morfo/types.ts
@@ -628,7 +628,9 @@ export interface MorfoFocus {
*/
export type MorfoArchetype =
| 'provider' // root context provider
- | 'trigger' // activates an action or opens an overlay
+ | 'trigger' // activates an action or opens an overlay (button-like)
+ | 'field-trigger' // a field/input/select/combo trigger — NOT a button; owns
+ // the flush field-control treatment, never the generic button/popover chrome
| 'content' // primary content panel of an overlay or section
| 'overlay' // modal/dim backdrop behind content
| 'viewport' // scrollable / focusable container
@@ -663,6 +665,7 @@ export type MorfoArchetype =
export const ARCHETYPE_VOCABULARY = [
'provider',
'trigger',
+ 'field-trigger',
'content',
'overlay',
'viewport',
diff --git a/src/uix/soma/components/color-picker/components/color-picker-trigger.svelte b/src/uix/soma/components/color-picker/components/color-picker-trigger.svelte
index 5af1631d5..4553eb5be 100644
--- a/src/uix/soma/components/color-picker/components/color-picker-trigger.svelte
+++ b/src/uix/soma/components/color-picker/components/color-picker-trigger.svelte
@@ -55,7 +55,9 @@
triggerState.props,
(popoverTriggerProps ?? {}) as Record,
{
- [colorPickerAttrs.trigger]: ''
+ [colorPickerAttrs.trigger]: '',
+ // Field overlay trigger composed on a popover trigger — not a button.
+ 'data-archetype': 'field-trigger'
} as Record
)
);
diff --git a/src/uix/soma/components/date-picker/components/date-picker-trigger.svelte b/src/uix/soma/components/date-picker/components/date-picker-trigger.svelte
index 331982e73..425758867 100644
--- a/src/uix/soma/components/date-picker/components/date-picker-trigger.svelte
+++ b/src/uix/soma/components/date-picker/components/date-picker-trigger.svelte
@@ -45,6 +45,10 @@
state.props,
{
[datePickerAttrs.trigger]: '',
+ // A field's overlay trigger is composed ON a popover trigger (which
+ // stamps the generic `trigger` archetype), but it is NOT a button —
+ // override to the `field-trigger` archetype so eidos treats it flush.
+ 'data-archetype': 'field-trigger',
'aria-label': resolvedLabel,
disabled: provider.opts.disabled.current || undefined
} as Record
diff --git a/src/uix/soma/components/date-range-picker/components/date-range-picker-trigger.svelte b/src/uix/soma/components/date-range-picker/components/date-range-picker-trigger.svelte
index 29c5726d6..89a63e9ed 100644
--- a/src/uix/soma/components/date-range-picker/components/date-range-picker-trigger.svelte
+++ b/src/uix/soma/components/date-range-picker/components/date-range-picker-trigger.svelte
@@ -45,6 +45,8 @@
const mergedProps = $derived(
mergeProps(restProps, state.props, {
[dateRangePickerAttrs.trigger]: '',
+ // Field overlay trigger composed on a popover trigger — not a button.
+ 'data-archetype': 'field-trigger',
'aria-label': resolvedLabel,
disabled: provider.opts.disabled.current || undefined
} as Record)
diff --git a/src/uix/soma/components/time-picker/components/time-picker-trigger.svelte b/src/uix/soma/components/time-picker/components/time-picker-trigger.svelte
index 2a94eba63..f00634984 100644
--- a/src/uix/soma/components/time-picker/components/time-picker-trigger.svelte
+++ b/src/uix/soma/components/time-picker/components/time-picker-trigger.svelte
@@ -42,6 +42,8 @@
const mergedProps = $derived(
mergeProps(restProps, state.props, {
[timePickerAttrs.trigger]: '',
+ // Field overlay trigger composed on a popover trigger — not a button.
+ 'data-archetype': 'field-trigger',
'aria-label': resolvedLabel,
disabled: provider.opts.disabled.current || undefined
} as Record)