Skip to content

⚛️ A Utility-First Styling Library for React Native

License

Notifications You must be signed in to change notification settings

branikclimbs/react-native-styled.macro

 
 

Repository files navigation

Disclaimer: This library is still in Beta. Use with caution. The Roadmap for v1.0.0 is available here.

💅 styled.macro

A Utility-first Styling Library for React Native.

Features

  • Zero-overhead: Styles get injected via the StyleSheet API during compilation.
  • 🍂 Minimal footprint: Styles that are never used won't make it to the final App bundle.
  • 🎲 Variants support: Conditionally style based on Platform, Layout or Screen size ... etc.
  • 💅 Style props: Supports common style-related Component props e.g. numberOfLines.
  • 🔌 Customizable (Coming soon): Optionally override the default theme by adding styled.config.js file

Table of Contents

Getting started

Compatible with React Native v0.62.0 or later

yarn add react-native-styled.macro babel-plugin-macros

Add babel-plugin-macros to your Babel config (if you haven't already)

// babel.config.js
module.exports = function (api) {
	return {
		plugins: ['macros'],
		// ... other stuff
	};
};

To use the library simply import the macro as follows:

import styled from 'react-native-styled.macro';

const Heading = ({ text }) => (
	<Text
		{...styled([
			'my-4',
			'text-2xl',
			'text-gray-900',
			'font-semibold',
			'letter-wide',
		])}
	>
		{text}
	</Text>
);

The compiled output for the above code will look something like the following:

import { Text } from 'react-native';
+import { StyleSheet } from 'react-native';
+import { rem } from 'react-native-styled.macro/path/not/relevant';
-import styled from 'react-native-styled.macro';

+const styles = StyleSheet.create({
+	_default: {
+		marginVertical: rem(1),
+		fontSize: rem(1.5),
+		color: '#1a202c',
+		fontWeight: '600',
+		letterSpacing: rem(0.025),
+	},
+});

const Heading = ({ text }) => (
	<Text
-		{...styled([
-			'my-4',
-			'text-2xl',
-			'text-gray-900',
-			'font-semibold',
-			'letter-wide',
-		])}
+		{...{
+			style: styles._default,
+		}}
	>
		{text}
	</Text>
);

How does it work?

  • styled (you can name it anything) is a Babel Macro which means it will be executed during compilation.
  • It will map the given styles and resolve the necessary style attributes/props.
  • It will try to merge styles of the same variant if possible so we don't end up creating an object for every style e.g. text-2xl.
  • For the best performance, it will then use the good/old StyleSheet.create to create the styles as you should normally do by yourself in a React Native app.

The output for any code you write will look more or less the same as above. The only exception is a style with multiple variants because we need to add logic to switch styles at runtime (same as you would do e.g. using Platform.select())

Available Styles

See docs/styles.md

Variants

Platform (Built-in)

Enables Platform-specific style. Based on the value of Platform.OS.

Possible values: android, ios, web or whatever the value of Platform.OS.

Example:

styled([
	'bg-white',
	'web:bg-purple-600',
	'android:bg-green-600',
	'ios:bg-blue-600',
]);

Layout (Built-in)

Enables Layout-specific style. Based on the value of I18nManager.isRTL.

Possible keys: ltr or rtl.

Example:

styled(['text-auto', 'rtl:text-right', 'ltr:text-left']);

Responsive

Built on the top of React Native's useWindowDimensions hook. Possible keys: sm, md, lg, xl or custom values (see below).

Example

import styled from 'react-native-styled.macro';
import { useWindowVariant } from 'react-native-styled.macro/lib';

const MyComponent = () => {
	const windowVariant = useWindowVariant();

	return (
		<Text
			{...styled(['w-full', 'md:w-64'], {
				...windowVariant /* other variants */,
			})}
		>
			My text
		</Text>
	);
};

You can also pass custom breakpoints as follows:

// Note: passing a custom object will remove the default breakpoints e.g. `sm`.
useWindowVariant({
	tablet: 640,
	laptop: 768,
	// .. anything really
});

// use it later
styled(['tablet:w-full', 'laptop:w-64']);

Dark mode

Since styled accepts arbitrary keys as variants supporting Dark mode can be easily acheived as follows:

import { useColorScheme } from 'react-native';
import styled from 'react-native-styled.macro';

const MyComponent = () => {
	// Can either be 'dark' or 'light'
	const colorScheme = useColorScheme();

	return (
		<Text
			{...styled(['text-black', 'dark:text-white'], {
				dark: colorScheme === 'dark',
			})}
		>
			My text
		</Text>
	);
};

Best Practices

Group variant styles together

Do NOT

styled(['web:bg-gray-100', 'bg-white', 'text-black', 'web:rounded']);

Do

styled(['bg-white', 'text-black', 'web:rounded', 'web:bg-gray-100']);

In addition to the readability concern, it also enables some compile-time optimizations.

Prior Art

  • Tailwind CSS (website): Tailwind is a great utility-first CSS framework. We borrowed the utility-first approach from there and re-imagined how it can be used in React Native apps to build user interfaces faster without additional Runtime overhead.

Alternatives

  • tailwind-react-native (github): A very promising Tailwind-like library made with React Native in mind.

License

MIT © Ahmed T. Ali

About

⚛️ A Utility-First Styling Library for React Native

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • TypeScript 92.6%
  • JavaScript 7.4%