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
176 lines
5.2 KiB
CSS
176 lines
5.2 KiB
CSS
/* ==========================================================================
|
|
Niveus — Menu
|
|
--------------------------------------------------------------------------
|
|
<button class="nv-button nv-menu-trigger" popovertarget="actions"
|
|
style="--nv-anchor: --actions">Actions</button>
|
|
|
|
<div class="nv-menu" popover id="actions" style="--nv-anchor: --actions">
|
|
<button class="nv-menu__item">Rename</button>
|
|
<hr class="nv-menu__separator">
|
|
<button class="nv-menu__item nv-menu__item--danger">Delete</button>
|
|
</div>
|
|
|
|
A menu has a spatial relationship with the control that opened it: anchor
|
|
positioning ties it to the trigger, and it scales out of the trigger's edge
|
|
rather than appearing somewhere unrelated. Both sides name the same anchor
|
|
through --nv-anchor, so the pair stays obvious in the markup.
|
|
|
|
Light dismiss, Escape, and top-layer stacking come from the popover
|
|
attribute — none of it is scripted.
|
|
========================================================================== */
|
|
|
|
.nv-menu-trigger {
|
|
anchor-name: var(--nv-anchor);
|
|
}
|
|
|
|
.nv-menu {
|
|
position: fixed;
|
|
inset: auto;
|
|
margin: 0;
|
|
padding: var(--nv-space-1);
|
|
|
|
min-inline-size: 12rem;
|
|
max-inline-size: min(20rem, calc(100vw - var(--nv-space-8)));
|
|
max-block-size: min(24rem, calc(100svh - var(--nv-space-12)));
|
|
overflow-y: auto;
|
|
overscroll-behavior: contain;
|
|
|
|
/* Below the trigger, aligned to its inline-start edge, flipping to whichever
|
|
side has room. */
|
|
position-anchor: var(--nv-anchor);
|
|
position-area: block-end span-inline-end;
|
|
position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
|
|
margin-block-start: var(--nv-space-2);
|
|
|
|
background-color: var(--nv-material-frosted-bg);
|
|
background-image: var(--nv-sheen-surface);
|
|
border: var(--nv-border-width) solid var(--nv-material-frosted-border);
|
|
border-radius: var(--nv-radius-lg);
|
|
box-shadow: var(--nv-material-frosted-shadow);
|
|
backdrop-filter: var(--nv-backdrop-frosted);
|
|
-webkit-backdrop-filter: var(--nv-backdrop-frosted);
|
|
color: var(--nv-color-text-primary);
|
|
|
|
/* Grows from the edge nearest the trigger. */
|
|
transform-origin: center block-start;
|
|
|
|
opacity: 0;
|
|
scale: 0.96;
|
|
translate: 0 calc(var(--nv-travel-sm) * -1);
|
|
transition:
|
|
opacity var(--nv-duration-quick) var(--nv-ease-standard),
|
|
scale var(--nv-duration-quick) var(--nv-ease-entrance),
|
|
translate var(--nv-duration-quick) var(--nv-ease-entrance),
|
|
display var(--nv-duration-quick) allow-discrete,
|
|
overlay var(--nv-duration-quick) allow-discrete;
|
|
}
|
|
|
|
.nv-menu:popover-open {
|
|
opacity: 1;
|
|
scale: 1;
|
|
translate: 0;
|
|
}
|
|
|
|
@starting-style {
|
|
.nv-menu:popover-open {
|
|
opacity: 0;
|
|
scale: 0.96;
|
|
translate: 0 calc(var(--nv-travel-sm) * -1);
|
|
}
|
|
}
|
|
|
|
/* Without anchor positioning the menu cannot know where its trigger is, so it
|
|
falls back to the popover's own centring rather than landing somewhere
|
|
arbitrary. The relationship is lost; the usability is not. */
|
|
@supports not (anchor-name: --nv-test) {
|
|
.nv-menu {
|
|
inset: 0;
|
|
margin: auto;
|
|
transform-origin: center;
|
|
}
|
|
}
|
|
|
|
/* --- Items -------------------------------------------------------------------- */
|
|
|
|
.nv-menu__item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--nv-space-2-5);
|
|
inline-size: 100%;
|
|
padding: var(--nv-space-2) var(--nv-space-2-5);
|
|
|
|
background-color: transparent;
|
|
border: 0;
|
|
border-radius: var(--nv-radius-sm);
|
|
color: var(--nv-color-text-secondary);
|
|
font: var(--nv-font-label);
|
|
text-align: start;
|
|
text-decoration: none;
|
|
white-space: nowrap;
|
|
cursor: pointer;
|
|
transition: var(--nv-transition-colors);
|
|
}
|
|
|
|
@media (hover: hover) {
|
|
.nv-menu__item:hover:not(:disabled, [aria-disabled="true"]) {
|
|
background-color: var(--nv-color-surface-hover);
|
|
color: var(--nv-color-text-primary);
|
|
}
|
|
}
|
|
|
|
.nv-menu__item:focus-visible {
|
|
outline: none;
|
|
background-color: var(--nv-color-surface-hover);
|
|
color: var(--nv-color-text-primary);
|
|
box-shadow: var(--nv-focus-ring);
|
|
}
|
|
|
|
.nv-menu__item--danger {
|
|
color: var(--nv-color-danger-text);
|
|
}
|
|
|
|
@media (hover: hover) {
|
|
.nv-menu__item--danger:hover:not(:disabled, [aria-disabled="true"]) {
|
|
background-color: var(--nv-color-danger-subtle);
|
|
color: var(--nv-color-danger-text);
|
|
}
|
|
}
|
|
|
|
.nv-menu__item:disabled,
|
|
.nv-menu__item[aria-disabled="true"] {
|
|
color: var(--nv-color-text-disabled);
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.nv-menu__item[aria-checked="true"]::before {
|
|
content: "";
|
|
inline-size: 0.28em;
|
|
block-size: 0.55em;
|
|
margin-inline-end: var(--nv-space-1);
|
|
translate: 0 -0.12em;
|
|
rotate: 45deg;
|
|
border-inline-end: 2px solid var(--nv-color-accent);
|
|
border-block-end: 2px solid var(--nv-color-accent);
|
|
}
|
|
|
|
.nv-menu__shortcut {
|
|
margin-inline-start: auto;
|
|
padding-inline-start: var(--nv-space-4);
|
|
color: var(--nv-color-text-tertiary);
|
|
font: var(--nv-font-caption);
|
|
}
|
|
|
|
.nv-menu__separator {
|
|
border: 0;
|
|
border-block-start: var(--nv-border-width) solid var(--nv-color-border-subtle);
|
|
margin: var(--nv-space-1) 0;
|
|
}
|
|
|
|
.nv-menu__label {
|
|
padding: var(--nv-space-2) var(--nv-space-2-5) var(--nv-space-1);
|
|
font: var(--nv-font-caption);
|
|
font-weight: var(--nv-font-weight-semibold);
|
|
letter-spacing: var(--nv-letter-spacing-wider);
|
|
text-transform: uppercase;
|
|
color: var(--nv-color-text-tertiary);
|
|
}
|