Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

various improvements #19

Merged
merged 5 commits into from
Dec 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
"exports": {
".": {
"import": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts"
}
},
"types": "./dist/index.d.ts",
"scripts": {
"test": "vitest",
"build": "rm -rf dist && rollup -c",
Expand Down
5 changes: 5 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ type Twc<TCompose extends AbstractCompose> = (<T extends React.ElementType>(

type ShouldForwardProp = (prop: string) => boolean;

export type TwcComponentProps<
TComponent extends React.ElementType,
TCompose extends AbstractCompose = typeof clsx,
> = ResultProps<TComponent, undefined, { asChild?: boolean }, TCompose>;

export type Config<TCompose extends AbstractCompose> = {
/**
* The compose function to use. Defaults to `clsx`.
Expand Down
4 changes: 2 additions & 2 deletions website/components/Hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ export function Hero() {
return (
<div className="text">
<div className="text-center mt-10 mb-20 max-w-4xl mx-auto text-balance">
<h1 className="text-[3.4rem] leading-none md:text-8xl mb-5 font-semibold font-accent text-transparent bg-clip-text bg-gradient-to-br from-blue-9 to-purple-11">
<h1 className="text-[3rem] leading-none md:text-8xl mb-5 font-semibold font-accent text-transparent bg-clip-text bg-gradient-to-br from-blue-9 to-purple-11">
Supercharge React + Tailwind
</h1>
<p className="text-xl md:text-2xl text-low">
<p className="text-lg md:text-2xl text-low">
TWC is a lightweight library to create Tailwind Components in one
line, write less code and build faster.
</p>
Expand Down
26 changes: 20 additions & 6 deletions website/pages/docs/guides/adapting-based-on-props.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,27 @@ You can pass a function to `twc` to adapt the components based on props.

This button component has a primary state that changes its color. When setting the `$primary` prop to true, we are changing its style.

```tsx
import { twc } from "react-twc";
```tsx {7} /props/ /$primary/
import { twc, TwcComponentProps } from "react-twc";

type ButtonProps = React.ComponentProps<"button"> & { $primary?: boolean };
type ButtonProps = TwcComponentProps<"button"> & { $primary?: boolean };

const Button = twc.button<ButtonProps>((props) => [
"font-semibold border border-blue-500 rounded",
props.$primary ? "bg-blue-500 text-white" : "bg-white text-gray-800",
]);

render(
export default () => (
<div>
<Button>Normal</Button>
<Button $primary>Primary</Button>
</div>,
);
```

You may have noticed the usage of `TwcComponentProps<"button">`, it is a helper to
return props accepted by a `twc` component. It's similar to `React.ComponentProps<"button">` with `asChild` prop and `className` type from `clsx`.

<details>
<summary>Why is the prop prefixed by a dollar?</summary>

Expand All @@ -35,7 +38,10 @@ We call the prop `$primary` a "transient prop", transient props can be consumed

For complex use cases, you can use [`cva`](https://cva.style/docs) with `twc` to have more control on the variants.

```tsx
```tsx {18} /$intent/
import { twc, TwcComponentProps } from "react-twc";
import { cva } from "class-variance-authority";

const button = cva("font-semibold border border-blue-500 rounded", {
variants: {
$intent: {
Expand All @@ -48,7 +54,15 @@ const button = cva("font-semibold border border-blue-500 rounded", {
},
});

type ButtonProps = React.ComponentProps<"button"> & VariantProps<typeof button>;
type ButtonProps = TwcComponentProps<"button"> & VariantProps<typeof button>;

const Button = twc.button<ButtonProps>(({ $intent }) => button({ $intent }));

export default () => (
<div>
<Button>Primary button (default)</Button>
<Button $intent="primary">Primary button</Button>
<Button $intent="secondary">Secondary button</Button>
</div>
);
```
30 changes: 16 additions & 14 deletions website/pages/docs/guides/as-child-prop.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,27 @@

## Usage

Take a `Link` component defined in your app:
A good example is an `Anchor` component that you may want to reuse but with a `next/link` component. Let's see how you can achieve this using `asChild`:

```ts filename="components/link.ts"
Take an `Anchor` component:

```ts filename="components/anchor.ts"
import { twc } from "react-twc";

const Link = twc.a`underline text-blue-800 hover:text-blue-900`;
export const Anchor = twc.a`underline text-blue-800 hover:text-blue-900`;
```

You can reuse the style for a `next/link`, it is possible using `asChild` prop:
You can reuse the style for a `next/link` using `asChild` prop:

```tsx filename="app.tsx"
```tsx {6} /asChild/
import NextLink from "next/link";
import { Link } from "./components/link";

function App() {
return (
<Link asChild>
<NextLink href="/">Go home</NextLink>
</Link>
);
}
import { Anchor } from "./components/anchor";

export default () => (
<Anchor asChild>
<NextLink href="/">Go home</NextLink>
</Anchor>,
);
```

To learn more about `asChild`, read [Radix documentation](https://www.radix-ui.com/primitives/docs/guides/composition#changing-the-element-type). `twc` uses [Slot component](https://www.radix-ui.com/primitives/docs/utilities/slot) internally to implement `asChild` prop.
4 changes: 2 additions & 2 deletions website/pages/docs/guides/class-name-prop.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const Title = twc.h2`text-2xl font-bold`;

By default classes are not merged. If you want to merge classes, you can use `twc` with [`tailwind-merge`](https://github.com/dcastil/tailwind-merge).

```ts filename="utils.ts"
```ts filename="utils.ts" {11}
import { twMerge } from "tailwind-merge";
import { clsx } from "clsx";
import { createTwc } from "react-twc";
Expand All @@ -42,7 +42,7 @@ export function cn(...inputs: ClassValue[]) {
export const twx = createTwc({ compose: cn });
```

```tsx filename="card.tsx"
```tsx filename="card.tsx" /twx/ {6}
import { twx } from "./utils";

const Card = twx.div`rounded-lg border bg-slate-100 text-white shadow-sm`;
Expand Down
37 changes: 37 additions & 0 deletions website/pages/docs/guides/refs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Refs

One of the biggest feature of `twc` is the automatic forwarding of `ref` prop. You don't have to worry about it any more.

## Usage

Take this component with a forwarded ref.

```tsx /React.forwardRef/
import * as React from "react";

const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className="rounded-lg border bg-slate-100 text-white shadow-sm"
{...props}
/>
));
```

With `twc`, the `ref` prop is automatically forwarded:

```tsx
const Card = twc.div`rounded-lg border bg-slate-100 text-white shadow-sm`;
```

So you can safely use `ref` on `Card` component:

```tsx /ref/
function CardDemo() {
const ref = useRef();
return <Card ref={ref}>TWC is magic!</Card>;
}
```
21 changes: 17 additions & 4 deletions website/pages/docs/guides/styling-any-component.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,24 @@

## Usage

In this example, we style a `next/link` by specifying `twc(Link)`:
Sometime you may want to style components from libraries like [Radix](https://www.radix-ui.com/) or [React Aria Components](https://react-spectrum.adobe.com/react-aria/index.html) or your own cooked components.

```tsx
import Link from "next/link";
In this example, we style a `HoverCard` from Radix:

```tsx {4-6,12}
import * as HoverCard from "@radix-ui/react-hover-card";
import { twc } from "react-twc";

const Link = twc(Link)`underline text-blue-800 hover:text-blue-900`;
const HoverCardContent = twc(
HoverCard.Content,
)`data-[side=bottom]:animate-slideUpAndFade data-[side=right]:animate-slideLeftAndFade data-[side=left]:animate-slideRightAndFade data-[side=top]:animate-slideDownAndFade w-[300px] rounded-md bg-white p-5 shadow-[hsl(206_22%_7%_/_35%)_0px_10px_38px_-10px,hsl(206_22%_7%_/_20%)_0px_10px_20px_-15px] data-[state=open]:transition-all`;

export default () => (
<HoverCard.Root>
<HoverCard.Trigger>Hover me</HoverCard.Trigger>
<HoverCard.Portal>
<HoverCardContent>Hello from hover card</HoverCardContent>
</HoverCard.Portal>
</HoverCard.Root>
);
```