jantimon / next-yak

a streamlined CSS-in-JS solution tailor-made for Next.js, seamlessly combining the expressive power of styled-components syntax with efficient build-time extraction and minimal runtime footprint, ensuring optimal performance and easy integration with existing atomic CSS frameworks like Tailwind CSS
https://yak.js.org
108 stars 2 forks source link
css-in-js

next-yak

Yak At Work as Frontend Dev

npm version License

next-yak is a CSS-in-JS solution tailored for Next.js 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

Preview (Video)

Installation

npm install next-yak

or

yarn add next-yak

Getting Started

See a live stackblitz demo or try our stackblitz starter kit

Locally

  1. Install next-yak in your Next.js project.

  2. Add next-yak to your next.config.mjs:

// next.config.js
import { withYak } from "next-yak/withYak";

const nextConfig = {
  // your next.js config
};

export default withYak(nextConfig);
  1. Ready to go:
// pages/index.js
import { styled } from 'next-yak';

const StyledDiv = styled.div`
  color: #333;
  padding: 16px;
  background-color: #f0f0f0;
`;

function HomePage() {
  return <StyledDiv>Hello, next-yak!</StyledDiv>;
}

export default HomePage;

More Examples

Dynamic Styles

Dynamic Styles will only toggle the css class during runtime:

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

const ToggleButton = styled.button`
  ${props => props.$active
     ? css`background-color: green`
     : css`background-color: lime`
  };
  color: white;
  padding: 10px 20px;
`;

Dynamic Styles (Video)

Dynamic Properties

Dynamic Properties use custom properties (aka css variables) under the hood to extract the CSS at built time but modify properties at runtime:

import { styled } from 'next-yak';

const ProgressBar = styled.div`
  width: ${props => `${props.$percent}%`};
  height: 20px;
  background-color: #3498db;
  transition: width 0.3s ease-in-out;
`;

const ProgressBarContainer = styled.div`
  border: 1px solid #ccc;
`;

const ExampleComponent = () => {
  const progress = 75; // You can dynamically set this value

  return (
    <ProgressBarContainer>
      <ProgressBar $percent={progress} />
    </ProgressBarContainer>
  );
};

Dynamic Props (video)

Targeting Components

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

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

const flip = keyframes`
  from { transform: rotateY(0deg); }
  to { transform: rotateY(360deg); }
`;

const Glow = styled.div`
  /* Add your Glow component styles here */
`;

const Text = styled.span`
  display: inline-block;
  ${Glow}:hover & {
    animation: 1.5s ${flip} ease-out;
  }
`;

const ExampleComponent = () => {
  return (
    <Glow>
      <Text>This text will flip on hover.</Text>
    </Glow>
  );
};

Nesting

next-yak supports nesting out of the box.
For now 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:

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

Nesting Example (video)

Motivation

Most of the existing CSS-in-JS libraries are either slow or have a complex api. This project tries to find a middle ground between speed and api complexity.

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

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.

Performance Gains

CSS Extract

How it works

next-yak converts css-in-js into css modules. 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.

Compile Flow

Atomic CSS

next-yak ships with atomic css support
So you can use tailwind out of the box without additonal configuration.

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;
 }
`;

// 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'
        )
      : 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'
        )}
`;

Build Time Constants

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:

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

const Container = styled.div`
  padding: ${spacings.md};
  ${breakpoints.md} {
    padding: ${spacings.lg};
  }
`;
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)

Yak shaving

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 ❤️

PRS - 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
Experiments Incomplete work in progress experiments to test the features and performance of next-yak: - https://next-yak-benchmark.vercel.app/ - https://yacijs.vercel.app/

SWC

A yak Riding on a rusty SWC Rocket

Next-yak provides a SWC Rust Plugin on npm. It will become the default plugin for next-yak in future but unfortunately it doesn't work on Stackblitz because of https://github.com/swc-project/swc/issues/3934

To use it locally you can install it via:

npm install yak-swc

And activate it in your next.config.js:

// next.config.js
import { withYak } from "next-yak/withYakSwc";

const nextConfig = {
  // your next.js config
};

export default withYak(nextConfig);

Acknowledgments

Massive kudos to:

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

License

next-yak is licensed under the MIT License.


Feel free to reach out with any questions, issues, or suggestions!