Saturday, July 16, 2022
HomeWeb DevelopmentConstruct your individual type-safe model of Tailwind CSS with Sprinkles

Construct your individual type-safe model of Tailwind CSS with Sprinkles


I’m a giant fan of the CSS library vanilla-extract; in my view, it’s probably the most thrilling CSS device since Tailwind CSS. In case you are new to vanilla-extract, it’s a CSS-in-JS library created by Mark Dalgleish that permits you to harness the ability of TypeScript to your CSS.

I’m particularly enthusiastic about vanilla-extract’s killer function, the Sprinkles API. Sprinkles permits you to create a totally custom-made design system from first ideas. You should utilize Sprinkles to create your individual atomic, utility-first CSS framework as a substitute of utilizing off-the-shelf libraries, akin to Tailwind and Tachyon.

On this tutorial, we’ll arrange a brand new Subsequent.js app and use vanilla-extract’s Sprinkles API to create a customized, utility-first CSS framework known as Sprinkles Tailwind. We are going to then use our new framework to recreate the product card demo on the Tailwind CSS homepage. Alongside the best way, we’ll see how vanilla-extract and Sprinkles take care of pseudo-classes, responsive design, and darkish mode themes.

Contents

Why use Sprinkles?

So why would we develop our personal Sprinkles-based, utilities-first CSS library after we can merely use Tailwind, which is already such a preferred device? Three causes: kind security, customization, and native scope.

First, with Sprinkles, you get the identical kind hints and compile-time verification as you’ll with every other Typescript variable, no extra documentation verification and sophistication title typos.

Second, Tailwind is each opinionated and has a little bit of a studying curve, particularly if you’re not utilizing the Tailwind preset syntax. With Sprinkles, you’re writing CSS as a substitute of Tailwind’s shorthand class names. And since Sprinkles doesn’t begin with any inbuilt values, you get to create your CSS framework simply the best way you prefer it.

Lastly, there’s native scope. One of many advantages of vanilla-extract with CSS modules below the hood is that your entire utility courses are actually component-scoped by default. This implies you’ll be able to simply incorporate a number of design methods inside a single app with out worrying about model conflicts.

Let’s first begin by creating a brand new Subsequent utility and putting in vanilla-extract. You may take a look at the whole supply code for this demo venture on the GitHub repo right here.

Begin by creating a brand new TypeScript model of Subsequent like so:

npx [email protected] --typescript sprinkles-tailwind

After operating yarn dev to verify our set up is working as supposed, we’ll set up vanilla-extract and Sprinkles:

yarn add @vanilla-extract/css @vanilla-extract/babel-plugin @vanilla-extract/next-plugin @vanilla-extract/sprinkles

Subsequent, embrace the vanilla-extract/next-plugin in our webpack construct course of by updating subsequent.config.js to the next:

const {
  createVanillaExtractPlugin
} = require('@vanilla-extract/next-plugin');
const withVanillaExtract = createVanillaExtractPlugin();

/** @kind {import('subsequent').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
};

module.exports = withVanillaExtract(nextConfig);

Lastly, as a result of we wish our new Sprinkles varieties to be picked up by the TypeScript compiler and our IDE’s TypeScript server, we’ll add *.css.ts to our venture’s tsconfig.json:

{
    ...
    "embrace": ["next-env.d.ts", "**/*.ts", "**/*.css.ts", "**/*.tsx"],
    ...
}

Making a design system with Sprinkles

One of many most important promoting factors of Tailwind CSS is the way it forces customers to work throughout the constraints of a well-defined design system as a substitute of inconsistent, one-off values.

Tailwind additionally comes prepackaged with a default system with wise values and cheap shortcuts for frequent structure wants. Within the case of our venture, Sprinkles Tailwind, we’ll take the colour palette, typography, and dimension scales from the Open Props venture. We’ll additionally add some frequent Tailwind values for structure.

To get began, we’ll first create a colours.css.ts file below the ./kinds listing and outline our shade values:

const palette = {

'gray-0': '#f8f9fa',

'gray-1': '#f1f3f5',
... 

'gray-9': '#212529',

'red-0': '#fff5f5',

... /* See https://github.com/Shimin-Zhang/TailSprinkles/blob/most important/kinds/colours.css.ts for full palette */

};


export default palette;

We’ll do the identical for border.css.ts, dimension.css.ts, typography.css.ts to arrange the inspiration of our design system.

As soon as we’ve arrange our design system values, we’ll outline our Sprinkles system like so:

import {
  defineProperties,
  createSprinkles,
} from '@vanilla-extract/sprinkles';

import palette from './colours.css';
import { fontFamily, fontWeight, lineHeight, fontSize } from './typography.css';
import { dimension, area } from './dimension.css';
import { borderSize, borderRadius } from './border.css';


const responsiveProperties = defineProperties({
  properties: {
    show: ['none', 'flex', 'block', 'inline'],
    flexDirection: ['row', 'column'],
    justifyContent: [
      'stretch',
      'flex-start',
      'center',
      'flex-end',
      'space-around',
      'space-between'
    ],
    alignItems: [
      'stretch',
      'flex-start',
      'center',
      'flex-end'
    ],
    paddingTop: area,
    paddingBottom: area,
    paddingLeft: area,
    paddingRight: area,
...
  },
  shorthands: {
    padding: [
      'paddingTop',
      'paddingBottom',
      'paddingLeft',
      'paddingRight'
    ],
    paddingX: ['paddingLeft', 'paddingRight'],
    paddingY: ['paddingTop', 'paddingBottom'],
...
    placeItems: ['justifyContent', 'alignItems']
  }
});


const systemProperties = defineProperties({
  properties: {
    shade: palette,
    background: palette,
    fontFamily: fontFamily,
    fontSize: fontSize,
    ...
  }
});

export const sprinkles = createSprinkles(
  responsiveProperties,
  systemProperties
);

export kind Sprinkles = Parameters<typeof sprinkles>[0];

Sprinkles’ defineProperties operate transforms our design system tokens right into a set of favor values. These values are then mixed by way of the createSprinkles so customers can devour them in a CSS-in-JS vogue.

Word that we separated our properties into two separate classes. responsiveProperties are ones we’ll later model in a responsive method just like Tailwind’s viewport prefix, i.e., md:. System properties will probably be conditioned on theme, just like Tailwind’s darkish: prefix. We then export each the Sprinkles object that generates atomic courses and its kind, which provides us kind security.

The shorthands key permit us to outline CSS shorthand values just like CSS shorthand and the Tailwind px-*, p-* courses.

Subsequent, we’ll create a brand new web page and import our new type-safe utilities framework:

import Head from 'subsequent/head'
import { sprinkles } from '../kinds/sprinkles.css';

export default operate Colours() {
    return (
        <div>
            <Head>
            </Head>
            <h1 className={
              sprinkles({
                ... kinds go in right here
              })
            }>
                Welcome To Colours
            </h1>
        </div>
    );
}

Evaluating our Sprinkles library to Tailwind CSS

Let’s see Sprinkles’ kind security in motion. First, we’ll attempt to add a legitimate CSS property, backgroundColor, which isn’t outlined in our Sprinkles app. We see that it throws a kind error:

Error message when user inputs an unknown style prop

Subsequent, we add the shade key and see the checklist of legitimate values present up as strategies:

Auto-complete CSS values

As anticipated, utilizing a shade worth not throughout the system throws a kind error instantly:

Error message when user inputs an unknown style value

That is a lot quicker suggestions cycle than trying up the proper Tailwind class title in documentation, making a typo, then ready for a browser refresh earlier than noticing it.

With the design system all arrange, we will now take the product card instance from the Tailwind homepage, copy the instance HTML, and translate it into our new Sprinkles Tailwind framework:

<h1>
  Tailwind Sprinkles
</h1>
<div 
className = {
  st({
    show: 'flex',
    fontFamily: 'sans-serif',
    width: 'size-content-3',
    marginY: 'size-auto',
    background: 'white',
    borderRadius: 'radius-3',
    boxShadow: 'shadow-2'
  })
}>
  <div 
    className = {
      st({
        flex: 'none',
        width: 'size-12',
        top: 'size-13',
        place: 'relative',
      })
    }
    >
    <img src="https://weblog.logrocket.com/jacket.jpg" alt="" 
    loading="lazy" className = {
      st({
        place: 'absolute',
        width: 'full',
        top: 'full',
        objectFit: 'cowl',
        inset: '0'
      })}/>
  </div>
    ... 
    /* See https://github.com/Shimin-Zhang/TailSprinkles/blob/most important/pages/index.tsx for full file*/

As a result of we’ve outlined our shorthands in our sprinkles.css.ts file, we’re ready to make use of marginY in our kinds as a substitute of writing marginTop and marginBottom individually.

We run into just a few points whereas styling our product card that can’t be solved by Sprinkles alone. Sprinkles is a minimal abstraction layer on high of plain CSS properties, so we wouldn’t have entry to the utility courses like srOnly and the pseudo choose :hover to point our buttons are clickable.

We have to herald vanilla-extract to create these extra advanced kinds:

// First we import vanilla-extract
import { model } from '@vanilla-extract/css';

// We create extra advanced courses
export const srOnly = model({
  place: 'absolute',
  width: '1px',
  top: '1px',
  padding: '0',
  margin: '-1px',
  overflow: 'hidden',
  clip: 'rect(0, 0, 0, 0)',
  whiteSpace: 'nowrap',
  borderWidth: '0'
});

export const clickable = model ({
  ':hover': {
    cursor: 'pointer'
  }
})

It took some configuring, however we managed to breed the identical jacket product card! Whereas this isn’t an actual copy (our design system relies off of Open Props), it’s a cheap copy that captures the spirit of the unique design:

Product card replication with Sprinkles

Taking a more in-depth have a look at the generated HTML, we see that Sprinkles generated prefixed utility class names primarily based on our design tokens. Word that every class is scoped to the element by way of CSS modules:

A chrome devtools panel showing encapsulated class names

Implementing themes and responsive design

Our card is trying nice on desktop, however it isn’t responsive after we shrink our viewport. To repair this, we’ll add viewport-based situations to our Sprinkles setup. Bear in mind earlier we named our structure properties responsiveProperties? We’ll add that viewport data now:

const responsiveProperties = defineProperties({
  situations: {
    cellular: {},
    pill: { '@media': 'display and (min-width: 768px)' },
  },
  defaultCondition: 'pill',
  properties: {
    ... identical as earlier than

As a result of we began with our desktop variation, we’re unable to start out with mobile-first design and can default to pill view as a substitute. Responsive kinds are simple to create with Sprinkles; as a substitute of the property: worth format we’ve seen to this point, we’ll use the property: { condition-name: worth } syntax as a substitute.

Right here’s what a element appears like with the extra cellular viewport structure:

        <div 
        className = {
          st({
            show: 'flex',
            fontFamily: 'sans-serif',
            width: {
              pill: 'size-content-3',
              cellular: 'size-auto'
            },
            marginY: 'size-auto',
            marginX: {
              pill: 'size-auto',
              cellular: 'size-3',
            },
            background: 'white',
            borderRadius: 'radius-3',
            boxShadow: 'shadow-2'
          })
        }>

After including the remainder of cellular viewport layouts, our card is now responsive:

Mobile view-port card

Our product card is actually coming collectively!

Including a darkish theme

One final function that we generally want for our elements lately is the power to toggle between numerous themes. Let’s add a darkish theme to our product card to exhibit the way it’s executed with Sprinkles.

Like with responsive design, we first begin by including a situation and a default worth to our system properties:

const systemProperties = defineProperties({
  situations: {
    lightMode: {},
    darkMode: { '@media': '(prefers-color-scheme: darkish)' }
  },
  defaultCondition: 'lightMode',

Not like with our responsive sizing, right here the default lightMode is already what we wished. And we solely have so as to add darkish theme variations when there’s a necessity. And similar to with responsive props, we’ll use the situation title to distinguish the 2 themes.

The <div> from earlier with each responsive design and darkish mode now appears like this:

<div 
className = {
  st({
    show: 'flex',
    fontFamily: 'sans-serif',
    width: {
      pill: 'size-content-3',
      cellular: 'size-auto'
    },
    marginY: 'size-auto',
    marginX: {
      pill: 'size-auto',
      cellular: 'size-3',
    },
    background: {
     lightMode: 'white',
     darkMode: 'grape-9',
    },
    borderRadius: 'radius-3',
    boxShadow: 'shadow-2'
  })
}>

Now, our last darkish theme product card appears like this:

Dark theme product card

Conclusion

I hope this submit has satisfied you to offer vanilla-extract a strive subsequent time you begin a brand new venture and are out there for a brand new CSS framework.

Similar to Tailwind CSS, the Sprinkles framework will let you create a utilities-first CSS framework that’s constructed on a design methods basis.

Not like Tailwind CSS, with Sprinkles you get the identical type-safe assure that you simply do with remainder of your TypeScript recordsdata, you get to totally customise it the best way you wish to, and also you get the scope security of CSS modules.

I’m extraordinarily bullish on the type-safe CSS paradigm that vanilla-extract introduces. Please share your ideas and expertise migrating from Tailwind CSS to Sprinkles within the remark part under.

Is your frontend hogging your customers’ CPU?

As internet frontends get more and more advanced, resource-greedy options demand increasingly more from the browser. When you’re taken with monitoring and monitoring client-side CPU utilization, reminiscence utilization, and extra for your entire customers in manufacturing, strive LogRocket.https://logrocket.com/signup/

LogRocket is sort of a DVR for internet and cellular apps, recording every part that occurs in your internet app or web site. As a substitute of guessing why issues occur, you’ll be able to mixture and report on key frontend efficiency metrics, replay consumer classes together with utility state, log community requests, and mechanically floor all errors.

Modernize the way you debug internet and cellular apps — .

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments