Files
2026-07-13 12:12:04 +08:00

290 lines
7.5 KiB
CSS
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Autocomplete styles */
.autocomplete {
@apply flex flex-col gap-1;
}
.autocomplete__trigger {
@apply relative isolate inline-flex min-h-9 rounded-field border bg-field px-3 py-2 text-sm text-field-foreground shadow-field outline-none select-none no-highlight;
/**
* Transitions
* CRITICAL: motion-reduce must be AFTER transition for correct override specificity
*/
transition:
background-color 150ms var(--ease-smooth),
border-color 150ms var(--ease-smooth),
box-shadow 150ms var(--ease-out);
@apply motion-reduce:transition-none;
/* Cursor */
cursor: var(--cursor-interactive);
border-width: var(--border-width-field);
border-color: var(--field-border);
/* Add inline-end padding when indicator is present */
&:has(.autocomplete__indicator) {
@apply pe-7;
}
/* Hover state - exclude when clear button is hovered to prevent double hover effect */
@media (hover: hover) {
&:hover:not(:has(.autocomplete__clear-button:hover)),
&[data-hovered="true"]:not(:has(.autocomplete__clear-button:hover)) {
@apply bg-field-hover;
border-color: var(--field-border-hover);
}
}
/* Focus visible state */
&:focus-visible:not(:focus),
&[data-focus-visible="true"] {
@apply status-focused;
border-color: var(--field-border-focus);
background-color: var(--field-focus);
}
/* Focus state */
&:focus,
&[data-focus="true"] {
}
/* Invalid state */
.autocomplete[data-invalid="true"] &,
.autocomplete[aria-invalid="true"] & {
@apply status-invalid-field;
background-color: var(--field-focus);
}
/* Disabled state */
&:disabled,
&[data-disabled="true"],
&[aria-disabled="true"] {
@apply status-disabled;
}
}
/* Variant property definitions */
.autocomplete--primary {
/* Default styles */
}
.autocomplete--secondary .autocomplete__trigger {
@apply shadow-none;
background-color: var(--autocomplete-trigger-bg);
--autocomplete-trigger-bg: var(--default);
--autocomplete-trigger-bg-hover: var(--default-hover);
--autocomplete-trigger-bg-focus: var(--default);
/* Exclude when clear button is hovered to prevent double hover effect */
@media (hover: hover) {
&:hover:not(:has(.autocomplete__clear-button:hover)),
&[data-hovered="true"]:not(:has(.autocomplete__clear-button:hover)) {
background-color: var(--autocomplete-trigger-bg-hover);
}
}
&:focus-visible:not(:focus),
&[data-focus-visible="true"] {
background-color: var(--autocomplete-trigger-bg-focus);
}
&:focus,
&[data-focus="true"] {
}
.autocomplete[data-invalid="true"] &,
.autocomplete[aria-invalid="true"] & {
background-color: var(--autocomplete-trigger-bg-focus);
}
}
.autocomplete__value {
@apply flex-1 text-start text-base wrap-break-word text-current sm:text-sm;
&[data-placeholder="true"] {
@apply text-field-placeholder;
}
/* Removes the list-box-item indicator element from the autocomplete value*/
[data-slot="list-box-item-indicator"] {
@apply hidden;
}
}
.autocomplete__indicator {
@apply absolute inset-y-0 end-2 my-auto flex shrink-0 items-center justify-center text-field-placeholder transition duration-150;
/* Cursor */
cursor: var(--cursor-interactive);
&[data-open="true"] {
@apply rotate-180;
}
&[data-slot="autocomplete-default-indicator"] {
@apply size-4;
}
}
/* Similar to popover content styles */
.autocomplete__popover {
@apply w-(--trigger-width) max-w-(--trigger-width) origin-(--trigger-anchor-point) scroll-py-1 overflow-hidden overscroll-contain bg-overlay p-0 pt-2 text-sm;
border-radius: min(32px, var(--radius-3xl));
box-shadow: var(--shadow-overlay);
/* Focus state */
&:focus-visible:not(:focus),
&[data-focus-visible="true"] {
@apply outline-none;
}
/* Entering animations */
&[data-entering="true"] {
@apply animate-in duration-250 ease-out-fluid fade-in-0 zoom-in-95;
/* Placement-specific translations */
&[data-placement="top"] {
@apply slide-in-from-bottom-1;
}
&[data-placement="bottom"] {
@apply slide-in-from-top-1;
}
&[data-placement="left"] {
@apply slide-in-from-right-1;
}
&[data-placement="right"] {
@apply slide-in-from-left-1;
}
}
/* Exiting animations */
&[data-exiting="true"] {
@apply animate-out duration-100 ease-out-quad zoom-out-95 fade-out;
}
&[data-exiting="true"],
&[data-entering="true"] {
@apply transition-[opacity,transform] will-change-[opacity,transform];
}
/* Arrow styling */
& [data-slot="popover-overlay-arrow"] {
fill: var(--overlay);
}
/* Arrow rotation based on placement */
&[data-placement="bottom"] [data-slot="popover-overlay-arrow"] {
rotate: 180deg;
}
&[data-placement="left"] [data-slot="popover-overlay-arrow"] {
rotate: -90deg;
}
&[data-placement="right"] [data-slot="popover-overlay-arrow"] {
rotate: 90deg;
}
/* Listbox styles */
[data-slot="list-box"] {
@apply max-h-[320px] scrollbar overflow-y-auto p-1.5 outline-none;
}
[data-slot="list-box-item"] {
@apply px-2.5;
}
/* Virtualized rows: react-aria's Virtualizer wraps each row in an absolutely-positioned
* [role="presentation"] element whose width equals the scroll container's clientWidth
* (which includes the list-box horizontal padding) while sitting inside the padded content
* box. Without this, rows overflow the right padding and the hover background reaches the
* popover edge. Subtract the horizontal padding (2 × p-1.5 = 12px) to keep rows aligned. */
[role="presentation"] > [data-slot="list-box-item"] {
width: calc(100% - var(--spacing) * 3);
}
/* As the popover closes fast in single-select mode, the default item indicator transition is not even visible - so we disable it only for single-select */
[data-slot="list-box"]:not([aria-multiselectable="true"]) [data-slot="list-box-item-indicator"],
[data-slot="list-box"]:not([aria-multiselectable="true"])
[data-slot="list-box-item-indicator"]
[data-slot="list-box-item-indicator--checkmark"] {
@apply transition-none;
}
/* Search Field styles */
[data-slot="search-field"] {
@apply px-3 py-1 outline-none;
}
/* Empty State */
[data-slot="empty-state"] {
@apply text-center text-sm text-overlay-foreground/60;
}
}
/* Popover dialog — mirrors Popover.Dialog focus management without a visible focus ring */
.autocomplete__popover-dialog {
@apply outline-none;
&:focus,
&:focus-visible,
&[data-focus-visible="true"] {
@apply outline-none;
}
}
/* Full width modifier */
.autocomplete--full-width {
@apply w-full;
}
.autocomplete__trigger--full-width {
@apply w-full;
}
/* Clear Button styles */
.autocomplete__clear-button {
@apply relative isolate inline-flex h-6 w-6 shrink-0 origin-center items-center justify-center rounded-xl p-1 text-muted select-none no-highlight;
@apply me-0 size-5 shrink-0 self-center bg-transparent;
/* Cursor */
cursor: var(--cursor-interactive);
@apply transform-gpu;
/* Transition only when visible - disappears immediately when cleared */
&:not([data-empty="true"]) {
transition: opacity 150ms var(--ease-smooth);
@apply motion-reduce:transition-none;
}
&[data-empty="true"] {
@apply pointer-events-none opacity-0;
}
[data-slot="autocomplete-clear-button-icon"] {
@apply size-3.5;
}
&:active,
&[data-pressed="true"] {
}
@media (hover: hover) {
&:hover,
&[data-hovered="true"] {
@apply bg-default-hover;
}
}
&:active,
&[data-pressed="true"] {
transform: scale(0.93);
}
}