Convert Figma logo to code with AI

ben-rogerson logotwin.macro

๐Ÿฆนโ€โ™‚๏ธ Twin blends the magic of Tailwind with the flexibility of css-in-js (emotion, styled-components, solid-styled-components, stitches and goober) at build time.

7,892
184
7,892
18

Top Related Projects

A utility-first CSS framework for rapid UI development.

Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress ๐Ÿ’…

17,396

๐Ÿ‘ฉโ€๐ŸŽค CSS-in-JS library designed for high performance style composition

Documentation about css-modules

[Not Actively Maintained] CSS-in-JS with near-zero runtime, SSR, multi-variant support, and a best-in-class developer experience.

Zero-runtime Stylesheets-in-TypeScript

Quick Overview

Twin.macro is a CSS-in-JS library that combines the power of Tailwind CSS with the flexibility of CSS-in-JS. It allows developers to use Tailwind's utility classes within styled-components or Emotion, providing a seamless integration of utility-first CSS with component-based styling.

Pros

  • Combines the benefits of Tailwind CSS and CSS-in-JS
  • Offers better performance compared to traditional Tailwind CSS usage
  • Provides type safety and autocompletion when used with TypeScript
  • Allows for easy customization and extension of Tailwind's utility classes

Cons

  • Requires additional setup and configuration compared to vanilla Tailwind CSS
  • May have a steeper learning curve for developers unfamiliar with CSS-in-JS
  • Can increase bundle size if not properly optimized
  • Limited community support compared to more established CSS solutions

Code Examples

  1. Basic usage with styled-components:
import tw, { styled } from 'twin.macro'

const Button = styled.button`
  ${tw`bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded`}
`
  1. Conditional styling:
import tw from 'twin.macro'

const Button = tw.button`
  bg-blue-500 text-white font-bold py-2 px-4 rounded
  ${props => props.primary && tw`bg-red-500 hover:bg-red-700`}
`
  1. Combining with custom CSS:
import tw, { styled } from 'twin.macro'

const Card = styled.div`
  ${tw`bg-white rounded-lg shadow-md p-6`}
  max-width: 300px;
  transition: transform 0.2s ease-in-out;

  &:hover {
    transform: scale(1.05);
  }
`

Getting Started

  1. Install the necessary dependencies:
npm install twin.macro @emotion/react @emotion/styled
  1. Add the Twin babel macro to your babel config:
{
  "plugins": ["babel-plugin-macros"]
}
  1. Create a twin configuration file (twin.config.js):
module.exports = {
  theme: {
    extend: {
      colors: {
        electric: '#db00ff',
        ribbon: '#0047ff',
      },
    },
  },
  plugins: [],
}
  1. Start using Twin in your components:
import tw from 'twin.macro'

const Button = tw.button`bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded`

Competitor Comparisons

A utility-first CSS framework for rapid UI development.

Pros of Tailwind CSS

  • Larger community and ecosystem, with more resources and third-party tools
  • Easier to get started with, as it doesn't require additional setup or configuration
  • Better performance for larger projects due to its purging capabilities

Cons of Tailwind CSS

  • Can lead to verbose HTML with many utility classes
  • Less flexible for creating custom designs or complex components
  • Requires additional tooling for optimal file size in production

Code Comparison

Tailwind CSS:

<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
  Button
</button>

Twin.macro:

import tw from 'twin.macro'

const Button = tw.button`
  bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded
`

Twin.macro allows for a more component-based approach, integrating Tailwind's utility classes directly into styled components. This can lead to cleaner JSX and better separation of concerns. However, Tailwind CSS's approach is more straightforward and doesn't require additional libraries or setup, making it easier for beginners to adopt.

Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress ๐Ÿ’…

Pros of styled-components

  • More intuitive syntax for developers familiar with CSS
  • Better support for dynamic styling based on props
  • Wider adoption and larger community

Cons of styled-components

  • Larger bundle size due to runtime CSS generation
  • Steeper learning curve for developers new to CSS-in-JS
  • Potential performance issues with complex styling logic

Code Comparison

styled-components:

const Button = styled.button`
  background-color: ${props => props.primary ? 'blue' : 'white'};
  color: ${props => props.primary ? 'white' : 'blue'};
  padding: 10px 20px;
  border: 2px solid blue;
`;

twin.macro:

import tw from 'twin.macro'

const Button = tw.button`
  bg-blue-500 text-white
  px-4 py-2
  border-2 border-blue-500
`

Twin.macro leverages Tailwind CSS classes, resulting in a more concise and predictable styling approach. It also offers better performance due to its compile-time CSS generation. However, styled-components provides more flexibility for complex, dynamic styling scenarios and has a larger ecosystem of tools and resources.

Both libraries have their strengths, and the choice between them often depends on project requirements, team preferences, and performance considerations.

17,396

๐Ÿ‘ฉโ€๐ŸŽค CSS-in-JS library designed for high performance style composition

Pros of Emotion

  • More established and widely adopted in the React community
  • Offers a broader set of features for CSS-in-JS, including server-side rendering support
  • Provides a more flexible API for creating and composing styles

Cons of Emotion

  • Steeper learning curve for developers new to CSS-in-JS
  • Requires more setup and configuration compared to Twin.macro
  • Can lead to larger bundle sizes due to its comprehensive feature set

Code Comparison

Emotion:

import { css } from '@emotion/react'

const style = css`
  background-color: hotpink;
  &:hover {
    color: ${props => props.color};
  }
`

Twin.macro:

import tw from 'twin.macro'

const style = tw`
  bg-pink-500
  hover:text-[color:var(--color)]
`

Summary

Emotion is a more comprehensive CSS-in-JS solution with a wider range of features and flexibility. It's well-suited for complex projects that require advanced styling capabilities. Twin.macro, on the other hand, focuses on integrating Tailwind CSS with CSS-in-JS, offering a simpler approach for developers familiar with utility-first CSS. The choice between the two depends on project requirements, team expertise, and preferred styling methodology.

Documentation about css-modules

Pros of css-modules

  • Simpler learning curve, as it builds on existing CSS knowledge
  • Better compatibility with existing CSS tooling and workflows
  • Easier to integrate with legacy projects or codebases

Cons of css-modules

  • Less flexible for dynamic styling compared to twin.macro
  • Requires additional build configuration and tooling setup
  • Limited ability to share styles across components without duplication

Code Comparison

css-modules:

.button {
  background-color: blue;
  color: white;
  padding: 10px 20px;
}
import styles from './Button.module.css';

const Button = () => <button className={styles.button}>Click me</button>;

twin.macro:

import tw from 'twin.macro';

const Button = () => (
  <button tw="bg-blue-500 text-white px-5 py-2">Click me</button>
);

css-modules offers a more traditional approach to styling, maintaining separation between CSS and JavaScript. It's easier to adopt for teams familiar with CSS but requires additional setup.

twin.macro provides a more integrated, inline styling approach using Tailwind CSS utility classes. It offers greater flexibility for dynamic styling and reduces context switching between files, but has a steeper learning curve for those unfamiliar with Tailwind CSS.

[Not Actively Maintained] CSS-in-JS with near-zero runtime, SSR, multi-variant support, and a best-in-class developer experience.

Pros of Stitches

  • Near-zero runtime, with CSS generated at build time
  • Built-in support for server-side rendering (SSR)
  • Offers a more traditional CSS-in-JS API, which may be familiar to developers coming from other styling libraries

Cons of Stitches

  • Less flexible than Twin.macro for integrating with existing CSS frameworks
  • Requires learning a new API and syntax for styling components
  • May have a steeper learning curve for developers used to traditional CSS or utility-first approaches

Code Comparison

Twin.macro:

import tw from 'twin.macro'

const Button = tw.button`
  bg-blue-500
  hover:bg-blue-700
  text-white
  font-bold
  py-2
  px-4
  rounded
`

Stitches:

import { styled } from '@stitches/react'

const Button = styled('button', {
  backgroundColor: '$blue500',
  color: 'white',
  fontWeight: 'bold',
  padding: '0.5rem 1rem',
  borderRadius: '0.25rem',
  '&:hover': {
    backgroundColor: '$blue700',
  },
})

Both Twin.macro and Stitches offer powerful styling solutions for React applications, but they differ in their approach and syntax. Twin.macro leverages Tailwind CSS utility classes, providing a familiar experience for Tailwind users, while Stitches offers a more traditional CSS-in-JS API with additional features like SSR support and near-zero runtime overhead.

Zero-runtime Stylesheets-in-TypeScript

Pros of vanilla-extract

  • Type-safe CSS-in-JS with zero runtime overhead
  • Supports static extraction of styles for improved performance
  • Seamless integration with existing CSS ecosystems and tooling

Cons of vanilla-extract

  • Steeper learning curve for developers familiar with traditional CSS
  • Limited real-time style updates compared to runtime CSS-in-JS solutions
  • Requires additional build step for style extraction

Code Comparison

vanilla-extract:

import { style } from '@vanilla-extract/css';

export const button = style({
  backgroundColor: 'blue',
  color: 'white',
  padding: '10px 20px',
});

twin.macro:

import tw from 'twin.macro';

const Button = tw.button`
  bg-blue-500
  text-white
  py-2
  px-4
`;

Twin.macro offers a more concise syntax using Tailwind-like utility classes, while vanilla-extract provides a more traditional CSS-in-JS approach with type safety. Twin.macro allows for easier adoption of utility-first CSS, whereas vanilla-extract focuses on zero-runtime CSS generation and type safety. Both solutions aim to improve the developer experience and performance of styling in modern web applications, but with different approaches and trade-offs.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Twin examples Twin examples

The magic of Tailwind with the flexibility of css-in-js.

Total Downloads Latest Release Discord

Open in StackBlitz


Style jsx elements using Tailwind classes:

import 'twin.macro'

const Input = () => <input tw="border hover:border-black" />

Nest Twinรขย€ย™s tw import within a css prop to add conditional styles:

import tw from 'twin.macro'

const Input = ({ hasHover }) => (
  <input css={[tw`border`, hasHover && tw`hover:border-black`]} />
)

Or mix sass styles with the css import:

import tw, { css } from 'twin.macro'

const hoverStyles = css`
  &:hover {
    border-color: black;
    ${tw`text-black`}
  }
`
const Input = ({ hasHover }) => (
  <input css={[tw`border`, hasHover && hoverStyles]} />
)

Styled Components

You can also use the tw import to create and style new components:

import tw from 'twin.macro'

const Input = tw.input`border hover:border-black`

And clone and style existing components:

const PurpleInput = tw(Input)`border-purple-500`

Switch to the styled import to add conditional styling:

import tw, { styled } from 'twin.macro'

const StyledInput = styled.input(({ hasBorder }) => [
  `color: black;`,
  hasBorder && tw`border-purple-500`,
])
const Input = () => <StyledInput hasBorder />

Or use backticks to mix with sass styles:

import tw, { styled } from 'twin.macro'

const StyledInput = styled.input`
  color: black;
  ${({ hasBorder }) => hasBorder && tw`border-purple-500`}
`
const Input = () => <StyledInput hasBorder />

How it works

When babel runs over your javascript or typescript files at compile time, twin grabs your classes and converts them into css objects. These css objects are then passed into your chosen css-in-js library without the need for an extra client-side bundle:

import tw from 'twin.macro'

tw`text-sm md:text-lg`

// รขย†ย“ รขย†ย“ รขย†ย“ รขย†ย“ รขย†ย“ รขย†ย“

{
  fontSize: '0.875rem',
  '@media (min-width: 768px)': {
    fontSize: '1.125rem',
  },
}

Features

รฐยŸย‘ยŒ Simple imports - Twin collapses imports from common styling libraries into a single import:

- import styled from '@emotion/styled'
- import css from '@emotion/react'
+ import { styled, css } from 'twin.macro'

รฐยŸยยน Adds no size to your build - Twin converts the classes youรขย€ย™ve used into css objects using Babel and then compiles away, leaving no runtime code

รฐยŸยยฑ Apply variants to multiple classes at once with variant groups

import 'twin.macro'

const interactionStyles = () => (
  <div tw="hover:(text-black underline) focus:(text-blue-500 underline)" />
)

const mediaStyles = () => <div tw="sm:(w-4 mt-3) lg:(w-8 mt-6)" />

const pseudoElementStyles = () => <div tw="before:(block w-10 h-10 bg-black)" />

const stackedVariants = () => <div tw="sm:hover:(bg-black text-white)" />

const groupsInGroups = () => <div tw="sm:(bg-black hover:(bg-white w-10))" />

รฐยŸย›ยŽ Helpful suggestions for mistypings - Twin chimes in with class and variant values from your Tailwind config:

รขยœย• ml-1.25 was not found

Try one of these classes:

- ml-1.5 > 0.375rem
- ml-1 > 0.25rem
- ml-10 > 2.5rem

รฐยŸย–ยŒรฏยธย Use the theme import to add values from your tailwind config

import { css, theme } from 'twin.macro'

const Input = () => <input css={css({ color: theme`colors.purple.500` })} />

See more examples using the theme import รขย†ย’

รฐยŸย’ยก Works with the official tailwind vscode plugin - Avoid having to look up your classes with auto-completions straight from your Tailwind config - setup instructions รขย†ย’

รฐยŸย’ยฅ Add !important to any class with a trailing or leading bang!

<div tw="hidden!" /> || <div tw="!hidden" />
// รขย†ย“ รขย†ย“ รขย†ย“ รขย†ย“ รขย†ย“ รขย†ย“ รขย†ย“ รขย†ย“ รขย†ย“
<div css={{ "display": "none !important" }} />

Add !important to multiple classes with bracket groups:

<div tw="(hidden ml-auto)!" />
// รขย†ย“ รขย†ย“ รขย†ย“ รขย†ย“ รขย†ย“ รขย†ย“ รขย†ย“ รขย†ย“ รขย†ย“
<div css={{ "display": "none !important", "marginLeft": "auto !important" }} />

Get started

Twin works with many modern stacks - take a look at these examples to get started:

App build tools and libraries

Advanced frameworks

Component libraries

Community

Drop into our Discord server for announcements, help and styling chat.

Discord

Resources

Special thanks

This project stemmed from babel-plugin-tailwind-components so a big shout out goes to Brad Cornes for the amazing work he produced. Styling with tailwind.macro has been such a pleasure.


Consider donating some รฐยŸยย• if you enjoy!

NPM DownloadsLast 30 Days