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

336 lines
6.2 KiB
CSS

/* -------------------------------------------------------------------------------------------------
* Modal Component Styles
* Block: modal
* -----------------------------------------------------------------------------------------------*/
/**
* Element: trigger - the element that opens the modal
*/
.modal__trigger {
@apply inline-block cursor-(--cursor-interactive);
/**
* Transitions
* CRITICAL: motion-reduce must be AFTER transition for correct override specificity
*/
transition:
transform 250ms var(--ease-out-quart),
background-color 150ms var(--ease-smooth),
box-shadow 150ms var(--ease-out);
@apply motion-reduce:transition-none;
/* Focus state */
&:focus-visible:not(:focus),
&[data-focus-visible="true"] {
@apply status-focused;
}
/* Disabled state */
&:disabled,
&[aria-disabled="true"] {
@apply status-disabled;
}
/* Active/pressed state */
&:active,
&[data-pressed="true"] {
transform: scale(0.97);
}
}
/**
* Element: backdrop - the overlay behind the dialog
*/
.modal__backdrop {
@apply fixed inset-0 z-50;
@apply flex flex-row items-center justify-center;
@apply h-(--visual-viewport-height) w-full;
/* Entering animation */
&[data-entering="true"] {
@apply animate-in duration-150 ease-out fade-in-0;
}
/* Exiting animation */
&[data-exiting="true"] {
@apply animate-out duration-100 ease-out fade-out-0;
}
/**
* Reduce Animation
* CRITICAL: motion-reduce must be AFTER animate for correct override specificity
*/
&[data-exiting="true"],
&[data-entering="true"] {
@apply will-change-[opacity];
@apply motion-reduce:animate-none;
}
}
/**
* Modifier: backdrop--transparent
*/
.modal__backdrop--transparent {
@apply bg-transparent;
}
/**
* Modifier: backdrop--opaque
*/
.modal__backdrop--opaque {
@apply bg-backdrop;
}
/**
* Modifier: backdrop--blur
*/
.modal__backdrop--blur {
@apply bg-backdrop backdrop-blur-md;
}
/**
* Element: container - the positioning wrapper for the modal
*/
.modal__container {
@apply flex flex-col items-center;
@apply h-(--visual-viewport-height) w-full min-w-0 flex-1;
@apply p-4;
@apply sm:w-fit sm:p-10;
@apply pointer-events-none;
/* Entering animation */
&[data-entering="true"] {
@apply animate-in duration-250 ease-out-quad fade-in-0 zoom-in-105;
&[data-placement="auto"] {
@apply slide-in-from-bottom-1;
@apply sm:slide-in-from-bottom-0;
}
&[data-placement="top"] {
@apply slide-in-from-top-1;
}
&[data-placement="center"] {
@apply slide-in-from-top-0;
}
&[data-placement="bottom"] {
@apply slide-in-from-bottom-1;
}
}
/* Exiting animation */
&[data-exiting="true"] {
@apply animate-out duration-100 ease-out-quad fade-out-0 zoom-out-95;
}
/**
* Reduce Animation
* CRITICAL: motion-reduce must be AFTER animate for correct override specificity
*/
&[data-exiting="true"],
&[data-entering="true"] {
@apply will-change-[opacity,transform];
@apply motion-reduce:animate-none;
}
}
/**
* Modifier: container--scroll-outside
*/
.modal__container--scroll-outside {
@apply scrollbar overflow-y-auto;
@apply pointer-events-auto;
-webkit-overflow-scrolling: touch;
}
/**
* Modifier: container--full
*/
.modal__container--full {
@apply p-0 sm:p-0;
/* Entering animation */
&[data-entering="true"] {
@apply slide-in-from-bottom-0 zoom-in-100;
@apply sm:slide-in-from-bottom-0;
}
/* Exiting animation */
&[data-exiting="true"] {
@apply zoom-out-100;
}
}
/**
* Element: dialog - the content container for the modal
*/
.modal__dialog {
@apply relative;
@apply flex w-full flex-col;
@apply bg-overlay shadow-overlay outline-none;
border-radius: min(32px, var(--radius-3xl));
@apply p-6;
@apply pointer-events-auto;
&[data-placement="auto"] {
@apply mt-auto;
@apply sm:my-auto;
}
&[data-placement="center"] {
@apply my-auto;
}
&[data-placement="bottom"] {
@apply mt-auto;
}
&[data-placement="top"] {
@apply mt-0;
}
}
/**
* Modifier: dialog--scroll-inside
*/
.modal__dialog--scroll-inside {
@apply max-h-full min-h-0 overflow-clip;
}
/**
* Modifier: dialog--scroll-outside
*/
.modal__dialog--scroll-outside {
@apply h-auto;
@apply min-h-0 shrink-0;
}
/**
* Modifier: dialog--xs
*/
.modal__dialog--xs {
@apply max-w-xs;
}
/**
* Modifier: dialog--sm
*/
.modal__dialog--sm {
@apply max-w-sm;
}
/**
* Modifier: dialog--md
*/
.modal__dialog--md {
@apply max-w-md;
}
/**
* Modifier: dialog--lg
*/
.modal__dialog--lg {
@apply max-w-lg;
}
/**
* Modifier: dialog--cover
*/
.modal__dialog--cover {
@apply h-full min-h-full w-full;
}
/**
* Modifier: dialog--full
*/
.modal__dialog--full {
@apply h-full min-h-full w-full;
@apply rounded-none shadow-none;
}
/**
* Element: header - the top section with the title
*/
.modal__header {
@apply flex flex-col gap-3;
@apply mb-0;
/* When header contains icon, use flex-col layout with gap */
& > .modal__icon {
@apply mb-0;
}
}
/**
* Element: heading - the main title text
*/
.modal__heading {
@apply align-middle;
@apply text-base font-medium text-foreground;
}
/**
* Element: icon - the optional icon indicator
*/
.modal__icon {
@apply flex items-center justify-center;
@apply size-10 shrink-0;
@apply rounded-3xl select-none;
}
/**
* Element: body - the main content area
*/
.modal__body {
@apply min-h-0 flex-1;
@apply text-sm leading-[1.43] text-muted;
@apply -m-[3px] my-0 overflow-visible p-[3px];
}
/**
* Modifier: body--scroll-inside
*/
.modal__body--scroll-inside {
@apply scrollbar overflow-y-auto overscroll-contain;
-webkit-overflow-scrolling: touch;
}
/**
* Modifier: body--scroll-outside
*/
.modal__body--scroll-outside {
@apply overflow-y-visible;
}
/**
* Element: footer - the bottom action section
*/
.modal__footer {
@apply flex flex-row items-center justify-end gap-2;
@apply mt-0;
}
/**
* Element: close-trigger - the optional x icon button to close the modal
*/
.modal__close-trigger {
@apply absolute top-4 right-4;
}
/**
* Elements Spacing
*/
.modal__header + .modal__body {
@apply mt-2;
}
.modal__header + .modal__footer {
@apply mt-5;
}
.modal__body + .modal__footer {
@apply mt-5;
}