Twin.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.
Install / Use
/learn @ben-rogerson/Twin.macroREADME
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
- Parcel<br/>styled-components / emotion / emotion (ts)
- Webpack<br/>styled-components (ts) / emotion (ts)
- Preact<br/>styled-components / emotion / goober
- Create React App<br/>styled-components / emotion
- Vite<br/>styled-components (ts) / emotion (ts) / solid (ts)
- Jest / React Testing Library<br/>styled-components (ts) / emotion (ts)
Advanced frameworks
- Next.js<br/>styled-components / styled-components (ts) / emotion / emotion (ts) / stitches (ts)
- T3 App<br/>styled-components (ts) / emotion (ts)
- Blitz.js<br/>emotion (ts)
- Gatsby<br/>styled-components / emotion
Component libraries
- Storybook<br/>styled-components (ts) / emotion / emotion (ts)
- yarn/npm workspaces + Next.js + shared ui components<br/>styled-components
- Yarn workspaces + Rollup<br/>emotion
- HeadlessUI (ts)
Community
Drop into our Discord server for announcements, help and styling chat.
<a href="https://discord.gg/Xj6x9z7"><img src="https://img.shields.io/discord/705884695400939552?label=discord&logo=discord" alt="Discord"></a>
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
Related Skills
bluebubbles
332.3kUse when you need to send or manage iMessages via BlueBubbles (recommended iMessage integration). Calls go through the generic message tool with channel="bluebubbles".
node-connect
332.3kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
slack
332.3kUse when you need to control Slack from OpenClaw via the slack tool, including reacting to messages or pinning/unpinning items in Slack channels or DMs.
frontend-design
81.7kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
