tiwi
v1.0.14
Published
React library to create components with Tailwind styles baked in.
Downloads
71
Maintainers
Readme
tiwi ·
Tiwi is a React library that makes it easy to create components with Tailwind styles baked in. This makes it straightforward to preserve the separation of concern between structure and style, similar to styled-components. It also comes with a powerful variants system for more advanced use cases.
This library works with both React on the web and React Native.
Getting started
1. Tailwind
Install and configure TailwindCSS in your project.
2. Tiwi
npm install tiwi
3. VSCode
For the best experience, install the official Tailwind CSS Intellisense extension.
The following configuration will allow the extension to work with Tiwi:
"editor.quickSuggestions": {
"strings": "on" // As-you-type suggestions within strings.
},
"tailwindCSS.experimental.classRegex": [
[
"(?:tiwi|tw)[.(][^`]+([^;]*)",
"[\"'`\\}]([^\"'`\\$]*)[\"'`\\$]"
]
]
Basic usage
Import Tiwi in your component file:
import tiwi from "tiwi";
You can now create Tiwi components:
const Header = tiwi.h1`
text-3xl
text-blue-600
`;
const Button = tiwi.button`
rounded
bg-blue-300
`;
These can be used like any other component:
<Button />
// Renders as:
// <button class="rounded bg-blue-300" />
Classes can still be overridden inline:
<Button className="bg-red-300" />
// Renders as:
// <button class="rounded bg-red-300" />
Other props work as expected:
<Button type="submit">Submit</Button>
// Renders as:
// <button class="rounded bg-blue-300" type="submit">Submit</button>
Tiwi components can be further extended:
const BigButton = tiwi(Button)`
text-lg
`;
// Renders as:
// <button class="rounded bg-blue-300 text-lg" />
When extending, styles can be overwritten:
const RedButton = tiwi(Button)`
bg-red-300
`;
// Renders as:
// <button class="rounded bg-red-300" />
Any component with a className
prop can be styled:
const SubmitButton: FC<{className?: string}> = props => {
return (
<button className={props.className} type="submit">
Submit
</button>
);
};
const RedSubmitButton = tiwi(SubmitButton)`
bg-red-300
`;
// Renders as:
// <button class="bg-red-300" type="submit">Submit</button>
Variants
What makes Tiwi so powerful is the built-in support for variants. It enables the style of a component to be changed along multiple dimensions without creating every permutation separately.
Here's a simple variant to support multiple sizes:
const SizeButton = tiwi.button`
m-1
p-2
text-normal
${{
medium: `
p-3
text-lg
`,
large: `
p-5
text-xl
`,
}}
`;
The desired variants can be provided in different ways:
<SizeButton />;
// Renders as:
// <button class="m-1 p-2 text-normal" />
<SizeButton variants="medium" />;
<SizeButton variants={["medium"]} />;
<SizeButton variants={{medium: true}} />;
// All render as:
// <button class="m-1 p-3 text-lg" />
If multiple variants overlap, the last one to be declared wins:
<SizeButton variants={["medium", "large"]} />;
<SizeButton variants={["large", "medium"]} />;
<SizeButton variants={{medium: true, large: true}} />;
// All render as:
// <button class="m-1 p-5 text-xl" />
Variants can be specified along separate dimensions:
const FlexButton = tiwi.button`
p-2
text-normal
bg-blue-300
${{
medium: `
p-3
text-lg
`,
large: `
p-5
text-xl
`,
}}
${{
primary: `
bg-green-300
`,
critical: `
bg-red-300
`,
}}
`;
<FlexButton variants={["medium", "critical"]} />;
<FlexButton variants={{medium: true, critical: true}} />;
// Both render as:
// <button class="p-3 text-lg bg-red-300" />
Variants can directly match some of the component's props:
const Button = tiwi.button`
bg-blue-500
${{
isDisabled: `bg-neutral-200`,
}}
`;
const MyComponent: FC<{isDisabled?: boolean}> = props => {
return <Button variants={props}>Continue</Button>;
};
Base style and variants can be declared in any order:
const FlexButton = tiwi.button`
text-normal
${{
medium: `text-lg`,
large: `text-xl`,
}}
bg-blue-300
${{
primary: `bg-green-300`,
critical: `bg-red-300`,
}}
`;
TypeScript
Tiwi is fully compatible with TypeScript and both props and variants are strongly typed automatically.
On top of that, an explicit variant type can be provided:
type Size = "small" | "medium";
const SizeButton = tiwi.button<Size>`
text-normal
${{medium: `text-medium`}}
`;
const MyComponent: FC<{size?: Size}> = props => {
return <SizeButton variants={props.size}>Press me!</SizeButton>;
};
The opposite (extracting the variants type) is also possible:
import tiwi, {VariantsOf} from "tiwi";
const SizeButton = tiwi.button`
text-normal
${{
medium: `text-medium`,
large: `text-large`,
}}
`;
type Variants = VariantsOf<typeof SizeButton>; // "medium" | "large".
React Native
Tiwi is compatible with React Native. It requires NativeWind to be installed.
React Native elements can then be styled in the same way:
import {View, Text} from "react-native";
import tiwi from "tiwi";
const Avatar = tiwi(View)`
size-10
rounded-full
`;
const Title = tiwi(Text)`
text-neutral-900
dark:text-white
`;
Full example
// tooltip.tsx
import {FC, PropsWithChildren, ReactNode} from "react";
import tiwi from "tiwi";
//
// Props.
//
type TooltipVariant = "normal" | "important";
interface TooltipProps extends PropsWithChildren {
variant?: TooltipVariant;
icon: ReactNode;
}
//
// Style.
//
const Layout = tiwi.div<TooltipVariant>`
flex
flex-row
rounded
p-2
gap-1
bg-neutral-200
${{
important: `bg-red-200`,
}}
`;
const Icon = tiwi.div`
size-5
`;
const Text = tiwi.div`
text-neutral-900
font-medium
`;
//
// Component.
//
export const Tooltip: FC<TooltipProps> = props => {
const {variant, icon, children} = props;
return (
<Layout variants={variant}>
<Icon>{icon}</Icon>
<Text>{children}</Text>
</Layout>
);
};
Acknowledgements
This library was inspired by Tailwind-Styled-Component and borrows some of its ideas. It also heavily relies on tailwind-merge for the underlying class manipulation and shares the same limitations.
License
Tiwi is MIT licensed.