Interaction states, responsive polish & a smoother docs experience
· Engineering
This release is about the details you feel rather than see on a feature list: how every control responds to a press, a keyboard, or a touch — and how the docs themselves behave across screens. Hover is only one interaction state; a professional component treats pointer, keyboard, and touch as first-class.
A library-wide interaction sweep
Section titled “A library-wide interaction sweep”We audited all 63 components against the full interaction hierarchy — not just hover. The foundation was already strong (disabled, focus-visible, keyboard, reflected state, reduced-motion, forced-colors were broadly covered), so this pass closed the real gaps:
- Touch-hover guard. Every visible
:hoverstyle is now wrapped in@media (hover: hover)so a tap on a phone never latches a hover state until you tap elsewhere. Where hover was fused into a:hover, :focus-visibleselector, we split them — keyboard focus stays ungated. - Press feedback.
:activefires for mouse, pen, and touch, so it’s the one affordance that works everywhere. Buttons, cards, tags, and menu items now give tactile feedback on press — menu items even mirror their highlight so a tap reads instantly. - Pointer over mouse. Outside-click dismissal in the color picker and editor
moved from
mousedowntopointerdownfor reliable touch behavior. The deliberatemousedown+preventDefaultidiom on remove/clear buttons was left intact — it exists to stop focus theft and already works cross-device.
<nana-button variant="primary">Press me</nana-button>
<nana-button variant="secondary">Secondary</nana-button>
<nana-tag removable>Removable</nana-tag>
<nana-icon-button label="Star"><nana-icon name="star"></nana-icon></nana-icon-button> Shared, not scattered
Section titled “Shared, not scattered”Rather than copy-paste the same rules into 63 files, the reusable pieces live in a
single src/styles/interactions.ts module (a pressable fragment and a
focusRing), composed into a component’s styles. On mixins: the library already
ships the right ones — Focusable, FormAssociatedMixin, NanaEventsMixin —
so we invested in wider adoption and shared styles instead of inventing a new
class hierarchy on a shipping library.
Everything honors prefers-reduced-motion, and because component styles live in
shadow DOM, a page-level motion reset can never leak into them.
Responsive docs, top to bottom
Section titled “Responsive docs, top to bottom”The documentation site now holds up from roughly 320px on up:
- Wide API tables scroll horizontally on narrow screens instead of pushing the whole page sideways.
- Display type, hero actions, stat strips, and the CTA scale and stack gracefully on small screens.
- Long URLs and union types wrap instead of overflowing.
Smoother by default
Section titled “Smoother by default”- Native smooth scrolling (
scroll-behavior: smooth+scroll-padding-top) eases anchor and table-of-contents jumps — no JavaScript, no dependency — and switches off underprefers-reduced-motion. - A centered top-bar search, sized as a real search field, so it’s easy to find at a glance.
Why this matters
Section titled “Why this matters”A component library earns trust in the small moments: the button that dips when you press it, the menu that doesn’t stick under your thumb, the page that never scrolls sideways on a phone. This release is a down-payment on that trust — and a standard we’ll hold new components to.