You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/eidos/components/toast/toast.css

335 lines
12 KiB

/**
* Eidos — Toast component recipe.
*
* Translated from `air/components/toast/toast.css` with substantive
* remapping: air's part naming and intent vocabulary differ from the
* Toast morfo. Air's token model still maps to the same conceptual parts, so
* we keep the values and rebind the selectors.
*
* Selector remap:
* air `[data-toast-toaster]` → eidos `[data-toast-viewport]`
* air `[data-toast]` (the card) → eidos `[data-toast-item]`
* air `[data-toast-message]` → DROPPED (soma has Title + Description
* directly; no separate "message" part)
* air `.air-toast-status` → DROPPED (no status icon part)
* air `.air-toast-main` → DROPPED (no content wrapper part)
*
* Intent vocabulary remap (air `data-type` ↔ morfo `data-intent`):
* air `error` → eidos `threat`
* air `warning` → eidos `risk`
* air `success` → eidos `fulfill`
* air `info` → eidos `neutral`
* no air equiv → eidos `affirm` (added below as success-tinted)
*
* `data-position` on viewport is a-la-carte: the morfo doesn't declare
* it, but a consumer can add it to enable air's positioning rules.
*/
/* ── Viewport ─────────────────────────────────────────────────────────── */
[data-toast-viewport] {
position: fixed;
z-index: var(--toast-toaster-z);
display: flex;
flex-direction: column;
gap: var(--toast-toaster-gap);
inline-size: min(
calc(100vw - (var(--toast-toaster-inset) * 2)),
var(--toast-toaster-width)
);
pointer-events: none;
}
[data-toast-viewport][data-position='top-left'] {
top: var(--toast-toaster-inset);
left: var(--toast-toaster-inset);
}
[data-toast-viewport][data-position='top-center'] {
top: var(--toast-toaster-inset);
left: 50%;
transform: translateX(-50%);
}
[data-toast-viewport][data-position='top-right'] {
top: var(--toast-toaster-inset);
right: var(--toast-toaster-inset);
}
[data-toast-viewport][data-position='bottom-left'] {
bottom: var(--toast-toaster-inset);
left: var(--toast-toaster-inset);
}
[data-toast-viewport][data-position='bottom-center'] {
bottom: var(--toast-toaster-inset);
left: 50%;
transform: translateX(-50%);
}
[data-toast-viewport][data-position='bottom-right'] {
right: var(--toast-toaster-inset);
bottom: var(--toast-toaster-inset);
}
/* ── Item (the card itself) ──────────────────────────────────────────── */
[data-toast-item] {
--_toast-surface: var(--toast-default-surface);
--_toast-border: var(--toast-default-border);
--_toast-accent: var(--toast-default-accent);
--_toast-status-bg: var(--toast-default-status-bg);
--_toast-status-color: var(--toast-default-status-color);
--_toast-action-color: var(--toast-default-action-color);
--_toast-action-hover-bg: var(--toast-default-action-hover-bg);
inline-size: min(100%, var(--toast-provider-max-width));
min-inline-size: min(100%, var(--toast-provider-min-width));
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
align-items: start;
gap: var(--toast-provider-gap);
padding: var(--toast-provider-py) var(--toast-provider-px);
border: var(--toast-provider-border-width) solid var(--_toast-border);
border-inline-start: var(--toast-accent-width) solid var(--_toast-accent);
border-radius: var(--toast-provider-radius);
background: var(--_toast-surface);
box-shadow: var(--toast-provider-shadow);
color: var(--toast-message-color);
pointer-events: auto;
opacity: 1;
transform: translateY(0) scale(1);
transition:
opacity var(--toast-transition-duration) var(--toast-transition-ease),
transform var(--toast-transition-duration) var(--toast-transition-ease);
}
/* Auto-collapse the 3-column grid based on which side parts are present.
* Status icon on the leading side, Close on the trailing side; if either
* is missing, the main column expands. */
[data-toast-item]:not(:has([data-toast-status])):not(:has([data-toast-close])) {
grid-template-columns: minmax(0, 1fr);
}
[data-toast-item]:not(:has([data-toast-status])):has([data-toast-close]) {
grid-template-columns: minmax(0, 1fr) auto;
}
[data-toast-item]:has([data-toast-status]):not(:has([data-toast-close])) {
grid-template-columns: auto minmax(0, 1fr);
}
/* Toast intent recipes — each maps to its doctrinal palette directly. */
[data-toast-item][data-intent='affirm'] {
--_toast-surface: var(--toast-affirm-surface);
--_toast-border: var(--toast-affirm-border);
--_toast-accent: var(--toast-affirm-accent);
--_toast-status-bg: var(--toast-affirm-status-bg);
--_toast-status-color: var(--toast-affirm-status-color);
--_toast-action-color: var(--toast-affirm-action-color);
--_toast-action-hover-bg: var(--toast-affirm-action-hover-bg);
}
[data-toast-item][data-intent='fulfill'] {
--_toast-surface: var(--toast-fulfill-surface);
--_toast-border: var(--toast-fulfill-border);
--_toast-accent: var(--toast-fulfill-accent);
--_toast-status-bg: var(--toast-fulfill-status-bg);
--_toast-status-color: var(--toast-fulfill-status-color);
--_toast-action-color: var(--toast-fulfill-action-color);
--_toast-action-hover-bg: var(--toast-fulfill-action-hover-bg);
}
[data-toast-item][data-intent='risk'] {
--_toast-surface: var(--toast-risk-surface);
--_toast-border: var(--toast-risk-border);
--_toast-accent: var(--toast-risk-accent);
--_toast-status-bg: var(--toast-risk-status-bg);
--_toast-status-color: var(--toast-risk-status-color);
--_toast-action-color: var(--toast-risk-action-color);
--_toast-action-hover-bg: var(--toast-risk-action-hover-bg);
}
[data-toast-item][data-intent='threat'] {
--_toast-surface: var(--toast-threat-surface);
--_toast-border: var(--toast-threat-border);
--_toast-accent: var(--toast-threat-accent);
--_toast-status-bg: var(--toast-threat-status-bg);
--_toast-status-color: var(--toast-threat-status-color);
--_toast-action-color: var(--toast-threat-action-color);
--_toast-action-hover-bg: var(--toast-threat-action-hover-bg);
}
[data-toast-item][data-intent='loss'] {
--_toast-surface: var(--toast-loss-surface);
--_toast-border: var(--toast-loss-border);
--_toast-accent: var(--toast-loss-accent);
--_toast-status-bg: var(--toast-loss-status-bg);
--_toast-status-color: var(--toast-loss-status-color);
--_toast-action-color: var(--toast-loss-action-color);
--_toast-action-hover-bg: var(--toast-loss-action-hover-bg);
}
/* neutral default — uses neutral palette (no override needed unless
* the recipe wants a specific neutral toast styling). */
[data-toast-item][data-state='closed'] {
opacity: 0;
transform: translateY(var(--toast-closed-offset)) scale(var(--toast-closed-scale));
pointer-events: none;
}
/* Loading state — subtle pulse on the accent stripe so the user reads
* "still working" without the toast competing for attention. */
[data-toast-item][data-loading] {
border-inline-start-color: var(--_toast-status-color);
animation: toast-loading-pulse 1.4s ease-in-out infinite;
}
@keyframes toast-loading-pulse {
0%, 100% { border-inline-start-color: var(--_toast-status-color); }
50% { border-inline-start-color: var(--_toast-accent); }
}
/* Swipe states — track the cursor / touch as the user pulls to dismiss.
* The Item's transform is updated by soma; the recipe just darkens the
* accent so the user gets stronger feedback as they near the threshold. */
[data-toast-item][data-swipe='start'],
[data-toast-item][data-swipe='move'] {
transition: none;
user-select: none;
cursor: grabbing;
}
[data-toast-item][data-swipe='cancel'] {
transition:
transform calc(var(--toast-transition-duration) * 1.5) var(--toast-transition-ease),
opacity var(--toast-transition-duration) var(--toast-transition-ease);
transform: translateY(0) scale(1);
}
[data-toast-item][data-swipe='end'] {
pointer-events: none;
}
/* Status icon disc — air's `air-toast-status` continuation. The wrapper
* reads `data-intent` from the Item and tints the disc via the cascade
* variables already set on Item. */
[data-toast-status] {
display: inline-flex;
align-items: center;
justify-content: center;
inline-size: var(--toast-status-size);
block-size: var(--toast-status-size);
border-radius: var(--toast-status-radius);
background: var(--_toast-status-bg);
color: var(--_toast-status-color);
flex: 0 0 auto;
}
/* Loading spinner — when the Item carries `data-loading`, the status
* disc spins. Uses an inline border trick so it doesn't depend on an
* extra child element. */
[data-toast-item][data-loading] [data-toast-status] {
background: transparent;
border: 2px solid color-mix(in srgb, var(--_toast-status-color) 30%, transparent);
border-block-start-color: var(--_toast-status-color);
animation: toast-spinner 0.9s linear infinite;
}
@keyframes toast-spinner {
to { transform: rotate(360deg); }
}
/* Title + description should NOT be column 1 when there's no status —
* but the auto-collapse rule above already collapses to a single column,
* so the main slot just fills. We add a wrapper rule in case a consumer
* uses an explicit `<div data-toast-main>` to group title+description. */
[data-toast-main] {
display: grid;
gap: var(--toast-provider-stack-gap);
min-inline-size: 0;
}
/* ── Title / Description / Action / Close ──────────────────────────── */
[data-toast-title] {
margin: 0;
font-family: var(--toast-title-font-family);
font-size: var(--toast-title-font-size);
font-weight: var(--toast-title-font-weight);
line-height: var(--toast-title-line-height);
color: var(--toast-title-color);
}
[data-toast-description] {
margin: 0;
font-family: var(--toast-description-font-family);
font-size: var(--toast-description-font-size);
line-height: var(--toast-description-line-height);
color: var(--toast-description-color);
}
[data-toast-action] {
justify-self: start;
margin-top: var(--toast-action-margin-top);
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--toast-action-gap);
padding: var(--toast-action-py) var(--toast-action-px);
border: var(--toast-action-border-width) solid currentColor;
border-radius: var(--toast-action-radius);
background: transparent;
color: var(--_toast-action-color);
font-family: var(--toast-action-font-family);
font-size: var(--toast-action-font-size);
font-weight: var(--toast-action-font-weight);
line-height: var(--toast-action-line-height);
cursor: pointer;
transition:
background var(--toast-transition-duration) var(--toast-transition-ease),
color var(--toast-transition-duration) var(--toast-transition-ease),
box-shadow var(--toast-transition-duration) var(--toast-transition-ease);
}
[data-toast-action]:hover {
background: var(--_toast-action-hover-bg);
}
[data-toast-action]:focus-visible {
outline: none;
box-shadow:
0 0 0 var(--toast-action-focus-ring-offset) var(--_toast-surface),
0 0 0 calc(var(--toast-action-focus-ring-offset) + var(--toast-action-focus-ring-width))
var(--toast-action-focus-ring-color);
}
[data-toast-close] {
display: inline-flex;
align-items: center;
justify-content: center;
inline-size: var(--toast-close-size);
block-size: var(--toast-close-size);
flex: 0 0 auto;
padding: 0;
border: var(--toast-close-border-width) solid var(--toast-close-border);
border-radius: var(--toast-close-radius);
background: var(--toast-close-bg);
color: var(--toast-close-color);
cursor: pointer;
transition:
background var(--toast-transition-duration) var(--toast-transition-ease),
border-color var(--toast-transition-duration) var(--toast-transition-ease),
color var(--toast-transition-duration) var(--toast-transition-ease),
box-shadow var(--toast-transition-duration) var(--toast-transition-ease);
}
[data-toast-close]:hover {
background: var(--toast-close-hover-bg);
border-color: var(--toast-close-hover-border);
color: var(--toast-close-hover-color);
}
[data-toast-close]:focus-visible {
outline: none;
box-shadow:
0 0 0 var(--toast-close-focus-ring-offset) var(--_toast-surface),
0 0 0 calc(var(--toast-close-focus-ring-offset) + var(--toast-close-focus-ring-width))
var(--toast-close-focus-ring-color);
}

Powered by TurnKey Linux.