@snowball-ui/react-native
v0.3.0
Published
React Native runtime components and provider for Snowball UI.
Maintainers
Readme
@snowball-ui/react-native
Experimental React Native runtime for Snowball UI.
This package shares Snowball UI foundation contracts and theme values, but implements components with React Native primitives.
Install
pnpm add @snowball-ui/react-native react react-nativeInstall react-native-svg as well when using components that render SVG
primitives, such as Checkbox, SearchField, ListRow, icons, charts, or
keypads. The package declares it as an optional peer dependency so apps can
decide when to include it.
Install react-native-safe-area-context when using overlay components that
need device insets, such as BottomSheet or top-positioned Toast. Snowball
falls back to conservative inset defaults when it is absent, but real device
insets give more accurate placement.
Current peer compatibility is React 19 and React Native 0.81 or newer. The local Expo review app runs React Native 0.81.4 with React 19.1.
Basic Usage
import {
Button,
SnowballNativeThemeProvider,
TextField,
} from '@snowball-ui/react-native';
export function App() {
return (
<SnowballNativeThemeProvider>
<TextField label="Email" prefix="@" placeholder="[email protected]" />
<Button onPress={() => {}}>Continue</Button>
</SnowballNativeThemeProvider>
);
}For tighter Metro bundles, component subpaths are also published:
import { Button } from '@snowball-ui/react-native/button';
import { TextField } from '@snowball-ui/react-native/text-field';Custom Themes
SnowballNativeThemeProvider accepts documented theme names, custom semantic
theme objects, and scoped themeOverrides. React Native uses the same public
theme helpers as web, but components read values from context instead of CSS
variables.
import {
Button,
SnowballNativeThemeProvider,
createSemanticTheme,
extendSemanticTheme,
} from '@snowball-ui/react-native';
const productTheme = createSemanticTheme('product', {
action: {
primary: {
bg: '#123456',
fg: '#ffffff',
},
},
});
const campaignTheme = extendSemanticTheme(productTheme, 'product-campaign', {
bg: {
accent: {
weak: '#eef6ff',
},
},
});
export function App() {
return (
<SnowballNativeThemeProvider theme={campaignTheme}>
<Button onPress={() => {}}>Continue</Button>
</SnowballNativeThemeProvider>
);
}For smaller product adjustments, pass themeOverrides on a named or custom
base theme. Unspecified semantic roles continue to inherit from the base theme.
<SnowballNativeThemeProvider
theme="dark"
themeOverrides={{
action: {
primary: {
bg: '#123456',
fg: '#ffffff',
},
},
}}
>
<Routes />
</SnowballNativeThemeProvider>Interaction Feedback
SnowballNativeThemeProvider accepts an optional interactionFeedback adapter.
Snowball components call it for lightweight haptics while keeping the native
runtime dependency-free. Apps can wire it to expo-haptics, a custom native
module, or React Native Vibration.
For Expo apps, install expo-haptics before using the adapter below.
import * as Haptics from 'expo-haptics';
import { SnowballNativeThemeProvider } from '@snowball-ui/react-native';
export function App() {
return (
<SnowballNativeThemeProvider
interactionFeedback={{
haptics: {
impactSoft: () => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Soft),
impactLight: () => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light),
impactMedium: () => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium),
notificationWarning: () => Haptics.notificationAsync(
Haptics.NotificationFeedbackType.Warning,
),
selection: () => Haptics.selectionAsync(),
},
}}
>
<Routes />
</SnowballNativeThemeProvider>
);
}Prefer expo-haptics or a native haptics module for iOS. React Native
Vibration is best treated as a fallback because it cannot match the short,
native Taptic Engine feedback used by iOS controls.
Set interactionFeedback={{ hapticsEnabled: false }} to disable all component
haptics at the provider boundary. Missing handlers are ignored, and handler
errors never block the component action.
Gesture Coexistence
Slider supports gestureActivationThreshold for screens that also use
horizontal navigation gestures, such as native-stack swipe-to-close or
swipe-back. Keep the default for ordinary forms. On screens where the stack
gesture must remain available, pass a small threshold so Slider waits for clear
horizontal intent before claiming the responder.
<Slider gestureActivationThreshold={8} value={40} />TextField
TextField supports the shared Snowball input variants (box, line, big,
hero), sizes, labels, helper/error text, disabled/read-only states, and native
theme values. React Native also exposes parity helpers from the same compound
component:
import { TextField } from '@snowball-ui/react-native';
export function AccountFields() {
return (
<>
<TextField
label="Handle"
prefix="@"
suffix=".snow"
placeholder="username"
/>
<TextField.Clearable label="Search" defaultValue="snowball" />
<TextField.Password label="Password" />
<TextField.Button label="Bank" placeholder="Select bank" onPress={() => {}} />
</>
);
}Use prefix, suffix, and right for adornments. Use TextField.Clearable
for editable clear actions, TextField.Password for visibility toggles, and
TextField.Button for input-shaped selection controls.
Theme Values
React Native does not use CSS variables. Components read semantic color values
from theme.semantic, and consumers can use theme.styleTokens for numeric
radius, spacing, layer, interaction hit slop/target sizes, state opacity,
component anatomy, typography, and motion values. Import these runtime helpers from the public
package entrypoint.
import { Text, View } from 'react-native';
import {
resolveShadow,
useSnowballNativeTheme,
} from '@snowball-ui/react-native';
function Panel() {
const theme = useSnowballNativeTheme();
return (
<View
style={{
backgroundColor: theme.semantic.bg.surface.default,
borderRadius: theme.styleTokens.radius.lg,
padding: theme.styleTokens.spacing[5],
zIndex: theme.styleTokens.layer.raised,
...resolveShadow(theme.semantic.shadow.subtle),
}}
>
<Text style={{ color: theme.semantic.fg.text.primary }}>
Shared Snowball theme
</Text>
</View>
);
}Semantic tokens describe theme-dependent intent such as surface, text, border,
action, status, and shadow roles. Use theme.semantic for colors and semantic
shadow strings, and use theme.styleTokens for cross-platform numeric values
and themeable component effects such as state opacity, touch target sizing,
dialog overlay opacity, and Highlight spotlight opacity.
const dialogOverlayEffect =
theme.styleTokens.effect.dialog.native.overlay.strong;
const highlightOverlayEffect =
theme.styleTokens.effect.highlight.native.overlay;
const disabledOpacity = theme.styleTokens.state.opacity.disabled;
const defaultHitSlop = theme.styleTokens.interaction.hitSlop.default;
const buttonAnatomy = theme.styleTokens.anatomy.button.xlarge;
const dialogCardWidth = theme.styleTokens.anatomy.dialog.width;Use theme.styleTokens.effect.field.native.focusRing for custom native
input-like focus rings. Use nativeEffectStyleTokens only for platform effect
recipes that are not semantic theme roles, such as exact component shadow
recipes. Import it from the public package entrypoint when custom native
surfaces need to share those recipes.
import { nativeEffectStyleTokens } from '@snowball-ui/react-native';
const fieldFocusRingEffect = theme.styleTokens.effect.field.native.focusRing;
const menuShadowEffect = nativeEffectStyleTokens.shadow.menuDropdown;Status
Experimental. Current supported components:
AgreementAgreementV3AgreementV4AlertDialogAssetBadgeBarChartBoardRowBorderBottomCTAFixedBottomCTABottomInfoBottomSheetBubbleButtonCardCheckboxConfirmDialogDialogGridListHighlightIconButtonKeypadAlphabetKeypadNumberKeypadFullSecureKeypadListFooterListHeaderListRowListRowLegacyLoaderMenuModalNumericSpinnerParagraphPostProgressBarProgressStepperRatingResultSearchFieldSegmentedControlSkeletonSliderSplitTextFieldStepperStepperRowSwitchTabTabsTableRowTextAreaTextFieldTextButtonToastTooltipTop
License
MIT
