mirror of
https://github.com/Nexoniarz/Niveus.git
synced 2026-11-03 12:44:26 +00:00
Niveus read flat: thin sheens, short shadows, opaque grey chrome and a painted canvas. This retunes values and component internals only; every class and token name is unchanged, so existing pages pick it up as is. - Canvas: a faint accent light falls across the page, so translucent layers have something to show through. - Surfaces: the surface sheen runs the full height (lit top, shaded foot); raised and filled sheens, highlights and bevels are stronger; the elevation scale is deeper and softer. - Translucency: cards, dialogs, the sidebar and tab tracks let some of the canvas through, with blur so busy grounds never read as texture; frosted and glass materials are clearer with more blur. - Filled buttons give off a soft glow of their own color. - Light fields are paper instead of grey wells that looked disabled. - Fixes: navigation bars wrap and lists scroll inside them instead of overflowing the page on phones; choice labels sit at control size; disabled ghost buttons no longer grow a filled well; the color-swatch rule is split so neither engine drops it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iMUTHrD8K9CSuHqsanje9
196 lines
5.9 KiB
CSS
196 lines
5.9 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;
|
|
|
|
/* The dialog sits over a dimmed, blurred page, so it can afford to let a
|
|
little of it through: it reads as a pane lifted off the page rather than
|
|
a card pasted over it. */
|
|
background-color: color-mix(in oklab, var(--nv-color-surface-elevated) 86%, transparent);
|
|
background-image: var(--nv-sheen-surface);
|
|
backdrop-filter: var(--nv-backdrop-frosted);
|
|
-webkit-backdrop-filter: var(--nv-backdrop-frosted);
|
|
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;
|
|
}
|