Hit Area
Expand the clickable box of a control by a fixed amount, without moving anything on screen.
Ships with the spark styles (@epilot/spark-ui/styles), so there is no install
step. hit-area-* adds an absolutely positioned ::before with negative
insets, so the control claims presses outside its painted box while its layout,
size and position stay exactly as they were.
Unlike touch-target, hit-area-* applies on
every pointer type and expands by an amount you choose rather than up to a
minimum. Use it to close a dead zone between controls that are already large
enough, and touch-target to bring a small control up to the 44px floor on
touch.
Usage
| Class | Effect |
|---|---|
hit-area-<n> | expand by spacing step n on all sides |
hit-area-x-<n> / hit-area-y-<n> | expand on the horizontal / vertical axis |
hit-area-t-<n> … hit-area-l-<n> | expand one side (top, right, bottom, left) |
hit-area-[10px] | arbitrary value, any axis or side variant |
hit-area | the pseudo-element with no expansion |
hit-area-debug | paint the claimed box while you tune it |
<button className="hit-area-x-1 size-8 rounded-md">
<Delete />
</button>Values follow the spacing scale and resolve through --spacing, so an expansion
tracks [data-scaling] the same way the surrounding layout does. Fractional
steps work (hit-area-x-0.5 claims 2px per side at 100%).
Reach for a single side or axis rather than all four. A dead zone is usually directional: rows leave vertical gaps, toolbars leave horizontal ones, and expanding the axis that has no gap is how you end up stealing from a neighbor.
hit-area-debug paints the claimed box while you tune it, dashed blue at rest
and green on hover, as in the demo above. It is a development aid: take it off
before shipping.
Notes
- Split the gap, do not claim all of it. Two adjacent controls that each
expand by the full gap both claim the same pixels, and the one later in the
DOM wins the overlap. The tap boundary then sits against one control's edge
instead of between the two. Expanding each side by half the gap puts the
boundary at the midpoint, which is why
app-navuseshit-area-x-0.5into its 4px gap rather thanhit-area-x-1. - The expansion inherits
pointer-events. A control markedpointer-events-nonegets an inert expansion, so an ancestor that owns the press keeps it. - It can add scrollable overflow. The
::beforeis absolutely positioned and reaches outside the box, so inside a scroll container it can extend the scrollable area. Reserve it with margin or padding on the control. ::beforeis taken. The expansion uses::before, and so docheckbox,radioandswitchfor their own overlays.touch-targetuses::afterfor this reason, and the two can be combined on one element.- Verify by probing, not by reading the classes.
elementFromPointwalking outward from a control's center is the only way to confirm what it really owns, including which control wins an overlap.
Hit area utilities by Kian Bazarjani.