Skip to main content

Variants, createStyles and Style Prop

This documentation covers three essential styling concepts: component variants, the createStyles function, and how to use the style prop effectively in your components.

Component Variants

Variants are predefined style configurations that can be applied to components. They provide a consistent way to apply different visual styles without writing custom CSS each time.

This is the preferred first step for real project code. Before writing a stylesheet, check whether built-in or app variants already express what you need.

There are two important sources of variants:

  • built-in package variants such as row, center, p:2, bg:token
  • app-specific variants registered with createAppVariants(...)

Dynamic Variants Depend On The Theme

Dynamic variants are not free-form values. They depend on the real app theme.

That means:

  • color:*, backgroundColor:*, bg:*, and border*Color:* must map to entries that exist in theme.colors
  • br:*, borderRadius:*, corner radius variants, and the current border*Width:* / border*Radius:* family must map to entries that exist in theme.radius
  • effect:* must map to entries that exist in theme.effects
  • spacing variants such as p:*, px:*, mt:*, and gap:* are based on the theme baseSpacing
  • cursor:* and scale:* are dynamic variants too, but they do not depend on theme tokens in the same way

Before using a dynamic variant, inspect the app theme and confirm that the token really exists.

Examples:

  • bg:neutralSolid200 only makes sense if neutralSolid200 exists in the theme color object
  • color:primarySolid500 only makes sense if primarySolid500 exists in the theme color object
  • br:large only makes sense if large exists in the theme radius object
  • effect:shadowMd only makes sense if shadowMd exists in the theme effects object

Do not invent dynamic variant tokens from memory. The theme is the source of truth.

Available Default Variants

All library components have access to a set of default variants that cover the most common use cases for layout, positioning, and alignment.

Display and Layout

// Display
<View style={['block']} /> // display: block
<View style={['flex']} /> // display: flex, flex: 1
<View style={['hidden']} /> // display: none

// Flex direction
<View style={['row']} /> // display: flex, flexDirection: row
<View style={['column']} /> // flexDirection: column
<View style={['centerRow']} /> // display: flex, flexDirection: row, alignItems: center

Positioning

// Position
<View style={['absolute']} /> // position: absolute
<View style={['relative']} /> // position: relative
<View style={['fixed']} /> // position: fixed
<View style={['sticky']} /> // position: sticky

// Positioning presets
<View style={['insetX']} /> // left: 0, right: 0
<View style={['insetY']} /> // top: 0, bottom: 0
<View style={['whole']} /> // top: 0, bottom: 0, left: 0, right: 0

Sizing

// Dimensions
<View style={['full']} /> // width: 100%, height: 100%
<View style={['fullWidth']} /> // width: 100%
<View style={['fullHeight']} /> // height: 100%

Flexbox Alignment

Align Items (cross axis)

<View style={['alignStart']} />     // alignItems: flex-start
<View style={['alignEnd']} /> // alignItems: flex-end
<View style={['alignCenter']} /> // alignItems: center
<View style={['alignStretch']} /> // alignItems: stretch

Justify Content (main axis)

<View style={['justifyStart']} />         // justifyContent: flex-start
<View style={['justifyEnd']} /> // justifyContent: flex-end
<View style={['justifyCenter']} /> // justifyContent: center
<View style={['justifySpaceBetween']} /> // justifyContent: space-between
<View style={['justifySpaceAround']} /> // justifyContent: space-around

Align Self (self-alignment)

<View style={['alignSelfCenter']} />    // alignSelf: center
<View style={['alignSelfStart']} /> // alignSelf: flex-start
<View style={['alignSelfEnd']} /> // alignSelf: flex-end
<View style={['alignSelfStretch']} /> // alignSelf: stretch

Centering Preset

<View style={['center']} />         // alignItems: center, justifyContent: center

Advanced Flexbox

// Flex wrap
<View style={['wrap']} /> // flexWrap: wrap

// Text
<Text style={['noWrap']} /> // whiteSpace: nowrap

Text Alignment

<Text style={['textLeft']} />       // textAlign: left
<Text style={['textCenter']} /> // textAlign: center
<Text style={['textRight']} /> // textAlign: right

Spacing Variants

Spacing variants require you to place the value after the : and remember that the final result will be a multiplication of the baseSpacing value defined in theme creation.

<View style={['padding:1']} />       // padding: baseSpacing * 1
<View style={['paddingBottom:2']} /> // paddingBottom: baseSpacing * 2
<View style={['margin:2']} /> // margin: baseSpacing * 2
<View style={['marginTop:2']} /> // marginTop: baseSpacing * 2
<View style={['gap:1']} /> // gap: baseSpacing * 1

Short Variants

Spacing variants can be used with their full name, but for brevity, you can use their short version:

<View style={['p:1']} />      // Padding
<View style={['pt:1']} /> // Padding top
<View style={['pb:1']} /> // Padding bottom
<View style={['pl:1']} /> // Padding left
<View style={['pr:1']} /> // Padding right
<View style={['px:1']} /> // Padding horizontal
<View style={['py:1']} /> // Padding vertical

<View style={['m:1']} /> // Margin
<View style={['mt:1']} /> // Margin top
<View style={['mb:1']} /> // Margin bottom
<View style={['ml:1']} /> // Margin left
<View style={['mr:1']} /> // Margin right
<View style={['mx:1']} /> // Margin horizontal
<View style={['my:1']} /> // Margin vertical

These short variants are especially important in real project code. For normal screens and components, they are usually the fastest and most correct path before reaching for createStyles(...).

Practical Example with Short Variants

// Layout using short variants for spacing
<View style={['row', 'p:2', 'gap:1', 'bg:neutralSolid100']}>
<Text style={['color:primarySolid500']}>Title</Text>
<Button style={['primary']} />
</View>

Color Variants

Color variants require you to place which color after the :

// Text color variants
<Text style={['color:neutralSolid600']} />
<Text style={['color:neutralSolid500']} />
<Text style={['color:primarySolid500']} />

// Background variants
<View style={['bg:neutralSolid600']} />
<View style={['bg:neutralSolid500']} />
<View style={['bg:primarySolid500']} />

Those color tokens come from theme.colors, so always check the project theme before choosing them.

Radius Variants

Radius variants depend on the radius entries defined in the theme:

<View style={['br:small']} />
<View style={['br:medium']} />
<View style={['br:large']} />

Those values only work when the corresponding keys exist in theme.radius.

The dynamic system also supports the longer radius families:

<View style={['borderRadius:large']} />
<View style={['borderTopLeftRadius:xl']} />
<View style={['borderBottomRightRadius:2xl']} />

Border Color, Width, and Radius Families

The dynamic variant system also supports border families:

<View style={['borderColor:primarySolid500']} />
<View style={['borderTopColor:dangerSolid500']} />
<View style={['borderLeftWidth:md']} />
<View style={['borderTopRadius:lg']} />

Important implementation detail:

  • border*Color:* uses theme.colors
  • border*Width:* currently resolves through theme.radius
  • border*Radius:* also resolves through theme.radius

So the AI should follow the real implementation, not assume generic CSS naming rules.

Effect Variants

Effect variants depend on theme.effects:

<View style={['effect:shadowMd']} />

Non-theme Dynamic Variants

Some dynamic variants are not driven by theme token maps:

<View style={['cursor:pointer']} />
<View style={['scale:1.25']} />
  • cursor:* uses the supported cursor values from the package
  • scale:* accepts numeric values and is rendered differently for browser and native environments

Practical Combination Examples

Breakpoints Also Depend On The Theme

Responsive syntax is not free-form either. Breakpoint names such as mobile, tablet, desktop, or wide only work when those names actually exist in the configured app theme.

That means:

  • breakpoint-prefixed strings such as tablet:px:3 depend on a real tablet breakpoint in the theme
  • breakpoints object keys such as tablet:up or desktop:down also depend on real theme breakpoint names
  • do not invent breakpoint names from memory

Always inspect the theme breakpoints before writing responsive style prop logic.

Header Layout

<View style={['row', 'alignCenter', 'justifySpaceBetween', 'fullWidth', 'p:2']}>
<Text style={['h2']}>Title</Text>
<Button style={['primary']} text="Action" />
</View>

Centered Card

<View style={['center', 'fullHeight', 'p:3']}>
<View style={['relative', 'centerRow', 'gap:2', 'bg:surface', 'p:4']}>
<Icon name="check" />
<Text style={['h3', 'color:success']}>Success!</Text>
</View>
</View>

Horizontal List with Scroll

<View style={['row', 'alignCenter', 'gap:2', 'wrap', 'p:2']}>
{items.map(item => (
<Button
key={item.id}
style={['secondary', 'px:3', 'py:1']}
text={item.name}
/>
))}
</View>

Custom Component Variants

When creating a stylesheet file for a component, you can create your own variants for it, and these variants you create will be available to use through the style prop.

const createComponentVariant = createStyles<ComponentComposition>

export const ComponentStyles = {
default: createComponentVariant((theme) => ({
wrapper: {
...theme.presets.center,
...theme.presets.fullWidth,
},
})),
primary: createComponentVariant((theme) => ({
wrapper: {
backgroundColor: theme.colors.primary,
borderRadius: theme.radius.medium,
...theme.spacing.padding(2),
},
})),
outlined: createComponentVariant((theme) => ({
wrapper: {
borderWidth: 1,
borderColor: theme.colors.primary,
backgroundColor: 'transparent',
},
})),
}

You can use it in the component:

<Component style={['primary']} />
<Component style={['outlined', 'p:3']} />

App-wide custom variants

When the project wants reusable custom style strings across many components, register them in the app styles module with createAppVariants(...):

import { createAppVariants } from '@codeleap/styles'

export const appVariants = createAppVariants({
elevatedCard: (theme) => ({
backgroundColor: theme.colors.neutralSolid0,
borderRadius: theme.radius.large,
...theme.effects.elevated,
}),
})

That makes elevatedCard available through the normal style prop language.

The createStyles Function

createStyles is used to create custom styles that have access to the theme. It's particularly useful for styles that cannot be achieved through variants alone.

The preferred project form is:

const styles = createStyles((theme) => ({
// themed stylesheet
}))

Prefer this after variants are no longer enough.

Conditional Style Prop Values

The style prop accepts more than just strings and objects. The type system also allows conditional values such as:

  • boolean
  • null
  • ''
  • arrays mixing those values with variants and style objects

This is useful for normal conditional UI styling:

<View
style={[
'row',
isActive && 'bg:primarySolid500',
disabled && { opacity: 0.5 },
hasError ? 'borderColor:dangerSolid500' : null,
]}
/>

This means the AI should prefer the normal conditional style-array pattern instead of building a separate temporary styles object just to toggle one or two variants.

Basic Usage

import { createStyles } from '@codeleap/styles'

// Static object support exists, but in real project code the themed function form is usually the better default
const styles = createStyles({
imageWrapper: {
flex: 1,
height: 200,
borderRadius: 12,
},
image: {
width: '70%',
aspectRatio: 1,
},
})

Accessing Theme Properties

The createStyles function receives the application theme and with that you have access to all the functionalities we've already described in the theme section.

const styles = createStyles((theme) => ({
container: {
// Colors
backgroundColor: theme.colors.surface,
borderColor: theme.colors.primary,

// Spacing
...theme.spacing.padding(2),
...theme.spacing.marginVertical(1),

// Border radius
borderRadius: theme.radius.medium,

// Presets
...theme.presets.center,
...theme.presets.shadow,

// Custom values
height: theme.values.buttonHeight,
width: theme.values.width / 2,
},

header: {
...theme.presets.row,
...theme.spacing.paddingHorizontal(3),
backgroundColor: theme.colors.primarySolid100,
},

content: {
flex: 1,
...theme.spacing.padding(2),
},
}))

The Style Prop

The style prop is the heart of the system, accepting a flexible array of styles that can include variants, custom objects, nested styles, and responsive breakpoints.

Basic Style Prop Syntax

// Single variant as string
<View style='flex' />

// Multiple variants and utilities
<View style={['flex', 'gap:2', 'padding:1']} />

// Combining variants with custom styles
<View style={['flex', 'center', styles.customContainer]} />

Inline Style Objects

// Custom CSS directly in the prop
<View style={[
'flex',
'center',
{
backgroundColor: 'red',
borderRadius: 8,
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
}
]} />

Nested Styles

You can style directly the composition components of the main component

When using nested composition styles, follow the exact composition keys exported by the component. For package components, inspect the installed source in node_modules. For project components, inspect the project stylesheet file. Do not invent slot names or state keys from memory. Keys such as icon, touchableWrapper, or icon:disabled are only valid when that specific component composition actually exposes them.

// Styling child elements through the parent component
<View style={[
'flex',
'center',
{
backgroundColor: 'red',

// Styles for child elements
innerWrapper: ['flex', 'center', { opacity: 0.5 }],
title: ['h1', { color: 'white', textAlign: 'center' }],
button: ['primary', { marginTop: 16 }],

// Pseudo-states
hover: { opacity: 0.8 },
active: { transform: 'scale(0.98)' },
}
]} />

Responsive Breakpoints (web only)

You can open a breakpoints object and the key will be one of the breakpoints defined in theme creation

// Different styles for different screen sizes
<View style={[
'flex',
'center',
{ backgroundColor: 'red' },
{
breakpoints: {
mobile: [
'column',
'p:2',
{ gap: 8 }
],
tablet: [
'alignStart',
'p:3',
{
backgroundColor: 'blue',
maxWidth: 768
}
],
desktop: [
'row',
'p:4',
{
padding: 20,
maxWidth: 1200,
margin: '0 auto'
}
]
}
}
]} />

Best Practices

When to Use Each Approach

Variants: For quick and direct styles

// Default variants for layout
<View style={['row', 'alignCenter', 'gap:2']} />
<Text style={['h1', 'textCenter', 'color:primary']} />

// Custom variants for specific components
<Button style={['primary']} />
<Card style={['elevated', 'rounded']} />

createStyles: For complex and custom styles that need theme access

const styles = createStyles((theme) => ({
complexLayout: {
height: theme.values.height / 3,
...theme.presets.center,
background: `linear-gradient(45deg, ${theme.colors.primary}, ${theme.colors.secondary})`,
},
}))

Inline Objects: For specific and unique adjustments

<View style={['flex', { backgroundColor: 'rgba(0,0,0,0.05)' }]} />

Combining All Approaches

// ✅ Complete example using all functionalities
<View style={[
// 1. Base variants for structure
'flex',
'column',

// 2. Short variants for quick spacing
'gap:2',
'p:2',

// 3. Custom styles from createStyles
styles.containerBase,

// 4. Inline objects for specific adjustments
{
backgroundColor: 'rgba(0,0,0,0.05)',
borderRadius: 12,

// 5. Nested styles for child elements
header: [
'row',
'alignCenter',
'justifySpaceBetween',
'pb:2',
{ borderBottomWidth: 1, borderBottomColor: '#eee' }
],

content: [
'flex',
'gap:1',
styles.contentArea
]
},

// 6. Breakpoints for responsiveness
{
breakpoints: {
mobile: [
'p:1',
{
header: ['column', 'alignStart', 'gap:1'],
content: ['gap:0.5']
}
],
desktop: [
'p:3',
{
maxWidth: 800,
margin: '0 auto'
}
]
}
}
]} />

// ✅ For simple components, keep it simple
<View style={['row', 'alignCenter', 'gap:2', 'p:2']} />
<Text style={['h1', 'textCenter', 'color:primary']} />
<Button style={['primary']} />

Final Tips

  • Prioritize variants for consistency, simplicity, and performance
  • Use createStyles when you need theme access
  • Combine approaches when necessary, but maintain readability
  • Prefer short variants (p:2 vs padding:2) for cleaner code

Anti-patterns

These are the most common mistakes when using @codeleap/styles. AI agents and new developers should read this carefully.

// ❌ Never use StyleSheet.create() — it bypasses the theme system
const styles = StyleSheet.create({
container: { padding: 16, backgroundColor: '#007AFF' }
})
// ✅ Use createStyles() instead
const styles = createStyles((theme) => ({
container: { ...theme.spacing.padding(2), backgroundColor: theme.colors.primary }
}))

// ❌ Never hardcode spacing values — they won't respect baseSpacing
<View style={{ padding: 16, gap: 8 }} />
// ✅ Use spacing variants
<View style={['p:2', 'gap:1']} />

// ❌ Never hardcode color values — they won't update with theme/dark mode
<Text style={{ color: '#007AFF' }} />
// ✅ Use color tokens from the theme
<Text style={['color:primary']} />
// or in createStyles:
<Text style={[styles.text]} /> // where styles.text = { color: theme.colors.primary }

// ❌ Never pass a plain style object as the style prop
<View style={{ flexDirection: 'row', alignItems: 'center' }} />
// ✅ Use string variants
<View style={['row', 'alignCenter']} />

// ❌ Don't skip the 'default' variant in component stylesheets
export const MyStyles = {
primary: createVariant((theme) => ({ ... })) // missing 'default'!
}
// ✅ Always include 'default' — it's mandatory
export const MyStyles = {
default: createVariant((theme) => ({ ... })),
primary: createVariant((theme) => ({ ... })),
}