Utilities
Interactivity
Create interactivity utilities for cursor styles, pointer events, user selection, and scroll behavior with full type safety.
Overview
Interactivity utilities help you control how users interact with elements including cursor styles, pointer events, text selection, scroll behavior, and touch actions.
Why Use Interactivity Utilities?
Interactivity utilities help you:
- Enhance user feedback: Provide visual cues through cursor changes
- Control interactions: Manage which elements respond to pointer events
- Improve accessibility: Support various input methods including touch
- Customize scrolling: Create smooth scroll experiences
useCursorUtility
The useCursorUtility() function creates utility classes for setting cursor styles.
styleframe.config.ts
import { styleframe } from "styleframe";
import { useCursorUtility } from "@styleframe/theme";
const s = styleframe();
useCursorUtility(s, {
auto: 'auto',
default: 'default',
pointer: 'pointer',
wait: 'wait',
text: 'text',
move: 'move',
help: 'help',
'not-allowed': 'not-allowed',
none: 'none',
grab: 'grab',
grabbing: 'grabbing',
'zoom-in': 'zoom-in',
'zoom-out': 'zoom-out',
});
export default s;
styleframe/index.css
._cursor\:auto { cursor: auto; }
._cursor\:default { cursor: default; }
._cursor\:pointer { cursor: pointer; }
._cursor\:wait { cursor: wait; }
._cursor\:text { cursor: text; }
._cursor\:move { cursor: move; }
._cursor\:help { cursor: help; }
._cursor\:not-allowed { cursor: not-allowed; }
._cursor\:none { cursor: none; }
._cursor\:grab { cursor: grab; }
._cursor\:grabbing { cursor: grabbing; }
._cursor\:zoom-in { cursor: zoom-in; }
._cursor\:zoom-out { cursor: zoom-out; }
<button class="_cursor:pointer">Clickable button</button>
<div class="_cursor:not-allowed">Disabled area</div>
<div class="_cursor:grab">Draggable element</div>
usePointerEventsUtility
Control whether an element responds to pointer events.
styleframe.config.ts
import { styleframe } from "styleframe";
import { usePointerEventsUtility } from "@styleframe/theme";
const s = styleframe();
usePointerEventsUtility(s, {
none: 'none',
auto: 'auto',
});
export default s;
styleframe/index.css
._pointer-events\:none { pointer-events: none; }
._pointer-events\:auto { pointer-events: auto; }
<div class="_pointer-events:none">Click passes through</div>
<div class="_pointer-events:auto">Normal click behavior</div>
useUserSelectUtility
Control text selection behavior.
styleframe.config.ts
import { styleframe } from "styleframe";
import { useUserSelectUtility } from "@styleframe/theme";
const s = styleframe();
useUserSelectUtility(s, {
none: 'none',
text: 'text',
all: 'all',
auto: 'auto',
});
export default s;
styleframe/index.css
._user-select\:none { user-select: none; }
._user-select\:text { user-select: text; }
._user-select\:all { user-select: all; }
._user-select\:auto { user-select: auto; }
<button class="_user-select:none">Can't select this text</button>
<code class="_user-select:all">Click to select all</code>
useScrollBehaviorUtility
Control scroll animation behavior.
styleframe.config.ts
import { styleframe } from "styleframe";
import { useScrollBehaviorUtility } from "@styleframe/theme";
const s = styleframe();
useScrollBehaviorUtility(s, {
auto: 'auto',
smooth: 'smooth',
});
export default s;
styleframe/index.css
._scroll-behavior\:auto { scroll-behavior: auto; }
._scroll-behavior\:smooth { scroll-behavior: smooth; }
<html class="_scroll-behavior:smooth">
<div class="_scroll-behavior:auto">Instant scrolling container</div>
useTouchActionUtility
Control how touch interactions are handled.
styleframe.config.ts
import { styleframe } from "styleframe";
import { useTouchActionUtility } from "@styleframe/theme";
const s = styleframe();
useTouchActionUtility(s, {
auto: 'auto',
none: 'none',
'pan-x': 'pan-x',
'pan-left': 'pan-left',
'pan-right': 'pan-right',
'pan-y': 'pan-y',
'pan-up': 'pan-up',
'pan-down': 'pan-down',
pinchZoom: 'pinch-zoom',
manipulation: 'manipulation',
});
export default s;
styleframe/index.css
._touch-action\:auto { touch-action: auto; }
._touch-action\:none { touch-action: none; }
._touch-action\:pan-x { touch-action: pan-x; }
._touch-action\:pan-y { touch-action: pan-y; }
._touch-action\:manipulation { touch-action: manipulation; }
/* ... more values */
<div class="_touch-action:none">Disable all touch gestures</div>
<button class="_touch-action:manipulation">Fast tap response</button>
More Interactivity Utilities
useAccentColorUtility
Set the accent color for form controls.
import { useAccentColorUtility } from "@styleframe/theme";
useAccentColorUtility(s, {
auto: 'auto',
primary: '#006cff',
});
useCaretColorUtility
Set the color of the text input caret.
import { useCaretColorUtility } from "@styleframe/theme";
useCaretColorUtility(s, {
primary: '#006cff',
transparent: 'transparent',
});
useResizeUtility
Control whether an element is resizable.
import { useResizeUtility } from "@styleframe/theme";
useResizeUtility(s, {
none: 'none',
y: 'vertical',
x: 'horizontal',
both: 'both',
});
useAppearanceUtility
Control native browser styling on form elements.
import { useAppearanceUtility } from "@styleframe/theme";
useAppearanceUtility(s, {
none: 'none',
auto: 'auto',
});
useWillChangeUtility
Hint to browsers about expected changes for optimization.
import { useWillChangeUtility } from "@styleframe/theme";
useWillChangeUtility(s, {
auto: 'auto',
scroll: 'scroll-position',
contents: 'contents',
transform: 'transform',
});
Examples
Disabled Button State
styleframe.config.ts
import { styleframe } from "styleframe";
import { useCursorUtility, usePointerEventsUtility, useUserSelectUtility } from "@styleframe/theme";
const s = styleframe();
const { modifier } = s;
const disabled = modifier('disabled', ({ declarations }) => ({
'&:disabled': declarations,
}));
useCursorUtility(s, { 'not-allowed': 'not-allowed' }, [disabled]);
usePointerEventsUtility(s, { none: 'none' }, [disabled]);
useUserSelectUtility(s, { none: 'none' });
export default s;
styleframe/index.css
._disabled\:cursor\:not-allowed:disabled { cursor: not-allowed; }
._disabled\:pointer-events\:none:disabled { pointer-events: none; }
._user-select\:none { user-select: none; }
Smooth Scroll Navigation
styleframe.config.ts
import { styleframe } from "styleframe";
import { useScrollBehaviorUtility } from "@styleframe/theme";
const s = styleframe();
const { selector } = s;
useScrollBehaviorUtility(s, { smooth: 'smooth' });
// Apply to html element for page-wide smooth scrolling
selector('html', {
scrollBehavior: 'smooth',
});
export default s;
styleframe/index.css
._scroll-behavior\:smooth { scroll-behavior: smooth; }
html {
scroll-behavior: smooth;
}
Best Practices
- Use semantic cursors: Match cursor to the action (pointer for clickable, grab for draggable)
- Respect user preferences: Some users prefer reduced motion; use smooth scroll thoughtfully
- Don't disable user select universally: Only prevent selection where it makes sense (buttons, drag handles)
- Test touch interactions: Touch actions affect mobile usability significantly
- Use will-change sparingly: It consumes memory; only use when you know changes will happen
FAQ
Use it for overlay elements that shouldn't block clicks (like decorative elements), elements that should be visually present but not interactive, or elements during loading/disabled states.
It enables panning and zooming but disables other non-standard gestures like double-tap zoom. This is useful for buttons and interactive elements where you want to avoid the 300ms click delay on mobile.
Generally yes, preventing text selection on buttons improves the interaction feel. Users don't expect to select button text. However, ensure this doesn't interfere with accessibility.
Yes,
scroll-behavior: smooth affects both anchor link navigation and JavaScript methods like scrollIntoView() and window.scrollTo(). Use { behavior: 'instant' } in JavaScript if you need to override it.