/* Select mapping: the trigger and its connected list. Label and Helper/Error text are consumer composition. States use the Radix attributes (data-state, data-placeholder, data-highlighted, data-side) and :disabled; size uses data-size. */ :root, [data-hm-theme] { /* ui_viz: docs/components/select.md §1.1 D-004, §8 — Confirmed. Control height is the inner height: the 1px border is added around it (outer 34 / 44). Not aliased to Input or Button. */ --hm-select-control-height-md: 32px; --hm-select-control-height-lg: 42px; --hm-select-border-width: 1px; /* ui_viz: docs/components/select.md §8, §17 — BASELINE_VALUE (current applied contract) */ --hm-select-padding-x: 12px; --hm-select-gap: 12px; /* ui_viz select.md §8: Option min-height 42 for both sizes (no medium value defined). Kept as one token per size so a later Medium decision changes only this line. */ --hm-select-option-height-md: 42px; --hm-select-option-height-lg: 42px; --hm-select-radius: var(--hm-radius-input); /* ui_viz: docs/components/select.md §9 — large body-md 16/24/400, medium trigger body-sm 14/20/400, option body-md in both sizes */ --hm-select-font-size-md: 14px; --hm-select-line-height-md: 20px; --hm-select-font-size-lg: 16px; --hm-select-line-height-lg: 24px; /* Option typography follows the Select size (Trigger and list share one size). ui_viz left the Medium Option Role open (TBD, medium Trigger body-sm vs Option body-md); the Medium Option takes the Medium Trigger value 14/20/400. */ --hm-select-option-font-size-md: var(--hm-select-font-size-md); --hm-select-option-line-height-md: var(--hm-select-line-height-md); --hm-select-option-font-size-lg: var(--hm-select-font-size-lg); --hm-select-option-line-height-lg: var(--hm-select-line-height-lg); --hm-select-font-weight: 400; /* ui_viz: docs/components/select.md §10, §1.3 — Confirmed (D-005, M06) */ --hm-select-background: var(--hm-sem-bg-surface); --hm-select-text: var(--hm-sem-fg-primary); --hm-select-placeholder: var(--hm-sem-fg-placeholder); --hm-select-border-color: var(--hm-sem-stroke-input-default); --hm-select-focus-border-color: var(--hm-sem-stroke-input-focus); --hm-select-seam-color: var(--hm-sem-stroke-input-default); /* Open list face measured from the target Open screen: #e1f1ec on the white list = 45% of --accent (#bddfd4). Same mix the Toggle/Calendar hover faces use. The list shadow is the target's soft, downward-offset one (not the floating-surface shadow). */ --hm-select-option-state-background: var(--hm-sem-surface-highlight); --hm-select-option-state-text: var(--accent-foreground); --hm-select-content-shadow: 0 2px 24px color-mix(in srgb, var(--hm-sem-palette-black) 12%, transparent); /* ui_viz: docs/components/select.md §1.2 — user-confirmed Disabled: opacity .6, same white face and #DEDBD2 border */ --hm-select-disabled-opacity: 0.6; /* ui_viz: docs/components/select.md §7 — indicator glyph stroke (user-provided Source) */ --hm-select-indicator-color: var(--hm-sem-palette-indicator); } /* Trigger. Large is the default when size is omitted. */ [data-slot="select-trigger"] { --hm-select-control-height: var(--hm-select-control-height-lg); --hm-select-font-size: var(--hm-select-font-size-lg); --hm-select-line-height: var(--hm-select-line-height-lg); box-sizing: border-box; min-width: 0; height: auto; min-height: calc(var(--hm-select-control-height) + 2 * var(--hm-select-border-width)); gap: var(--hm-select-gap); padding: 0 var(--hm-select-padding-x); border: var(--hm-select-border-width) solid var(--hm-select-border-color); border-radius: var(--hm-select-radius); background: var(--hm-select-background); color: var(--hm-select-text); font-size: var(--hm-select-font-size); line-height: var(--hm-select-line-height); font-weight: var(--hm-select-font-weight); } [data-slot="select-trigger"] > [data-slot="select-value"] { min-width: 0; flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } [data-slot="select-trigger"][data-size="medium"] { --hm-select-control-height: var(--hm-select-control-height-md); --hm-select-font-size: var(--hm-select-font-size-md); --hm-select-line-height: var(--hm-select-line-height-md); } [data-slot="select-trigger"][data-placeholder] { color: var(--hm-select-placeholder); } [data-slot="select-icon"] { width: 18px; height: 18px; color: var(--hm-select-indicator-color); } /* Focus and Open retain the focus-colored outer outline. The connected edge follows Content's actual side. */ [data-slot="select-trigger"]:focus { border-color: var(--hm-select-focus-border-color); } [data-slot="select-trigger"][data-state="open"] { border-color: var(--hm-select-focus-border-color); } /* Direction is mirrored from the actual Radix Content through its ARIA relationship. */ [data-slot="select-trigger"][data-state="open"][data-side="bottom"] { border-radius: var(--hm-select-radius) var(--hm-select-radius) 0 0; border-bottom-color: var(--hm-select-seam-color); } [data-slot="select-trigger"][data-state="open"][data-side="top"] { border-radius: 0 0 var(--hm-select-radius) var(--hm-select-radius); border-top-color: var(--hm-select-seam-color); } /* Disabled keeps the Default paint and dims the whole control. */ [data-slot="select-trigger"]:disabled { border-color: var(--hm-select-border-color); background: var(--hm-select-background); opacity: var(--hm-select-disabled-opacity); cursor: not-allowed; } /* Invalid belongs to the trigger border, never the popup surface or its neutral seam. */ [data-slot="select-trigger"][aria-invalid="true"] { border-color: var(--hm-sem-action-destructive); } [data-slot="select-trigger"][aria-invalid="true"]:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 50%, transparent); } [data-slot="select-trigger"][aria-invalid="true"][data-state="open"][data-side="bottom"] { border-bottom-color: var(--hm-select-seam-color); } [data-slot="select-trigger"][aria-invalid="true"][data-state="open"][data-side="top"] { border-top-color: var(--hm-select-seam-color); } /* List: same outer width as the trigger, border in the focus color, rounded only at the bottom (connected outline). */ [data-slot="select-content"] { box-sizing: border-box; border: var(--hm-select-border-width) solid var(--hm-select-focus-border-color); border-radius: var(--hm-select-radius); background: var(--hm-select-background); color: var(--hm-select-text); box-shadow: var(--hm-select-content-shadow); } [data-slot="select-content"]:not([data-align-trigger="true"]) { width: var(--radix-select-trigger-width); min-width: var(--radix-select-trigger-width); } [data-slot="select-content"][data-side="bottom"] { border-radius: 0 0 var(--hm-select-radius) var(--hm-select-radius); /* sideOffset=-1 puts this top border on the trigger's bottom border, so it is the one seam line between the two. Only the seam is the quiet control border; the outer outline stays the focus color. */ border-top-color: var(--hm-select-seam-color); } [data-slot="select-content"][data-side="top"] { border-radius: var(--hm-select-radius) var(--hm-select-radius) 0 0; border-bottom-color: var(--hm-select-seam-color); } /* Content uses the trigger's outer width. Its Viewport lives inside that bordered box, so it must fill the available inner width instead of applying the trigger's outer width as another minimum. */ [data-slot="select-content"] [data-position="popper"] { box-sizing: border-box; width: 100%; min-width: 0; } /* Option profile follows the Select size: SelectContent carries data-size (from the Trigger, through context), the Portal keeps it out of the Trigger subtree. Width is not size-dependent (Content = Trigger outer width). Hover, Selected and Keyboard Active use the same face (set by the shadcn state classes). */ [data-slot="select-content"] { --hm-select-option-height: var(--hm-select-option-height-lg); --hm-select-option-font-size: var(--hm-select-option-font-size-lg); --hm-select-option-line-height: var(--hm-select-option-line-height-lg); } [data-slot="select-content"][data-size="medium"] { --hm-select-option-height: var(--hm-select-option-height-md); --hm-select-option-font-size: var(--hm-select-option-font-size-md); --hm-select-option-line-height: var(--hm-select-option-line-height-md); } [data-slot="select-item"] { box-sizing: border-box; min-height: var(--hm-select-option-height); padding: 0 var(--hm-select-padding-x); border-radius: 0; font-size: var(--hm-select-option-font-size); line-height: var(--hm-select-option-line-height); font-weight: var(--hm-select-font-weight); } /* ItemText: a flex item defaults to min-width:auto, so an unbreakable label kept its full width and pushed the row past the list (Item scrollWidth > Viewport clientWidth). Let it shrink to the row and truncate, like the trigger value. */ [data-slot="select-item"] > span { display: block; min-width: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* Radix keeps selection and the current pointer/keyboard highlight as separate states. ui_viz gives both the same option face, so use the actual Radix attributes rather than DOM position or focus alone. */ [data-slot="select-item"][data-state="checked"], [data-slot="select-item"][data-highlighted] { background: var(--hm-select-option-state-background); color: var(--hm-select-option-state-text); }