Twin blends the magic of Tailwind with the flexibility of css-in-js
Demo twin on CodeSandbox β
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
+ import tw, { styled, css } from 'twin.macro'
- import tw from 'twin.macro'
- import styled from '@emotion/styled'
- import css from '@emotion/react'
β ml-7 was not found
Try one of these classes:
ml-0 [0] / ml-1 [0.25rem] / ml-2 [0.5rem] / ml-3 [0.75rem] / ml-4 [1rem] / ml-5 [1.25rem] / ml-6 [1.5rem]
ml-8 [2rem] / ml-10 [2.5rem] / ml-12 [3rem] / ml-16 [4rem] / ml-20 [5rem] / ml-24 [6rem] / ml-32 [8rem]
ml-40 [10rem] / ml-48 [12rem] / ml-56 [14rem] / ml-64 [16rem] / ml-auto [auto] / ml-px [1px]
- Prefix with
hocus:
to style hover + focus at the same time - Style form field states with
checked:
,invalid:
andrequired:
- Stack up variants whenever you need them
sm:hover:first:bg-black
Check out the full list of variants β
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))" />
const setCssVariables = () => <div tw="--base-color[#C0FFEE]" />
const customGridProperties = () => <div tw="grid-area[1 / 1 / 4 / 2]" />
const vendorPrefixes = () => <div tw="-webkit-mask-image[url(mask.png)]" />
import { css, theme } from 'twin.macro'
const Input = () => <input css={css({ color: theme`colors.purple.500` })} />
See more examples using the theme import β
<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 within many modern stacks - take a look at these examples to get started:
App build tools and libraries
- Parcel
styled-components / emotion / emotion (ts) - Webpack
styled-components (ts) / emotion (ts) - Preact
styled-components / emotion / goober - Create React App
styled-components / emotion - Snowpack
styled-components / styled-components (ts) / emotion / emotion (ts) - Vite
styled-components (ts) / emotion (ts)
Advanced frameworks
- Gatsby
styled-components / emotion - Next.js
styled-components / emotion / emotion (ts)π / stitches (ts) - Blitz.js
emotion (ts)π - Laravel
styled-components (ts) - Remix@v1 (soon)
Component libraries
- Storybook
styled-components (ts) / emotion - yarn/npm workspaces + Next.js + shared ui components
styled-components - Yarn workspaces + Rollup
emotion - HeadlessUI (ts)
π
Community
Drop into our Discord server for announcements, help and styling chat.
Resources
π₯ Docs: The prop styling guide - A must-read guide to level up on prop stylingπ₯ Docs: The styled component guide - A must-read guide on getting productive with styled components- Docs: Options - Learn about the features you can tweak via the twin config
- Plugin: babel-plugin-twin - Use the tw and css props without adding an import
- Example: Advanced theming - Add custom theming the right way using css variables
- Example: React + Tailwind breakpoint syncing - Sync your tailwind.config.js breakpoints with react
- Helpers: Twin VSCode snippits - For devs who want to type less
- Plugins: VSCode plugins - VScode plugins that work with twin
- Article: "Why I Love Tailwind" by Max Stoiber - Max (inventor of styled-components) shares his thoughts on twin
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.