chakra-ui / zag

Finite state machines for building accessible design systems and UI components. Works with modern frameworks, and even just Vanilla JS
http://zagjs.com
MIT License
4k stars 163 forks source link

Feature request: hover utility #397

Closed mujahidfa closed 1 year ago

mujahidfa commented 1 year ago

🚀 Feature request

A framework-agnostic hover utility that handles pointer hover interactions for an element. Similar to useHover in React Aria.

🧱 Problem Statement / Justification

Same reason as to why the useHover hook in React Aria is needed; to address device consistency issues with the :hover pseudo class in CSS. Taken from React Aria's docs on useHover:

useHover is similar to the :hover pseudo class in CSS, but :hover is problematic on touch devices due to mouse emulation in mobile browsers. Depending on the browser and device, :hover may never apply, or may apply continuously until the user touches another element. useHover only applies when the pointer is truly capable of hovering, and emulated mouse events are ignored.

✅ Proposed solution or API

Usage (e.g. in Vue):

<script setup>
import * as hover from "@zag-js/hover";
import { normalizeProps, useMachine } from "@zag-js/vue";
import { computed } from "vue";

const [state, send] = useMachine(hover.machine({ id: "1" }));

const api = computed(() => hover.connect(state.value, send, normalizeProps));
</script>

<template>
  <button v-bind="api.hoverProps">
    <span v-if="api.isHovered">Hovered!</span>
    <span v-else>Press Me</span>
  </button>
</template>

Methods and Properties

Property Description
isHovered Whether the target is currently hovered.
const api = connect(state, send)
api.isHovered
// false

Handlers

The hover utility returns props that you should spread onto the target element:

Property Description
onHoverStart Handler that is called when a hover interaction starts.
onHoverEnd Handler that is called when a hover interaction ends.
onHoverChange Handler that is called when the hover state changes.

Each of these handlers is fired with a HoverEvent, which exposes information about the target and the type of event that triggered the interaction.

Property Description Type
type The type of hover event being fired. 'hoverstart', 'hoverend'
pointerType The pointer type that triggered the press event. 'mouse', 'pen'
target The target element of the hover event. HTMLElement

Styling guide

Hovered State

When the element is hovered, the data-hovered attribute is added to the target:

[data-hovered] {
  /* styles for hovered state */
}

Disabling the hover

To disable hover, set the context's disabled property to true:

const [state, send] = useMachine(
  hover.machine({
    disabled: true,
  }),
)

↩ī¸ Alternatives

An alternative is to simply use React Aria's useHover hook to handle hover interactions, but as the name suggests, it is not framework-agnostic and only supports React. The goal is to also support Vue and Solid. Also, it would be nice to have a Zag-native hover utility so that it works naturally with other Zag utilities (such as pressable).

📝 Additional Information

Inspired by the useHover hook in React Aria:

segunadebayo commented 1 year ago

After thinking about this some more, we don't think this should be added to Zag.js for now.

We'll consider it in the future.