Skip to content

a16n-dev/react-native-styling-kit

Repository files navigation

React Native Style Kit

A Styling API for React Native, designed as a lightweight set of tools to quickly build beautiful apps. You get:

  • 🎨 Theming
  • 📏 Breakpoint logic
  • 🧩 Variants/compound variants inspired by CVA
  • 📱 Access to runtime values (safe area insets, screen dimensions) in stylesheets
  • 🔩 Presets for building powerful utility stylesheets, for a "tailwind-like" experience

All with no babel/metro plugins, full compatibility with 3rd party components, and a focus on performance.

Installation

You know the drill 😄

pnpm install react-native-styling-kit

You're almost ready to go! You'll also need to wrap your app in <StyleKitProvider> (more on this later)

import { StyleKitProvider } from 'react-native-styling-kit';

const Main = () => {
  return (
    <StyleKitProvider>
      <App />
    </StyleKitProvider>
  );
}

Creating styles

Styles are created via makeUseStyles() which is a drop-in replacement for StyleSheet.create(). This function returns a hook you can call within your component to access the styles.

import { makeUseStyles } from 'react-native-styling-kit';


const useStyles = makeUseStyles({
  root: {
    backgroundColor: 'white',
    padding: 16,
  }
});

const Button = () => {
    const styles = useStyles();
    
    return <Pressable style={styles.root}/>
}

🎨 Theming

Define a theme, then pass it to your StyleKitProvider. If you're using TypeScript, also augment the theme type to get the correct typings across your app.

const theme = {...};

type ThemeType = typeof theme;

declare module 'react-native-styling-kit' {
  interface StyleKitTheme extends ThemeType {}
}

const Main = () => {
  return (
    <StyleKitProvider theme={theme}>
      <App />
    </StyleKitProvider>
  );
}

You can then create styles that access the theme by passing a function tomakeUseStyles() instead of an object

import { makeUseStyles } from 'react-native-styling-kit';

const useStyles = makeUseStyles(({ theme }) => ({
  root: {
    backgroundColor: theme.colors.background,
    padding: theme.spacing.md,
  }
}));

const Button = () => {
    const styles = useStyles();
    
    return <Pressable style={styles.root}/>
}

You can also access the theme directly with the useTheme() hook.

import { useTheme } from 'react-native-styling-kit';

const Button = () => {
  const theme = useTheme();
    
  return <Pressable 
    style={(pressed) => ({ 
      backgroundColor: pressed ? theme.colors.highlight : theme.colors.background 
    })}
  />
}

🧩 Variants

To use variants, first define a type for your variants, and pass it as a generic to makeUseStyles(). You can then define variant-specific styles within the variants key of your style definition.

Then, in the useStyles() hook within your component, pass it an object with the current variant values.

import { makeUseStyles } from 'react-native-styling-kit';

interface ButtonVariants {
    variant: 'outlined' | 'filled';
}

// Note the double parentheses here "()({...})" required for TypeScript to infer the types correctly
const useStyles = makeUseStyles<ButtonVariants>()({
  root: {
    variants: {
      outlined: { ... }, 
      filled: { ... },
    }  
  }
});

const Button = ({ variant = 'filled' }: Partial<ButtonVariants>) => {
  const styles = useStyles({ variant });
    
  return <Pressable style={styles.root}/>
}

Compound variants

You can also define compound variants that apply when multiple variant conditions are met

import { makeUseStyles } from 'react-native-styling-kit';

interface ButtonVariants {
    variant: 'outlined' | 'filled';
    size: 'sm' | 'md' | 'lg';
}

const useStyles = makeUseStyles<ButtonVariants>()(() => ({
  root: {
    variants: {...},
    compoundVariants: [
      {
        size: 'sm',
        variant: 'outlined',
        style: { ... }
      }
    ]  
  }
}));

Note: Compound variants are applied in the order they are defined, so later definitions will override earlier ones. Compound variants also take precedence over regular variants.


📱 Runtime values

You can also access runtime values such as screen dimensions or safe area insets within your stylesheets, through the rt value passed to the style function

import { makeUseStyles } from 'react-native-styling-kit';

const useStyles = makeUseStyles(({rt}) => ({
  root: {
    paddingTop: rt.insets.top,
  }
}));

const Button = () => {
  const styles = useStyles();
    
  return <Pressable style={styles.root}/>
}

📏 Breakpoints

You'll first need to configure a set of breakpoints, and pass them to your StyleKitProvider

const breakpoints = {
    xs: 0, // First breakpoint should start with 0
    sm: 360,
    md: 768,
    lg: 1024,
};

type BreakpointType = typeof breakpoints;

declare module 'react-native-styling-kit' {
  interface StyleKitBreakpoints extends BreakpointType {}
}

const Main = () => {
  return (
    <StyleKitProvider breakpoints={breakpoints}>
      <App />
    </StyleKitProvider>
  );
}

You can create breakpoint conditional styles by using the bp key within your style definitions.

import { makeUseStyles } from 'react-native-styling-kit';

const useStyles = makeUseStyles(({ bp }) => ({
  root: {
    height: 32, 
    // Apply a different style above the 'md' breakpoint 
    ...bp.above.md({
      height: 48,
    })
  }
}));

🔩 Utility Stylesheets

Having a set of utility styles for applying common styles such as margins, paddings, and flex properties is an especially powerful tool for composing UI. To help with this, react-native-styling-kit provides a number of presets that can be used to compose utility stylesheets.

import { preset, dynamicSpacing } from 'react-native-styling-kit/utility';

// Create a stylesheet by composing different utility presets
const a = StyleSheet.create({
    // Preset includes a predefined set of utility styles covering flexbox, positioning, opacity, and more
    ...preset,
    // You can also generate spacing tokens
    ...dynamicSpacing({
        s: 12,
        m: 16,
        l: 24,
    } as const)
});

// Usage
<View style={[a.flex_row, a.px_m]}>
    <Text style={[a.text_center, a.opacity_50]}>Hello, world!</Text>
</View>

Performance

react-native-styling-kit is designed to be as performant as possible without leveraging any compile-time optimisations. Only styles that depend on theme or runtime values subscribe to state updates (although in practice these are not likely to change often).

Styles are memoized and cached to ensure they are only recalculated when absolutely necessary. Computed styles are also passed to StyleSheet.create() to take advantage of the optimisations provided by React Native.

About

Toolkit for stylesheets with theming and variants in React Native

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published