mirror of
https://github.com/Nexoniarz/Niveus.git
synced 2026-11-03 20:54:25 +00:00
Bevelled controls with light falling across them, fields as wells, solid windows, and glass kept for the chrome that floats over everything else. Warm graphite in dark, soft paper in light, one brand color driving both. Ships its own fonts (Instrument Sans, JetBrains Mono; OFL). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
191 lines
5.6 KiB
CSS
191 lines
5.6 KiB
CSS
/* ==========================================================================
|
|
Niveus — Dialog
|
|
--------------------------------------------------------------------------
|
|
Built on the native <dialog> element, so focus trapping, the top layer,
|
|
Escape-to-close and inertness come from the platform rather than from
|
|
script.
|
|
|
|
<dialog class="nv-dialog"> … </dialog> opened with .showModal()
|
|
.nv-dialog--sheet enters from the block end
|
|
.nv-dialog--wide | --compact width
|
|
.nv-dialog__header / __body / __footer / __close
|
|
|
|
The dialog is the system's highest material: elevation 4 over a blurred
|
|
scrim. It enters by rising and settling, and leaves the way it came — the
|
|
motion is what tells the user the surface was always there, above the page.
|
|
========================================================================== */
|
|
|
|
.nv-dialog {
|
|
--_width: 30rem;
|
|
--_padding: var(--nv-space-6);
|
|
|
|
inline-size: min(var(--_width), calc(100vw - var(--nv-space-8)));
|
|
max-block-size: min(42rem, calc(100svh - var(--nv-space-12)));
|
|
padding: 0;
|
|
|
|
background-color: var(--nv-color-surface-elevated);
|
|
background-image: var(--nv-sheen-surface);
|
|
color: var(--nv-color-text-primary);
|
|
border: var(--nv-border-width) solid var(--nv-color-border-subtle);
|
|
border-radius: var(--nv-radius-xl);
|
|
box-shadow: var(--nv-bevel), var(--nv-outline), var(--nv-elevation-4);
|
|
|
|
overflow: hidden;
|
|
|
|
/* Closed state. [open] below is the resting state; @starting-style replays
|
|
these values for the entrance. */
|
|
/* A dialog should look as though it came from the control that opened it.
|
|
nv.openDialog() sets --nv-origin to that control's position relative to
|
|
the dialog, so the entrance scale expands away from the trigger rather
|
|
than from the middle of the screen. Centred when nothing sets it. */
|
|
transform-origin: var(--nv-origin, 50% 50%);
|
|
|
|
opacity: 0;
|
|
scale: 0.97;
|
|
translate: 0 var(--nv-travel-md);
|
|
|
|
transition:
|
|
opacity var(--nv-duration-normal) var(--nv-ease-standard),
|
|
scale var(--nv-duration-normal) var(--nv-ease-standard),
|
|
translate var(--nv-duration-normal) var(--nv-ease-standard),
|
|
display var(--nv-duration-normal) allow-discrete,
|
|
overlay var(--nv-duration-normal) allow-discrete;
|
|
}
|
|
|
|
.nv-dialog[open] {
|
|
opacity: 1;
|
|
scale: 1;
|
|
translate: 0;
|
|
transition-timing-function: var(--nv-ease-entrance);
|
|
}
|
|
|
|
@starting-style {
|
|
.nv-dialog[open] {
|
|
opacity: 0;
|
|
scale: 0.97;
|
|
translate: 0 var(--nv-travel-md);
|
|
}
|
|
}
|
|
|
|
.nv-dialog::backdrop {
|
|
background-color: var(--nv-color-scrim);
|
|
backdrop-filter: var(--nv-backdrop-scrim);
|
|
-webkit-backdrop-filter: var(--nv-backdrop-scrim);
|
|
opacity: 0;
|
|
transition:
|
|
opacity var(--nv-duration-normal) var(--nv-ease-standard),
|
|
display var(--nv-duration-normal) allow-discrete,
|
|
overlay var(--nv-duration-normal) allow-discrete;
|
|
}
|
|
|
|
.nv-dialog[open]::backdrop {
|
|
opacity: 1;
|
|
}
|
|
|
|
@starting-style {
|
|
.nv-dialog[open]::backdrop {
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
/* showModal() does not stop the page behind from scrolling. */
|
|
html:has(.nv-dialog:modal) {
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* --- Widths -------------------------------------------------------------------- */
|
|
|
|
.nv-dialog--compact { --_width: 24rem; }
|
|
.nv-dialog--wide { --_width: 44rem; }
|
|
|
|
/* --- Sheet ----------------------------------------------------------------------
|
|
Anchored to the block end and full width. On small screens this is usually
|
|
the right form for anything with more than two actions. */
|
|
|
|
.nv-dialog--sheet {
|
|
--_width: 34rem;
|
|
|
|
/* Flush to the viewport edges — a sheet with side gaps and square bottom
|
|
corners reads as a mistake rather than as a sheet. */
|
|
inline-size: min(var(--_width), 100vw);
|
|
max-block-size: min(46rem, calc(100svh - var(--nv-space-10)));
|
|
margin-block-end: 0;
|
|
margin-block-start: auto;
|
|
border-start-start-radius: var(--nv-radius-xl);
|
|
border-start-end-radius: var(--nv-radius-xl);
|
|
border-end-start-radius: 0;
|
|
border-end-end-radius: 0;
|
|
scale: 1;
|
|
translate: 0 var(--nv-travel-lg);
|
|
}
|
|
|
|
@starting-style {
|
|
.nv-dialog--sheet[open] {
|
|
scale: 1;
|
|
translate: 0 var(--nv-travel-lg);
|
|
}
|
|
}
|
|
|
|
/* --- Parts ------------------------------------------------------------------------ */
|
|
|
|
.nv-dialog__header {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: var(--nv-space-4);
|
|
padding: var(--_padding);
|
|
padding-block-end: var(--nv-space-3);
|
|
}
|
|
|
|
.nv-dialog__title {
|
|
font: var(--nv-font-heading);
|
|
font-size: var(--nv-font-size-xl);
|
|
letter-spacing: var(--nv-letter-spacing-tight);
|
|
margin: 0;
|
|
}
|
|
|
|
.nv-dialog__description {
|
|
font: var(--nv-font-body);
|
|
color: var(--nv-color-text-secondary);
|
|
margin-block-start: var(--nv-space-2);
|
|
}
|
|
|
|
.nv-dialog__body {
|
|
padding: var(--_padding);
|
|
padding-block: var(--nv-space-2);
|
|
overflow-y: auto;
|
|
overscroll-behavior: contain;
|
|
color: var(--nv-color-text-secondary);
|
|
}
|
|
|
|
.nv-dialog__footer {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
gap: var(--nv-space-2);
|
|
padding: var(--_padding);
|
|
padding-block-start: var(--nv-space-4);
|
|
}
|
|
|
|
/* A footer over scrollable content needs its own ground: the inset tint and
|
|
hairline tell the user there is more above it. */
|
|
.nv-dialog__footer--divided {
|
|
background-color: var(--nv-color-background-subtle);
|
|
border-block-start: var(--nv-border-width) solid var(--nv-color-border-subtle);
|
|
}
|
|
|
|
.nv-dialog__close {
|
|
flex: none;
|
|
margin: calc(var(--nv-space-1) * -1);
|
|
}
|
|
|
|
/* A dialog laid out as a column keeps its header and footer fixed while only
|
|
the body scrolls. */
|
|
.nv-dialog:has(.nv-dialog__body) {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.nv-dialog:not([open]) {
|
|
display: none;
|
|
}
|