src/skills/mobile-styling-unistyles/SKILL.md
Unistyles 3.0 styling - C++ powered StyleSheet superset with zero re-renders, theming, breakpoints, variants, dynamic functions, runtime values
npx skillsauth add agents-inc/skills mobile-styling-unistylesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Quick Guide: Unistyles 3.0 is a StyleSheet superset powered by Nitro Modules (C++/JSI). Import
StyleSheetfromreact-native-unistylesinstead ofreact-native-- same API, but with themes, breakpoints, variants, dynamic functions, and runtime values. Zero re-renders: styles update via the Shadow Tree, not React state. Configure withStyleSheet.configure()before anyStyleSheet.create(). Never spread styles ({...a, ...b}) -- use array syntax ([a, b]). Requires New Architecture (RN 0.78+).
<critical_requirements>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST import StyleSheet from react-native-unistyles, NOT from react-native -- the Unistyles version is a superset that enables all features)
(You MUST call StyleSheet.configure() BEFORE any StyleSheet.create() -- configure in your entry file before importing components)
(You MUST use array syntax [styles.a, styles.b] for merging styles -- NEVER spread {...styles.a, ...styles.b} as it destroys C++ state)
(You MUST NOT use useUnistyles hook in regular components -- it forces full re-renders, defeating Unistyles' zero-render architecture)
(You MUST pass only serializable arguments to dynamic functions -- strings, numbers, booleans, arrays, objects (no functions or components))
</critical_requirements>
Auto-detection: Unistyles, react-native-unistyles, StyleSheet.configure, UnistylesRuntime, UnistylesThemes, UnistylesBreakpoints, useVariants, compoundVariants, ScopedTheme, withUnistyles, useUnistyles, miniRuntime, rt.insets, rt.screen, mq.only, Display, Hide
When to use:
When NOT to use:
Key patterns covered:
Detailed Resources:
Unistyles 3.0 is a StyleSheet superset -- if you know React Native's StyleSheet.create, you know 80% of Unistyles. The remaining 20% is what makes it powerful: themes, responsive breakpoints, variants, and runtime values, all managed in C++ via JSI with zero React re-renders.
How it works:
Core principles:
When Unistyles adds value over plain StyleSheet:
Replace react-native import with react-native-unistyles. The create function accepts a callback with theme and rt (miniRuntime) for dynamic styles. Static styles (no theme/runtime) work identically to plain StyleSheet.
import { StyleSheet } from "react-native-unistyles";
const styles = StyleSheet.create((theme, rt) => ({
container: {
flex: 1,
backgroundColor: theme.colors.background,
paddingTop: rt.insets.top,
},
text: {
color: theme.colors.typography,
fontSize: rt.fontScale * 16,
},
// Static styles work exactly like plain StyleSheet
separator: {
height: StyleSheet.hairlineWidth,
backgroundColor: "#ccc",
},
}));
Why good: theme and rt are injected by the C++ core -- no hooks needed, no re-renders when theme or device values change
See examples/core.md for static vs themed vs runtime StyleSheets and the full miniRuntime property list.
Define style variations inside variants -- then select them with styles.useVariants(). Compound variants apply styles when multiple variant conditions are met simultaneously.
const styles = StyleSheet.create((theme) => ({
button: {
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 8,
variants: {
color: {
primary: { backgroundColor: theme.colors.primary },
secondary: { backgroundColor: theme.colors.secondary },
},
size: {
sm: { paddingHorizontal: 8, paddingVertical: 4 },
lg: { paddingHorizontal: 24, paddingVertical: 12 },
},
},
compoundVariants: [
{
color: "primary",
size: "lg",
styles: { borderWidth: 2, borderColor: theme.colors.accent },
},
],
},
}));
// In component -- call useVariants to select active variants
styles.useVariants({ color: "primary", size: "lg" });
Why good: eliminates conditional style objects, compound variants reduce complex if/else chains, TypeScript infers valid variant combinations
See examples/variants.md for boolean variants, default variants, the component props pattern with UnistylesVariants, and multi-style variants.
When styles depend on component-level values (not just theme/runtime), use dynamic functions. Arguments must be serializable (strings, numbers, booleans, arrays, objects).
const styles = StyleSheet.create((theme) => ({
card: (isHighlighted: boolean, index: number) => ({
backgroundColor: isHighlighted
? theme.colors.highlight
: theme.colors.surface,
opacity: index === 0 ? 1 : 0.8,
}),
}));
// In JSX -- call the function with arguments
<View style={styles.card(isHighlighted, index)} />
Why good: serializable arguments pass to C++ for native recalculation, full TypeScript inference on parameters
See examples/core.md for dynamic function patterns and the serializable constraint.
Define breakpoints in StyleSheet.configure, then use breakpoint objects or the mq utility in styles. At least one breakpoint must start at 0.
// In style definitions -- breakpoint object syntax
const styles = StyleSheet.create((theme) => ({
container: {
flexDirection: {
xs: "column",
md: "row",
},
padding: {
xs: 8,
sm: 16,
lg: 24,
},
},
}));
// Media query syntax for precise ranges
import { mq } from "react-native-unistyles";
const styles = StyleSheet.create(() => ({
sidebar: {
display: {
[mq.only.width(0, 768)]: "none",
[mq.only.width(768)]: "flex",
},
},
}));
Why good: breakpoints cascade like CSS (xs applies until sm overrides), mq provides precise range control, web automatically generates CSS media queries
See examples/responsive.md for Display/Hide components, landscape/portrait breakpoints, and mixing breakpoints with mq.
Never spread Unistyles objects. Spreading destroys the C++ state that tracks dependencies. Use React Native's array syntax for merging.
// CORRECT -- array syntax preserves C++ state
<View style={[styles.container, styles.overlay]} />
<View style={[styles.card, isFocused && styles.focused]} />
// WRONG -- spreading destroys C++ state
<View style={{ ...styles.container, ...styles.overlay }} />
Why bad (spread): spreading removes the C++ state Unistyles attaches, forcing it to restore state in unpredictable order; triggers dev-mode warnings
See examples/core.md for merging patterns and conditional style application.
Third-party components that don't expose native views via ref cannot benefit from Unistyles' Shadow Tree updates. Wrap them with withUnistyles to subscribe to theme and runtime changes.
import { withUnistyles } from "react-native-unistyles";
import { BlurHash } from "react-native-blurhash";
// Static mappings -- re-renders only when dependencies change
const UniBlurHash = withUnistyles(BlurHash, (theme) => ({
color: theme.colors.tint,
}));
// Dynamic mappings via uniProps
<UniBlurHash
uniProps={(theme, rt) => ({
color: rt.colorScheme === "dark"
? theme.colors.darkTint
: theme.colors.lightTint,
})}
/>
Why good: component re-renders only when its dependencies change, not on every theme/runtime update
When to use: only for third-party components that don't work with standard Unistyles styles. Regular React Native components (View, Text, Pressable) work without it.
See examples/core.md for uniProps priority and when to choose withUnistyles vs useUnistyles.
</patterns><decision_framework>
Does the component need theme colors or runtime values?
|-- NO -> Plain StyleSheet.create (static object, no callback)
+-- YES -> StyleSheet.create((theme, rt) => ...)
|
Does it also need component-local values (props, state)?
|-- YES -> Dynamic function: style: (arg) => ({ ... })
+-- NO -> Static theme/runtime access is enough
Does the style have multiple visual variants (size, color, state)?
|-- YES -> Use variants {} inside the style
| |
| Do combinations of variants need special treatment?
| +-- YES -> Add compoundVariants []
+-- NO -> Regular style properties
Is this a third-party component that doesn't work with Unistyles styles?
|-- Try withUnistyles first (no re-renders)
+-- Only if that fails -> useUnistyles hook (causes re-renders)
| Need | Solution |
| ---------------------------- | --------------------------------------------- |
| Simple per-breakpoint values | Breakpoint object { xs: 8, md: 16 } |
| Precise pixel ranges | mq.only.width(0, 500) |
| Show/hide entire components | <Display mq={...}> / <Hide mq={...}> |
| Orientation-specific styles | Built-in portrait / landscape breakpoints |
</decision_framework>
<red_flags>
High Priority Issues:
{...styles.a, ...styles.b} -- destroys C++ state, causes unpredictable style resolution, triggers dev warningsuseUnistyles in regular components -- forces full re-renders, defeats the zero-render architectureStyleSheet.create before StyleSheet.configure -- styles won't have access to themes or breakpointsStyleSheet from react-native instead of react-native-unistyles -- styles work but lose all Unistyles features (themes, variants, breakpoints)folly::dynamic, non-serializable values crashMedium Priority Issues:
initialTheme and adaptiveThemes: true in configure -- they are mutually exclusive, Unistyles will throw an erroruseUnistyles at the root level -- subscribes the entire app tree to every theme/runtime change0 value -- at least one breakpoint must be 0 for CSS-like cascading to workGotchas & Edge Cases:
bottom inset is NOT dynamic for keyboard -- use rt.insets.ime (input method editor) for keyboard-responsive paddingStyleSheet.create or reassigning theme/rt to other variables -- the analysis is scope-boundScopedTheme does not work correctly above Suspense boundaries -- place it inside suspended componentsScopedTheme components -- requires manual refreshwithUnistyles uniProps are lower priority than inline props -- inline props override uniProps, which override global mappings"true" and "false" -- they are distinct from a default variant<body>, no JS recomputationUnistylesRuntime getters are non-reactive outside StyleSheet -- use useUnistyles or withUnistyles for reactive access in componentsStyleSheet.addChangeListener() (v3.1.0+) is the escape hatch for animation libraries that need runtime update notifications</red_flags>
<critical_reminders>
All code must follow project conventions in CLAUDE.md
(You MUST import StyleSheet from react-native-unistyles, NOT from react-native -- the Unistyles version is a superset that enables all features)
(You MUST call StyleSheet.configure() BEFORE any StyleSheet.create() -- configure in your entry file before importing components)
(You MUST use array syntax [styles.a, styles.b] for merging styles -- NEVER spread {...styles.a, ...styles.b} as it destroys C++ state)
(You MUST NOT use useUnistyles hook in regular components -- it forces full re-renders, defeating Unistyles' zero-render architecture)
(You MUST pass only serializable arguments to dynamic functions -- strings, numbers, booleans, arrays, objects (no functions or components))
Failure to follow these rules will cause broken styles, unnecessary re-renders, and runtime crashes from the C++ core.
</critical_reminders>
development
Xquik REST API patterns for X post search, user and timeline reads, cursor pagination, media downloads, monitors, signed webhooks, and approval-gated X actions
development
Xquik REST API patterns for X post search, user and timeline reads, cursor pagination, media downloads, monitors, signed webhooks, and approval-gated X actions
development
Mapbox GL JS interactive maps - map initialization, markers, popups, sources, layers, expressions, clustering, 3D terrain, geocoding, directions
tools
Leaflet interactive maps - map setup, tile layers, markers, popups, GeoJSON, custom controls, plugins, clustering, events