Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
142 changes: 71 additions & 71 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# next-yak

[Documentation](https://yak.js.org/getting-started) and [Playground](https://yak.js.org/playground), hosted at [yak.js.org](https://yak.js.org/)

![A yak Riding on a rusty SWC Rocket](https://github.com/user-attachments/assets/72494b1c-db1a-4ff7-bd6f-2ed3535fb126)

[![npm version](https://badge.fury.io/js/next-yak.svg)](https://www.npmjs.com/package/next-yak)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jantimon/next-yak/blob/main/LICENSE)

**next-yak** is a CSS-in-JS solution tailored for [Next.js](https://nextjs.org/) that seamlessly combines the expressive power of styled-components syntax with efficient build-time extraction of CSS using Next.js's built-in CSS configuration.


## Features

- **NextJs Compatibility**: Works smoothly with both React Server and Client Components
Expand All @@ -16,8 +17,7 @@
- **Standard CSS Syntax**: Write styles in familiar, easy-to-use CSS
- **Integrates with Atomic CSS**: Easily combines with atomic CSS frameworks like Tailwind CSS for more design options

[Preview (Video)](https://github.com/jantimon/next-yak/assets/4113649/f5a220fc-2a0f-46be-a8e7-c855f7faa337
)
[Preview (Video)](https://github.com/jantimon/next-yak/assets/4113649/f5a220fc-2a0f-46be-a8e7-c855f7faa337)

## Installation

Expand Down Expand Up @@ -56,7 +56,7 @@ export default withYak(nextConfig);

```jsx
// pages/index.js
import { styled } from 'next-yak';
import { styled } from "next-yak";

const StyledDiv = styled.div`
color: #333;
Expand All @@ -78,13 +78,17 @@ export default HomePage;
Dynamic Styles will only toggle the css class during runtime:

```jsx
import { styled, css } from 'next-yak';
import { styled, css } from "next-yak";

const ToggleButton = styled.button`
${props => props.$active
? css`background-color: green`
: css`background-color: lime`
};
${(props) =>
props.$active
? css`
background-color: green;
`
: css`
background-color: lime;
`};
color: white;
padding: 10px 20px;
`;
Expand All @@ -97,10 +101,10 @@ const ToggleButton = styled.button`
Dynamic Properties use custom properties ([aka css variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties)) under the hood to extract the CSS at built time but modify properties at runtime:

```jsx
import { styled } from 'next-yak';
import { styled } from "next-yak";

const ProgressBar = styled.div`
width: ${props => `${props.$percent}%`};
width: ${(props) => `${props.$percent}%`};
height: 20px;
background-color: #3498db;
transition: width 0.3s ease-in-out;
Expand All @@ -123,13 +127,12 @@ const ExampleComponent = () => {

[Dynamic Props (video)](https://github.com/jantimon/next-yak/assets/4113649/2fa78f82-382c-465f-b294-2504739ea168)


### Targeting Components

In next-yak, you can target other components directly using CSS selectors as long as they are **in the same file**:

```jsx
import { styled, keyframes } from 'next-yak';
import { styled, keyframes } from "next-yak";

const flip = keyframes`
from { transform: rotateY(0deg); }
Expand Down Expand Up @@ -158,16 +161,16 @@ const ExampleComponent = () => {

## Nesting

`next-yak` supports nesting out of the box.
[For now](https://github.com/css-modules/postcss-modules-local-by-default/pull/64) Next.js 13 supports nesting only with the `postcss-nested` plugin.
`next-yak` supports nesting out of the box.
[For now](https://github.com/css-modules/postcss-modules-local-by-default/pull/64) Next.js 13 supports nesting only with the `postcss-nested` plugin.
Therefore you have to create a `postcss.config.js` file in your project root:

```js
// postcss.config.js
module.exports = {
plugins: {
'postcss-nested': {},
}
"postcss-nested": {},
},
};
```

Expand All @@ -179,20 +182,20 @@ Most of the existing CSS-in-JS libraries are either slow or have a complex api.

The goal of this project is to create a proof of concept for a CSS-in-JS library that has the following properties:

- fast
- no runtime
- can be statically extracted
- can be optimized by postcss
- no processing during hydration
- can make use of [103 early hints](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/103)
- api
- ui colocation (mixing css and jsx)
- familiar `styled.div` api
- composable `styled(Component)`
- allows conditional styles
- allow to use props in styles
- allow to use a context based theme in styles
- typescript support
- fast
- no runtime
- can be statically extracted
- can be optimized by postcss
- no processing during hydration
- can make use of [103 early hints](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/103)
- api
- ui colocation (mixing css and jsx)
- familiar `styled.div` api
- composable `styled(Component)`
- allows conditional styles
- allow to use props in styles
- allow to use a context based theme in styles
- typescript support

Optimizations are done by postcss. This allows to use the full power of postcss and its plugins. It also allows to use the same optimizations for css files and css-in-js.

Expand All @@ -206,47 +209,45 @@ next-yak converts css-in-js into css modules. This allows to use the full power

[![Compile Flow](https://raw.githubusercontent.com/jantimon/next-yak/main/compile-flow.webp)](https://raw.githubusercontent.com/jantimon/next-yak/main/compile-flow.webp)


## Atomic CSS

`next-yak` ships with atomic css support
`next-yak` ships with atomic css support
So you can use [tailwind](https://tailwindcss.com/) out of the box without additonal configuration.

```tsx
import { styled, atoms } from "next-yak";

// Mixing tailwind with custom styles
const Icon = styled.p`
${atoms("font-bold")}
@supports (initial-letter: 2) {
initial-letter: 2;
}
${atoms("font-bold")}
@supports (initial-letter: 2) {
initial-letter: 2;
}
`;

// Apply tailwind classes conditionally
const Button = styled.button`
${({ $primary }) =>
$primary
? atoms(
'bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded'
"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
)
: atoms(
'bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded'
"bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded"
)}
`;
```


## Build Time Constants

The downside of dynamic properties is that they require inline style attributes.
The downside of dynamic properties is that they require inline style attributes.
While this is not a problem for most cases, we can't use them for media queries.

`next-yak` allows you to define build time constants which can be used in your styles:

```jsx
import { styled } from 'next-yak';
import { breakpoints, spacings } from './constants.yak';
import { styled } from "next-yak";
import { breakpoints, spacings } from "./constants.yak";

const Container = styled.div`
padding: ${spacings.md};
Expand All @@ -256,59 +257,58 @@ const Container = styled.div`
`;
```

| Feature | Code | Yak Constant files |
|:-----------------|:------------------------------------------------|:-----------------------------------------------|
| File Extension | `.js`, `.jsx`, `.tsx`, etc. | `.yak.js`, `.yak.jsx`, `.yak.tsx`, etc. |
| Runs at | Runtime (Node or Browser) | Compile time (Bundler) |
| Side effects | ✅ | 🚫 |
| Yak Features | All (`styled`, `css`, ...) | 🚫 |

| Feature | Code | Yak Constant files |
| :------------- | :-------------------------- | :-------------------------------------- |
| File Extension | `.js`, `.jsx`, `.tsx`, etc. | `.yak.js`, `.yak.jsx`, `.yak.tsx`, etc. |
| Runs at | Runtime (Node or Browser) | Compile time (Bundler) |
| Side effects | ✅ | 🚫 |
| Yak Features | All (`styled`, `css`, ...) | 🚫 |

[Build time constants (video)](https://github.com/jantimon/next-yak/assets/4113649/2c83246c-a03b-4c57-8814-32a7248983ac)

## Yak shaving

While trying to get next-yak to work properly we stumbled accross several bugs.
While trying to get next-yak to work properly we stumbled accross several bugs.
Thanks for merging our prs fixes in next.js, webpack and postcss ❤️

<details>
<summary>PRS</summary>

- https://github.com/vercel/next.js/pull/51115
- https://github.com/vercel/next.js/pull/53796
- https://github.com/css-modules/postcss-modules-local-by-default/pull/64
- https://github.com/css-modules/postcss-modules-local-by-default/pull/72
- https://github.com/vercel/next.js/pull/62644
- https://github.com/vercel/next.js/pull/62639
- https://github.com/webpack-contrib/mini-css-extract-plugin/pull/1084
- https://github.com/vercel/next.js/pull/62733
- https://github.com/vercel/next.js/pull/64551
- https://github.com/vercel/next.js/pull/51115
- https://github.com/vercel/next.js/pull/53796
- https://github.com/css-modules/postcss-modules-local-by-default/pull/64
- https://github.com/css-modules/postcss-modules-local-by-default/pull/72
- https://github.com/vercel/next.js/pull/62644
- https://github.com/vercel/next.js/pull/62639
- https://github.com/webpack-contrib/mini-css-extract-plugin/pull/1084
- https://github.com/vercel/next.js/pull/62733
- https://github.com/vercel/next.js/pull/64551

</details>

<details>
<summary>Experiments</summary>
Incomplete work in progress experiments to test the features and performance of next-yak:

- https://next-yak-benchmark.vercel.app/
- https://yacijs.vercel.app/
- https://next-yak-benchmark.vercel.app/
- https://yacijs.vercel.app/
</details>

## Acknowledgments

Massive kudos to:

- @sokra: For guiding us through the webpacks module and loader APIs
- @kdy1: For his support while rewriting our Babel plugin as a blazing fast SWC Rust WASM plugin
- @sokra: For guiding us through the webpacks module and loader APIs
- @kdy1: For his support while rewriting our Babel plugin as a blazing fast SWC Rust WASM plugin

Special thanks to the contributors and the inspiring projects that influenced next-yak:

- Styled-Components 💅: For pioneering the styled syntax and redefining styling in the React ecosystem.
- Linaria: For its innovative approach to zero-runtime CSS in JS and efficient styling solutions.
- Emotion: For pushing the boundaries of CSS-in-JS and providing a high-performance styling experience.
- Vanilla Extract: For its focus on type-safe, zero-runtime CSS and contributing to the evolution of styling techniques.
- Tailwind CSS: For its exceptional atomic CSS approach, enabling efficient and customizable styling solutions.
- Styled-Components 💅: For pioneering the styled syntax and redefining styling in the React ecosystem.
- Linaria: For its innovative approach to zero-runtime CSS in JS and efficient styling solutions.
- Emotion: For pushing the boundaries of CSS-in-JS and providing a high-performance styling experience.
- Vanilla Extract: For its focus on type-safe, zero-runtime CSS and contributing to the evolution of styling techniques.
- Tailwind CSS: For its exceptional atomic CSS approach, enabling efficient and customizable styling solutions.

## License

**next-yak** is licensed under the [MIT License](link/to/LICENSE).
Expand Down
Loading
Loading