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/drawer/drawer.css

489 lines
19 KiB

/* ── Trigger ────────────────────────────────────────────────────────────── */
[data-drawer-trigger] {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
min-block-size: var(--control-height-md);
padding-inline: var(--space-3);
border: var(--border-width) solid var(--color-border-default);
border-radius: var(--radius-md);
background: var(--color-surface-raised);
color: var(--color-content-primary);
font: inherit;
font-weight: var(--font-weight-medium);
cursor: pointer;
}
[data-drawer-trigger]:hover {
background: var(--color-surface-overlay);
}
[data-drawer-trigger]:focus-visible {
outline: var(--focus-ring-width) solid var(--focus-ring-color);
outline-offset: var(--focus-ring-offset);
}
[data-drawer-trigger]:disabled,
[data-drawer-trigger][data-disabled] {
cursor: default;
opacity: var(--opacity-disabled);
}
/* ── Overlay ────────────────────────────────────────────────────────────── */
/* Default: hidden. Only `variant='overlay'` paints the backdrop — the
* `'inline'` and `'persistent'` variants don't have a backdrop concept.
* The wrapper skips Overlay entirely in those modes; this rule guards
* the case where a consumer renders `<Drawer.Overlay />` explicitly
* inside the compound API with a non-overlay variant. */
[data-drawer-overlay] {
display: none;
}
[data-drawer-overlay][data-variant='overlay'] {
display: block;
position: fixed;
inset: 0;
background: color-mix(
in srgb,
var(--drawer-overlay-bg) var(--drawer-overlay-opacity),
transparent
);
backdrop-filter: blur(var(--drawer-overlay-blur));
z-index: var(--drawer-overlay-z);
opacity: 1;
}
/* Overlay enter/exit motion comes from the motion system (preset `fade` via
* `data-animation-style`). The overlay is `display:none` for inline/persistent,
* so the fade only shows for `variant='overlay'`. Timing preserved via override. */
[data-drawer-overlay] {
--motion-duration-enter: var(--duration-normal);
--motion-duration-exit: var(--duration-normal);
}
/* ── Content (variant-shared base) ──────────────────────────────────────── */
[data-drawer-content] {
--_drawer-size-width: var(--drawer-content-width-md);
--_drawer-size-height: var(--drawer-content-height-md);
--_drawer-panel-width: var(--_drawer-content-width-override, var(--_drawer-size-width));
--_drawer-panel-height: var(--_drawer-content-height-override, var(--_drawer-size-height));
--_drawer-padding: var(--drawer-content-padding-md);
display: flex;
flex-direction: column;
gap: var(--drawer-stack-gap);
padding: var(--_drawer-padding);
overflow: auto;
min-inline-size: var(--_drawer-content-min-width-override, auto);
max-inline-size: var(--_drawer-content-max-width-override, none);
min-block-size: var(--_drawer-content-min-height-override, auto);
max-block-size: var(--_drawer-content-max-height-override, none);
/* Elevation bundle (surface · border · shadow) from the `modal` plane via
`data-depth` (A1, Decisión 8). Keeps its own radius/color; the
[data-dragging] box-shadow override below still wins while dragging. */
border-radius: var(--drawer-content-radius);
color: var(--drawer-content-color);
outline: none;
}
/* Drag-state visual feedback. While the gesture is in flight (`data-dragging`
* is set on Content by the runtime from the gesture state), the panel lifts
* with a stronger shadow and gains a subtle outline ring. Animation is short
* (no jank) and additive — does not alter layout. */
[data-drawer-content][data-dragging] {
box-shadow: var(--drawer-content-shadow-dragging);
outline: var(--drawer-content-ring-dragging-width) solid var(--drawer-content-ring-dragging-color);
outline-offset: var(--drawer-content-ring-dragging-offset);
}
/* Reserved slot for exit tint on the leading edge — colored when the
* morfo's prewrite has written `data-last-action` and `data-state` flips
* to 'closed'. The leading edge depends on side: top→bottom, right→left,
* etc. (the edge facing the user before closing). Only meaningful in
* overlay/inline variants where the drawer animates out. */
[data-drawer-content][data-side='right'] {
border-left: var(--border-width-thick) solid transparent;
}
[data-drawer-content][data-side='left'] {
border-right: var(--border-width-thick) solid transparent;
}
[data-drawer-content][data-side='top'] {
border-bottom: var(--border-width-thick) solid transparent;
}
[data-drawer-content][data-side='bottom'] {
border-top: var(--border-width-thick) solid transparent;
}
/* ── Content sizing (variant-shared) ────────────────────────────────────── */
[data-drawer-content][data-size='sm'] {
--_drawer-size-width: var(--drawer-content-width-sm);
--_drawer-size-height: var(--drawer-content-height-sm);
--_drawer-padding: var(--drawer-content-padding-sm);
}
[data-drawer-content][data-size='md'] {
--_drawer-size-width: var(--drawer-content-width-md);
--_drawer-size-height: var(--drawer-content-height-md);
--_drawer-padding: var(--drawer-content-padding-md);
}
[data-drawer-content][data-size='lg'] {
--_drawer-size-width: var(--drawer-content-width-lg);
--_drawer-size-height: var(--drawer-content-height-lg);
--_drawer-padding: var(--drawer-content-padding-lg);
}
[data-drawer-content][data-size='full'] {
--_drawer-padding: var(--drawer-content-padding-full);
}
/* ── Variant 'overlay' — fixed positioning + backdrop stack ─────────────── */
[data-drawer-content][data-variant='overlay'] {
position: fixed;
z-index: calc(var(--drawer-overlay-z) + 1);
}
[data-drawer-content][data-variant='overlay'][data-side='right'] {
top: var(--drawer-content-inset);
right: var(--drawer-content-inset);
bottom: var(--drawer-content-inset);
width: min(calc(100vw - (var(--drawer-content-inset) * 2)), var(--_drawer-panel-width));
}
[data-drawer-content][data-variant='overlay'][data-side='left'] {
top: var(--drawer-content-inset);
left: var(--drawer-content-inset);
bottom: var(--drawer-content-inset);
width: min(calc(100vw - (var(--drawer-content-inset) * 2)), var(--_drawer-panel-width));
}
[data-drawer-content][data-variant='overlay'][data-side='top'] {
top: var(--drawer-content-inset);
left: var(--drawer-content-inset);
right: var(--drawer-content-inset);
height: min(
calc(100dvh - (var(--drawer-content-inset) * 2)),
var(--_drawer-panel-height)
);
}
[data-drawer-content][data-variant='overlay'][data-side='bottom'] {
right: var(--drawer-content-inset);
bottom: var(--drawer-content-inset);
left: var(--drawer-content-inset);
height: min(
calc(100dvh - (var(--drawer-content-inset) * 2)),
var(--_drawer-panel-height)
);
}
[data-drawer-content][data-variant='overlay'][data-size='full'] {
inset: var(--drawer-content-inset);
width: var(--_drawer-content-width-override, auto);
height: var(--_drawer-content-height-override, auto);
}
/* ── Variant 'inline' — fixed positioning, no backdrop, lower stack ─────── */
/* Same geometry as overlay but a separate z-index band so it doesn't
* fight a real modal that may also be on screen. The drawer sits over
* content (background remains interactive — soma drops the focus trap
* and dismissal layers in this variant). */
[data-drawer-content][data-variant='inline'] {
position: fixed;
z-index: var(--drawer-inline-z);
}
[data-drawer-content][data-variant='inline'][data-side='right'] {
top: var(--drawer-content-inset);
right: var(--drawer-content-inset);
bottom: var(--drawer-content-inset);
width: min(calc(100vw - (var(--drawer-content-inset) * 2)), var(--_drawer-panel-width));
}
[data-drawer-content][data-variant='inline'][data-side='left'] {
top: var(--drawer-content-inset);
left: var(--drawer-content-inset);
bottom: var(--drawer-content-inset);
width: min(calc(100vw - (var(--drawer-content-inset) * 2)), var(--_drawer-panel-width));
}
[data-drawer-content][data-variant='inline'][data-side='top'] {
top: var(--drawer-content-inset);
left: var(--drawer-content-inset);
right: var(--drawer-content-inset);
height: min(
calc(100dvh - (var(--drawer-content-inset) * 2)),
var(--_drawer-panel-height)
);
}
[data-drawer-content][data-variant='inline'][data-side='bottom'] {
right: var(--drawer-content-inset);
bottom: var(--drawer-content-inset);
left: var(--drawer-content-inset);
height: min(
calc(100dvh - (var(--drawer-content-inset) * 2)),
var(--_drawer-panel-height)
);
}
[data-drawer-content][data-variant='inline'][data-size='full'] {
inset: var(--drawer-content-inset);
width: var(--_drawer-content-width-override, auto);
height: var(--_drawer-content-height-override, auto);
}
/* ── Variant 'persistent' — in flow, no insets ──────────────────────────── */
/* Persistent drawer takes its slot in a parent flex/grid layout (the
* consumer's responsibility). The recipe just sizes the panel along
* the drawer's main axis; the cross axis fills the parent (`100%`). */
[data-drawer-content][data-variant='persistent'] {
position: relative;
border-radius: 0;
}
[data-drawer-content][data-variant='persistent'][data-side='left'],
[data-drawer-content][data-variant='persistent'][data-side='right'] {
width: var(--_drawer-panel-width);
block-size: 100%;
}
[data-drawer-content][data-variant='persistent'][data-side='top'],
[data-drawer-content][data-variant='persistent'][data-side='bottom'] {
inline-size: 100%;
height: var(--_drawer-panel-height);
}
[data-drawer-content][data-variant='persistent'][data-size='full'] {
width: 100%;
height: 100%;
}
/* ── Presence motion (overlay + inline) ─────────────────────────────────
* Driven by the motion system: the eidos wrapper writes
* `data-animation-style='slide-full'` and the registry's side-aware preset
* (full slide, keyed on `data-side`) animates enter/exit. The panel is INSET
* by `--drawer-content-inset`, so it must travel its own size PLUS that gap to
* fully clear the edge — `--motion-slide-leave` feeds the preset that extra.
* See `eidos-motion.md` (momento --state). */
[data-drawer-content] {
--motion-slide-leave: var(--drawer-content-inset);
--motion-duration-enter: var(--duration-moderate);
--motion-duration-exit: var(--duration-normal);
}
/* `persistent` is always present — it opts out of the presence animation. */
[data-drawer-content][data-variant='persistent'] {
animation: none !important;
}
/* ── data-last-action exit tinting (overlay + inline only) ──────────────── */
[data-drawer-content][data-state='closed'][data-side='right'][data-last-action='saved'] { border-left-color: var(--color-fulfill-element); }
[data-drawer-content][data-state='closed'][data-side='left'][data-last-action='saved'] { border-right-color: var(--color-fulfill-element); }
[data-drawer-content][data-state='closed'][data-side='top'][data-last-action='saved'] { border-bottom-color: var(--color-fulfill-element); }
[data-drawer-content][data-state='closed'][data-side='bottom'][data-last-action='saved'] { border-top-color: var(--color-fulfill-element); }
[data-drawer-content][data-state='closed'][data-side='right'][data-last-action='failed'] { border-left-color: var(--color-threat-element); }
[data-drawer-content][data-state='closed'][data-side='left'][data-last-action='failed'] { border-right-color: var(--color-threat-element); }
[data-drawer-content][data-state='closed'][data-side='top'][data-last-action='failed'] { border-bottom-color: var(--color-threat-element); }
[data-drawer-content][data-state='closed'][data-side='bottom'][data-last-action='failed'] { border-top-color: var(--color-threat-element); }
[data-drawer-content][data-state='closed'][data-side='right'][data-last-action='cancelled'],
[data-drawer-content][data-state='closed'][data-side='right'][data-last-action='dismissed'],
[data-drawer-content][data-state='closed'][data-side='right'][data-last-action='dismissed-outside'] { border-left-color: var(--color-neutral-element); }
[data-drawer-content][data-state='closed'][data-side='left'][data-last-action='cancelled'],
[data-drawer-content][data-state='closed'][data-side='left'][data-last-action='dismissed'],
[data-drawer-content][data-state='closed'][data-side='left'][data-last-action='dismissed-outside'] { border-right-color: var(--color-neutral-element); }
[data-drawer-content][data-state='closed'][data-side='top'][data-last-action='cancelled'],
[data-drawer-content][data-state='closed'][data-side='top'][data-last-action='dismissed'],
[data-drawer-content][data-state='closed'][data-side='top'][data-last-action='dismissed-outside'] { border-bottom-color: var(--color-neutral-element); }
[data-drawer-content][data-state='closed'][data-side='bottom'][data-last-action='cancelled'],
[data-drawer-content][data-state='closed'][data-side='bottom'][data-last-action='dismissed'],
[data-drawer-content][data-state='closed'][data-side='bottom'][data-last-action='dismissed-outside'] { border-top-color: var(--color-neutral-element); }
/* ── Handle (canon part — air didn't ship a handle) ─────────────────────── */
/* The handle is a small grab bar at the leading edge of the drawer
* (typically the only edge meant to be dragged). Orientation flips
* with `data-side`: horizontal bar for top/bottom (sheet pattern),
* vertical bar for left/right. Snap-point cycling handled by soma. */
[data-drawer-handle] {
flex: 0 0 auto;
background: var(--drawer-handle-bg);
border-radius: var(--drawer-handle-radius);
cursor: grab;
touch-action: none;
transition:
background var(--drawer-handle-transition-duration) var(--drawer-handle-transition-ease),
transform var(--drawer-handle-transition-duration) var(--drawer-handle-transition-ease);
}
[data-drawer-handle]:active {
cursor: grabbing;
}
/* Active feedback while the gesture is live. `data-dragging` lives on
* Content (set by the runtime from the Gesture state) — descendant
* selector reaches into the handle. Brighter color + small bump signals
* that the handle is the load-bearing element of the gesture. */
[data-drawer-content][data-dragging] [data-drawer-handle] {
background: var(--drawer-handle-bg-active);
cursor: grabbing;
}
/* Cursor persistence during drag: the Gesture layer captures the pointer,
* which removes the handle from the element-under-cursor → `:active` is
* lost and the cursor reverts to default. Hold `grabbing` on the entire
* Content while data-dragging is on, AND on `body` via `:has()` so that
* drags extending past the drawer edges still show the right cursor. */
[data-drawer-content][data-dragging] {
cursor: grabbing;
}
body:has([data-drawer-content][data-dragging]) {
cursor: grabbing;
}
/* Bump axis depends on which dimension is the "thickness". For top/bottom
* drawers the handle is horizontal — scale Y; for left/right it's vertical
* — scale X. */
[data-drawer-content][data-dragging][data-side='top'] [data-drawer-handle],
[data-drawer-content][data-dragging][data-side='bottom'] [data-drawer-handle] {
transform: scaleY(var(--drawer-handle-active-scale));
}
[data-drawer-content][data-dragging][data-side='left'] [data-drawer-handle],
[data-drawer-content][data-dragging][data-side='right'] [data-drawer-handle] {
transform: translateY(-50%) scaleX(var(--drawer-handle-active-scale));
}
/* For top/bottom drawers: horizontal grabber centered on the leading edge */
[data-drawer-content][data-side='top'] [data-drawer-handle],
[data-drawer-content][data-side='bottom'] [data-drawer-handle] {
width: var(--drawer-handle-length);
height: var(--drawer-handle-thickness);
margin-inline: auto;
}
/* For top: handle sits at the bottom edge (the "pulled" edge)
* For bottom: at the top edge */
[data-drawer-content][data-side='top'] [data-drawer-handle] {
order: 999;
margin-block-start: auto;
}
[data-drawer-content][data-side='bottom'] [data-drawer-handle] {
order: -1;
margin-block-end: auto;
}
/* For left/right drawers: vertical grabber */
[data-drawer-content][data-side='left'] [data-drawer-handle],
[data-drawer-content][data-side='right'] [data-drawer-handle] {
width: var(--drawer-handle-thickness);
height: var(--drawer-handle-length);
margin-block: auto;
position: absolute;
top: 50%;
transform: translateY(-50%);
}
[data-drawer-content][data-side='left'] [data-drawer-handle] {
right: var(--space-2);
}
[data-drawer-content][data-side='right'] [data-drawer-handle] {
left: var(--space-2);
}
/* ── Header / Footer / Title / Description ──────────────────────────────── */
[data-drawer-header] {
display: flex;
align-items: start;
justify-content: space-between;
gap: var(--drawer-header-gap);
}
[data-drawer-footer] {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: end;
gap: var(--drawer-footer-gap);
margin-top: var(--drawer-footer-margin-top);
}
[data-drawer-title] {
margin: 0;
font-family: var(--drawer-title-font-family);
font-size: var(--drawer-title-font-size);
font-weight: var(--drawer-title-font-weight);
line-height: var(--drawer-title-line-height);
color: var(--drawer-title-color);
text-wrap: balance;
}
[data-drawer-description] {
margin: 0;
font-family: var(--drawer-description-font-family);
font-size: var(--drawer-description-font-size);
line-height: var(--drawer-description-line-height);
color: var(--drawer-description-color);
}
/* ── Close button ───────────────────────────────────────────────────────── */
[data-drawer-close] {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
min-inline-size: var(--drawer-close-size);
block-size: var(--drawer-close-size);
padding-inline: var(--drawer-close-px);
border: var(--drawer-close-border-width) solid var(--drawer-close-border);
border-radius: var(--drawer-close-radius);
background: var(--drawer-close-bg);
color: var(--drawer-close-color);
cursor: pointer;
transition:
background var(--drawer-close-transition-duration) var(--drawer-close-transition-ease),
color var(--drawer-close-transition-duration) var(--drawer-close-transition-ease),
border-color var(--drawer-close-transition-duration) var(--drawer-close-transition-ease),
box-shadow var(--drawer-close-transition-duration) var(--drawer-close-transition-ease);
}
[data-drawer-close][data-icon-only] {
inline-size: var(--drawer-close-size);
padding-inline: 0;
}
[data-drawer-close]:hover {
background: var(--drawer-close-bg-hover);
color: var(--drawer-close-color-hover);
}
[data-drawer-close]:focus-visible {
outline: none;
box-shadow:
0 0 0 var(--drawer-close-focus-ring-offset) var(--drawer-content-bg),
0 0 0 calc(var(--drawer-close-focus-ring-offset) + var(--drawer-close-focus-ring-width))
var(--drawer-close-focus-ring-color);
}
[data-drawer-close]:disabled,
[data-drawer-close][data-disabled] {
cursor: default;
opacity: var(--drawer-close-disabled-opacity);
}

Powered by TurnKey Linux.