Essential utility functions and React hooks for building accessible React Aria UI components
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Browser and device identification utilities for conditional behavior and platform-specific features. All functions use caching for performance and return boolean values.
Utilities for detecting Apple devices and macOS platforms.
/**
* Detects macOS platform
* @returns true if running on macOS
*/
function isMac(): boolean;
/**
* Detects iPhone devices
* @returns true if running on iPhone
*/
function isIPhone(): boolean;
/**
* Detects iPad devices (handles iPadOS 13+ detection)
* @returns true if running on iPad, including iPadOS 13+ devices
*/
function isIPad(): boolean;
/**
* Detects iOS devices (iPhone or iPad)
* @returns true if running on any iOS device
*/
function isIOS(): boolean;
/**
* Detects any Apple device (Mac or iOS)
* @returns true if running on any Apple device
*/
function isAppleDevice(): boolean;Usage Examples:
import { isMac, isIOS, isIPad } from "@react-aria/utils";
function PlatformSpecificComponent() {
const shortcutKey = isMac() ? "⌘" : "Ctrl";
const hasHover = !isIOS(); // iOS devices don't have true hover
return (
<div>
<p>Press {shortcutKey}+K to open search</p>
{hasHover && <button className="hover-effects">Hover me</button>}
{isIPad() && <p>iPad-specific feature available!</p>}
</div>
);
}
// Keyboard shortcut handling
function useKeyboardShortcuts() {
useEffect(() => {
const handleKeyDown = (e) => {
const cmdOrCtrl = isMac() ? e.metaKey : e.ctrlKey;
if (cmdOrCtrl && e.key === 'k') {
e.preventDefault();
openSearch();
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, []);
}Utilities for detecting specific browsers and browser engines.
/**
* Detects WebKit browsers (Safari, excluding Chrome)
* @returns true if running on WebKit-based browser (not Chrome)
*/
function isWebKit(): boolean;
/**
* Detects Chrome browser
* @returns true if running on Chrome browser
*/
function isChrome(): boolean;
/**
* Detects Firefox browser
* @returns true if running on Firefox browser
*/
function isFirefox(): boolean;Usage Examples:
import { isWebKit, isChrome, isFirefox } from "@react-aria/utils";
function BrowserSpecificBehavior() {
useEffect(() => {
// WebKit has different scrolling behavior
if (isWebKit()) {
document.body.style.webkitOverflowScrolling = 'touch';
}
// Chrome-specific optimizations
if (isChrome()) {
// Enable hardware acceleration for Chrome
document.body.style.transform = 'translateZ(0)';
}
// Firefox-specific handling
if (isFirefox()) {
// Firefox handles some CSS properties differently
document.documentElement.style.scrollBehavior = 'smooth';
}
}, []);
return <div>Browser-optimized content</div>;
}
// CSS-in-JS with browser detection
function getButtonStyles() {
return {
padding: '8px 16px',
borderRadius: isWebKit() ? '8px' : '4px', // Different radius for Safari
boxShadow: isChrome()
? '0 2px 4px rgba(0,0,0,0.1)'
: '0 1px 3px rgba(0,0,0,0.1)'
};
}Utilities for detecting mobile platforms and devices.
/**
* Detects Android devices
* @returns true if running on Android device
*/
function isAndroid(): boolean;Usage Examples:
import { isAndroid, isIOS } from "@react-aria/utils";
function MobileOptimizedComponent() {
const isMobile = isIOS() || isAndroid();
const touchOptimized = isMobile;
return (
<button
style={{
minHeight: touchOptimized ? '44px' : '32px', // Larger touch targets
fontSize: isAndroid() ? '16px' : '14px' // Prevent zoom on Android
}}
>
{isMobile ? 'Tap me' : 'Click me'}
</button>
);
}
// Platform-specific input handling
function useInputBehavior() {
const [inputMode, setInputMode] = useState('none');
useEffect(() => {
if (isIOS()) {
setInputMode('decimal'); // Better numeric input on iOS
} else if (isAndroid()) {
setInputMode('numeric'); // Android prefers numeric mode
} else {
setInputMode('none'); // Desktop doesn't need input mode
}
}, []);
return inputMode;
}Real-world examples combining multiple platform checks:
import { isMac, isIOS, isAndroid, isWebKit, isChrome } from "@react-aria/utils";
function PlatformAwareComponent() {
// Determine if device supports true hover
const hasHover = !isIOS() && !isAndroid();
// Determine optimal scroll behavior
const scrollBehavior = useMemo(() => {
if (isIOS()) return 'momentum'; // iOS has native momentum scrolling
if (isAndroid()) return 'smooth'; // Android prefers smooth scrolling
return 'auto'; // Desktop default
}, []);
// Platform-specific event handling
const handleClick = useCallback((e) => {
// Prevent double-tap zoom on mobile
if (isIOS() || isAndroid()) {
e.preventDefault();
}
// Handle platform-specific actions
if (isMac()) {
// Mac-specific behavior
}
}, []);
return (
<div
onClick={handleClick}
style={{
cursor: hasHover ? 'pointer' : 'default',
WebkitOverflowScrolling: isIOS() ? 'touch' : undefined,
scrollBehavior: scrollBehavior
}}
>
Content optimized for {
isIOS() ? 'iOS' :
isAndroid() ? 'Android' :
isMac() ? 'macOS' :
'Desktop'
}
</div>
);
}
// Feature detection based on platform
function useFeatureSupport() {
return useMemo(() => ({
supportsHover: !isIOS() && !isAndroid(),
supportsTouch: isIOS() || isAndroid(),
prefersMomentumScrolling: isIOS(),
needsLargerTouchTargets: isIOS() || isAndroid(),
supportsBackdropFilter: isWebKit() || isChrome(),
usesCmdKey: isMac(),
supportsPassiveEvents: !isAndroid() // Some Android versions have issues
}), []);
}All platform detection functions use caching internally, so multiple calls to the same function within a render cycle are optimized. The detection is performed once and cached for subsequent calls:
// These calls are optimized - detection only happens once
const isMacPlatform = isMac();
const isIOSPlatform = isIOS();
const isMacAgain = isMac(); // Returns cached resultInstall with Tessl CLI
npx tessl i tessl/npm-react-aria--utils