mirror of
https://github.com/Nexoniarz/Niveus.git
synced 2026-11-03 12:44:26 +00:00
Selection markers, tab panels, brand changes and theme changes no longer use a view transition. The page snapshot could not blur what sits behind frosted glass, so every frosted surface went clear for the length of the change. nv.transition now measures the marker before and after, and animates the marker itself (FLIP); arriving content animates on its own; colours fade on their own surfaces. Verified in Firefox: no view transition is ever started. Time picker: wheels take every pointer type (mouse, pen, touch) with touch-action: none, so a phone, a browser's phone mode and a desktop behave alike. The rubber-band is capped at about 40% of a row (a whole row made the neighbour look selected). Each wheel has its own frame slot, so one wheel can no longer swallow another's last scroll event. Switching 24H / AM / PM happens when the wheel settles, and the hours animate into the new layout. Carousel arrows are frosted glass, as anything floating over content should be. At phone width the top bar keeps to one row and its link list scrolls. GUIDELINES.md records the new cross-engine rules. check.py, build.py and build.py --check pass. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
144 lines
4.5 KiB
CSS
144 lines
4.5 KiB
CSS
/* ==========================================================================
|
|
Niveus — Scroller
|
|
--------------------------------------------------------------------------
|
|
<div class="nv-scroller-frame" data-nv-scroller>
|
|
<div class="nv-scroller"> …cards… </div>
|
|
<button class="nv-button nv-button--icon" data-nv-scroll="prev">…</button>
|
|
<button class="nv-button nv-button--icon" data-nv-scroll="next">…</button>
|
|
</div>
|
|
|
|
A row of content that continues past its frame. Two things make that
|
|
readable rather than merely clipped: the edges fade, so the row visibly runs
|
|
under them instead of being cut, and snapping lands each item at the same
|
|
place every time — so moving left and right returns you to positions you
|
|
have already seen rather than to arbitrary offsets.
|
|
|
|
Scrolling works with no script at all: wheel, trackpad, touch and keyboard
|
|
are the browser's. The buttons are an affordance for pointers that have no
|
|
horizontal axis, and niveus.js wires them.
|
|
========================================================================== */
|
|
|
|
.nv-scroller-frame {
|
|
position: relative;
|
|
}
|
|
|
|
.nv-scroller {
|
|
--nv-scroller-item: 18rem;
|
|
--nv-scroller-fade: var(--nv-space-8);
|
|
|
|
display: flex;
|
|
gap: var(--nv-space-4);
|
|
overflow-x: auto;
|
|
overscroll-behavior-inline: contain;
|
|
|
|
/* Room for items to lift on hover without being clipped by the scroll box. */
|
|
padding-block: var(--nv-space-2);
|
|
|
|
scroll-snap-type: inline mandatory;
|
|
scroll-padding-inline: var(--nv-space-1);
|
|
scroll-behavior: smooth;
|
|
scrollbar-width: none;
|
|
|
|
/* The row runs under its own edges rather than stopping at them — but only
|
|
on the side where there is actually more to see. A fade at an end the row
|
|
has already reached is a lie about the content. niveus.js marks the ends;
|
|
with no script both sides fade, which is the safe default. */
|
|
mask-image: linear-gradient(
|
|
to right,
|
|
transparent,
|
|
black var(--nv-scroller-fade),
|
|
black calc(100% - var(--nv-scroller-fade)),
|
|
transparent
|
|
);
|
|
}
|
|
|
|
.nv-scroller-frame[data-nv-at-start] .nv-scroller {
|
|
mask-image: linear-gradient(
|
|
to right,
|
|
black calc(100% - var(--nv-scroller-fade)),
|
|
transparent
|
|
);
|
|
}
|
|
|
|
.nv-scroller-frame[data-nv-at-end] .nv-scroller {
|
|
mask-image: linear-gradient(to right, transparent, black var(--nv-scroller-fade));
|
|
}
|
|
|
|
.nv-scroller-frame[data-nv-at-start][data-nv-at-end] .nv-scroller {
|
|
mask-image: none;
|
|
}
|
|
|
|
.nv-scroller::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
.nv-scroller > * {
|
|
flex: 0 0 auto;
|
|
inline-size: min(var(--nv-scroller-item), 78%);
|
|
scroll-snap-align: start;
|
|
}
|
|
|
|
/* Items that should size to their content rather than to the track. */
|
|
.nv-scroller--auto > * {
|
|
inline-size: auto;
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.nv-scroller {
|
|
scroll-behavior: auto;
|
|
}
|
|
}
|
|
|
|
/* --- Controls ------------------------------------------------------------
|
|
Centred on the row's block axis and pulled half outside it, so they read as
|
|
belonging to the frame rather than floating over the content. */
|
|
|
|
.nv-scroller-frame [data-nv-scroll] {
|
|
position: absolute;
|
|
inset-block-start: 50%;
|
|
z-index: 1;
|
|
|
|
/* Read by .nv-button--float, which keeps it through hover and press. */
|
|
--nv-button-offset: 0 -50%;
|
|
}
|
|
|
|
/* The arrows float over the cards, so they are glass: frosted, with the content
|
|
passing blurred beneath them, rather than an opaque key hiding what it sits
|
|
on. This is the system's rule for anything that floats over content. */
|
|
.nv-scroller-frame [data-nv-scroll] {
|
|
--_bg: var(--nv-material-frosted-bg);
|
|
--_bg-hover: var(--nv-material-frosted-bg);
|
|
--_bg-active: var(--nv-material-frosted-bg);
|
|
--_border: var(--nv-material-frosted-border);
|
|
--_sheen: none;
|
|
backdrop-filter: var(--nv-backdrop-frosted);
|
|
-webkit-backdrop-filter: var(--nv-backdrop-frosted);
|
|
}
|
|
|
|
/* Belt and braces: the frame knows these controls float, so their centring
|
|
survives even if the markup forgets --float. */
|
|
.nv-scroller-frame [data-nv-scroll],
|
|
.nv-scroller-frame [data-nv-scroll]:hover,
|
|
.nv-scroller-frame [data-nv-scroll]:active {
|
|
translate: var(--nv-button-offset);
|
|
}
|
|
|
|
.nv-scroller-frame [data-nv-scroll="prev"] {
|
|
inset-inline-start: calc(var(--nv-space-4) * -1);
|
|
}
|
|
|
|
.nv-scroller-frame [data-nv-scroll="next"] {
|
|
inset-inline-end: calc(var(--nv-space-4) * -1);
|
|
}
|
|
|
|
/* At either end the control has nothing left to do, and says so. */
|
|
.nv-scroller-frame [data-nv-scroll]:disabled {
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
@media (hover: none) {
|
|
.nv-scroller-frame [data-nv-scroll] {
|
|
display: none;
|
|
}
|
|
}
|