Touch & Mobile Viewports
Two topics that stay invisible on desktop and only show up on real devices.
Touch Devices Have No Hover
A tap on iPhone/Android doesn't reliably trigger :hover rules - a button whose only visual feedback is a :hover state therefore feels completely dead on touch. Every visually significant :hover rule in ncss is therefore wrapped in @media (hover: hover) and (pointer: fine) (only devices with a real pointing device), never unconditional. :focus-visible (keyboard focus) always stays ungated by this.
Tactile Feedback: :active Instead of :hover
For tactile feedback on touch, :active applies (works for every pointing device including touch, no gate needed) - .ncss-btn has its own, component-wide :active state (scale: 0.95) and every color variant additionally gets a real color change on :active (the same values as :hover, just unconditional).
A tap often lasts only ~100-150ms - the normal transition duration (200ms) frequently doesn't even reach its target value in that time. A dedicated token --ncss-motion-duration-fast (100ms), overridden only as transition-duration on the :active rule itself - the entry into the pressed look is thereby fast, the spring-back stays at the normal, softer duration.
For a custom element: .ncss-press (helpers/animations.css, likewise :active-based with the same fast entry, combinable with any element).
<div class="ncss-card ncss-press">Any element with tap feedback</div>
Full Screen Height on Mobile
100vh/height: 100% correspond on mobile browsers to the largest possible height (address bar hidden) - if the address bar is currently visible, a 100vh element extends beyond the actually visible area.
| Unit | Behavior | Use in ncss |
|---|---|---|
svh | Smallest possible value, always fits | Modal, off-canvas, .ncss-sticky-container - elements that must stay guaranteed visible without scrolling (with vh as fallback: height: 100vh; height: 100svh;) |
lvh | Identical to classic vh - stable, doesn't change while scrolling | components/scroll-stack.css - scroll-linked animations need timing that doesn't get recalculated mid-scroll |
dvh | Recalculates live as soon as the address bar shows/hides | Deliberately NOT used for scroll-linked animations (known jank behavior in mobile Safari) |
.ncss-full-height {
height: 100vh; /* fallback for older browsers */
height: 100svh; /* wins where supported - always fits */
}