Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save SeyyedKhandon/5d5327a979230bfc16f54d4cf1b5de55 to your computer and use it in GitHub Desktop.
Save SeyyedKhandon/5d5327a979230bfc16f54d4cf1b5de55 to your computer and use it in GitHub Desktop.
Notes on TailwindCSS from Zero to Production

Notes on "TailwindCcss From Zero to Production" series:

Here is the Tutorial on Tailwindcss youtube channel

Here is the article on dev.to

Here is the code for each episode

Development start from mobile size < 640px:

we start design development from mobile size which is < 640px

sm: 640px => @media (min-width: 640px)
md: 768px => @media (min-width: 768px)
lg: 1024px => @media (min-width: 1024px)
xl: 1280px => @media (min-width: 1280px)
2xl: 1536px => @media (min-width: 1536px)
  1. It's very common for designers to use very dark gray instead of pure black, to make things a little bit easier on the eyes! text-gray-900
  2. we use a mt-2 instead of mt-6 to make the paragraph feel connected to it's heading
<h1 className="mt-6 text-2xl font-bold text-gray-900">
  You can work from anywhere.{" "}
  <span className="text-indigo-600">Take advantage of it!</span>
</h1>
<p className="mt-2 text-gray-600">
  Workcation helps you find work-friendly rentals in beautiful locations so you
  can enjoy some nice weather even when you're not on vacation.
</p>
  1. for setting gap between items with margin, we can use space-y-6

  2. for a button which is a link:

    • we use anchor tag instead of button tag
    • because anchor is inline element we wrap it inside a div element
    • because it doesn't respect our vertical spacing, we set an inline-block class to anchor tag,
    • whenever using uppercase, it's better to increase the letter spacing with tracking-wider class
<div className="mt-4">
  <a
    className="inline-block bg-indigo-600 px-5 py-3
            text-gray-100 font-semibold rounded-lg uppercase tracking-wider
              text-sm shadow-lg"
    href="#"
  >
    Book your escape
  </a>
</div>

Responsive for larger screens

sm breakpoint: 640px<=

  1. we will expand the container max width for screens >= 640px usin sm:max-w-xl:
<div className="px-8 py-10 max-w-md mx-auto sm:max-w-xl">...</div>
  1. and make fonts size a double and the gap between items bigger using sm: prefixes:
<img
  className="mt-6 rounded-lg shadow-xl sm:mt-8"
  src="../src/assets/beach-work.jpg"
  alt="woman workcationing on the beach"
/>
<h1 className="mt-6 text-2xl font-bold text-gray-900 sm:mt-8 sm:text-4xl">
  You can work from anywhere.
  <br />
  <span className="text-indigo-600">Take advantage of it!</span>
</h1>
<p className="mt-2 text-gray-600 sm:mt-4 sm:text-xl">
  Workcation helps you find work-friendly rentals in beautiful locations so you
  can enjoy some nice weather even when you're not on vacation.
</p>
<div className="mt-4 sm:mt-6">
  <a
    className="inline-block bg-indigo-600 px-5 py-3
            text-gray-100 font-semibold rounded-lg uppercase tracking-wider 
              text-sm shadow-lg sm:text-base"
    href="#"
  >
    Book your escape
  </a>
</div>
  1. the image took too much of the screen so we will set an height to it using sm:h-64 and to force it to take full width we set sm:w-full and to prevent stretching sm:object-cover sm:object-center
<img
  className="mt-6 rounded-lg shadow-xl sm:mt-8 sm:h-64 sm:w-full sm:object-cover sm:object-center"
  src="../src/assets/beach-work.jpg"
  alt="woman workcationing on the beach"
/>

md breakpoint: 768px<=

we can expand the max width again here, but i'm not sure if it adds much value here, so we will skip this breakpoint and got to the next one

lg breakpoint 1024px<=

at this break point we want to change the layout and put the image on right and text on the left of the page:

  1. we use lg:hidden on the image of the first container
<img
  className="mt-6 rounded-lg shadow-xl sm:mt-8 sm:h-64 sm:w-full sm:object-cover sm:object-center lg:hidden"
  src="../src/assets/beach-work.jpg"
  alt="woman workcationing on the beach"
/>
  1. create a copy of the above image at the new container after the first container which is shown on:
<div className="hidden lg:block">
  <img
    src="../src/assets/beach-work.jpg"
    alt="woman workcationing on the beach"
  />
</div>
  1. now we want to make these to next to each other, we can use flex but we use grid grid-cols-2 on their parents:
<div className="bg-gray-100 grid lg:grid-cols-2">...</div>
  1. we need to increase the space of the text section to make it a little better to read lg:px-12 lg:py-24 lg:max-w-full:

  2. now we want to make the heading smaller because it is too big for the left section lg:text-3xl

  3. and also to make image take the full height with out distortion we set h-full object-cover object-center on it

xl breakpoint: 1280px<=

it seems ok but we can tweak it :

  1. make the h1 text bigger: using xl:text-4xl
<h1
  className="mt-6 text-2xl font-bold text-gray-900 sm:mt-8 sm:text-4xl lg:text-3xl xl:text-4xl"
>
  You can work from anywhere.
  <br />
  <span className="text-indigo-600">Take advantage of it!</span>
</h1>
  1. to prevent unlimited streching of the left section, we will set a max width again using xl:max-w-xl but we will wrap the content inside another div, because we dont want to to apply on the left grid column it'self
<div className="bg-gray-100 grid lg:grid-cols-2">
  <!-- left section -->
  <div
    className="px-8 py-10 max-w-md mx-auto sm:max-w-xl lg:px-12 lg:py-24 lg:max-w-full xl:mr-0"
  >
    <!-- inner div for xl -->
    <div className="xl:max-w-xl">....</div>
  </div>
  <!-- right section -->
  <div className="hidden lg:block">
    <img
      className="h-full object-cover object-center"
      src="../src/assets/beach-work.jpg"
      alt="woman workcationing on the beach"
    />
  </div>
</div>

2xl breakpoint: 1536px<=

we want to make images bigger on 2xl screens

  1. we change grid columns from 2 to 5 and set 3 spans on the image and 2 spans for the text contents using 2xl:grid-cols-5 on the parent and 2xl:col-span-2 on the left section and 2xl:col-span-3 on the right:
<div className="bg-gray-100 grid lg:grid-cols-2 2xl:grid-cols-5">
  <!-- left section with 2 col -->
  <div
    className="px-8 py-10 max-w-md mx-auto sm:max-w-xl lg:px-12 lg:py-24 lg:max-w-full xl:mr-0 2xl:col-span-2"
  ></div>
  <!-- right section-image section with 3 cols -->
  <div className="hidden lg:block 2xl:col-span-3"></div>
</div>

Add hover, transition, focus, active states:

  1. hover: we decrease the bg color of anchor button on hover by on layer from 600 to 500 hover:bg-indigo-500;

  2. transition: we want a smooth translate to up in y axis: hover:-translate-y-0.5 transition

  3. focus: we will set a beautiful outline for it: focus:outline-none focus:ring focus:ring-offset-2 focus:ring-indigo-500 focus:ring-opacity-50

  4. active: we can set active via active:bg-indigo-700

<div className="mt-4 sm:mt-6">
  <a
    className="inline-block bg-indigo-600 hover:bg-indigo-500 hover:-translate-y-0.5 transition 
              focus:outline-none focus:ring focus:ring-offset-2 focus:ring-indigo-500 focus:ring-opacity-50 active:bg-indigo-700  px-5 py-3
            text-gray-100 font-semibold rounded-lg uppercase tracking-wider 
              text-sm shadow-lg sm:text-base"
    href="#"
  >
    Book your escape
  </a>
</div>

SOLID principle

Composing utilities with @apply and @layer inside index.css:

For SOLID we can extract duplicated code for reusable components:

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer components {
  .btn {
    @apply inline-block px-5 py-3 focus:outline-none focus:ring focus:ring-offset-2 focus:ring-opacity-50
    font-semibold rounded-lg uppercase tracking-wider text-sm sm:text-base;
  }
  .btn-primary {
    @apply bg-indigo-600 hover:bg-indigo-500 hover:-translate-y-0.5 transition 
    focus:ring-indigo-500 active:bg-indigo-700 text-gray-100 shadow-lg;
  }
  .btn-secondary {
    @apply bg-gray-300 hover:bg-gray-200 focus:ring-gray-300 active:bg-gray-400 text-gray-800;
  }
}

use it like this:

<div className="mt-4 sm:mt-6 space-x-1">
  <a className="btn btn-primary" href="#"> Book your escape </a>
  <a className="btn btn-secondary" href="#"> Learn more </a>
</div>

It's better not to extract every classes from html and put them inside of css because you will end-up with an html with lots of classes that makes you always back to the css file to understand what does each of those class means! so use @apply for the components like button which will be used very much and avoid using for layouts etc.... be cause we want to understand the code quickly when we read it 3 months later.

Re-useable components with reactjs:

tailwindcss's idea is never leave html! so when ever using @apply stick to a single html element like button, headline, form input. whenever you find yourself trying to abstract bigger components like a card component with an image, heading, text, there is a better way to handle this , and it is using re-useable components using a library like reactjs:

  • preferred version🌺:
// card component in reactjs in preferred way
// so we dont need a .css file for card too
import React from "react";
export default function DestinationCard({ destination }) {
  return (
    <div className="flex items-center rounded-lg bg-white shadow-lg overflow-hidden">
      <img
        className="h-32 w-32 flex-shrink-0"
        src="{destination.imageUrl}"
        alt="{destination.imageAlt}"
      />
      <div className="px-6 py-4">
        <h3 className="text-lg font-semibold text-gray-800">
          {destination.city}
        </h3>

        <p className="text-gray-600">
          ${destination.averagePrice} / night average
        </p>
        <div className="mt-4">
          <a
            href="#"
            className="text-indigo-600 hover:text-indigo-500 font-semibold text-sm"
          >
            Explore {destination.propertyCount} properties
          </a>
        </div>
      </div>
    </div>
  );
}
  • over engineered or old version 🚨: this looks nicer, but:
  1. at the development time you have to think about naming and name conventions especially across the team developers which is a heavy and time consuming process
  2. you have to always think where in which files you should put these classes, (layout folder, layout.css, components etc...)
  3. you have to always look for and see inside .css files to see what does each class do! So, finally you will end-up with a more difficult maintainable project!
  4. And for using one class we will be forced to always to use them in the same order in the markup and it makes it harder to changing one element.
// card component in reactjs in old way
// you have to leave the jsx file and always check .css to see what does each class do!
// also you always have to face uf with the challenge of thinking on picking a better name which is super time consuming!
import React from "react";
export default function DestinationCard({ destination }) {
  return (
<div className="card-container">
  <img
    className="card-image"
    src="{destination.imageUrl}"
    alt="{destination.imageAlt}"
  />
  <div className="card-content-wrapper">
    <h3 className="card-title">{destination.city}</h3>

    <p className="card-text">${destination.averagePrice} / night average</p>
    <div className="mt-4">
      <a href="#" className="card-property">
        Explore {destination.propertyCount} properties
      </a>
    </div>
  </div>
</div>);

Customizing your Design System

by running npx tailwindcss init tailwind-full.config.js --full, tailwind will generate the current version of configs that is being used by it in the file tailwind-full.config.js, so you can open it and check it. to set this file as you new configuration you have to add this file to config option inside postcss.config.cjs or postcss.config.js:

module.exports = {
  plugins: {
    tailwindcss: {
      config: "./tailwind-full.config.js",
    },
    autoprefixer: {},
  },
};
Full config on v3.1
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [],
  presets: [],
  darkMode: "media", // or 'class'
  theme: {
    accentColor: ({ theme }) => ({
      ...theme("colors"),
      auto: "auto",
    }),
    animation: {
      none: "none",
      spin: "spin 1s linear infinite",
      ping: "ping 1s cubic-bezier(0, 0, 0.2, 1) infinite",
      pulse: "pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
      bounce: "bounce 1s infinite",
    },
    aria: {
      checked: 'checked="true"',
      disabled: 'disabled="true"',
      expanded: 'expanded="true"',
      hidden: 'hidden="true"',
      pressed: 'pressed="true"',
      readonly: 'readonly="true"',
      required: 'required="true"',
      selected: 'selected="true"',
    },
    aspectRatio: {
      auto: "auto",
      square: "1 / 1",
      video: "16 / 9",
    },
    backdropBlur: ({ theme }) => theme("blur"),
    backdropBrightness: ({ theme }) => theme("brightness"),
    backdropContrast: ({ theme }) => theme("contrast"),
    backdropGrayscale: ({ theme }) => theme("grayscale"),
    backdropHueRotate: ({ theme }) => theme("hueRotate"),
    backdropInvert: ({ theme }) => theme("invert"),
    backdropOpacity: ({ theme }) => theme("opacity"),
    backdropSaturate: ({ theme }) => theme("saturate"),
    backdropSepia: ({ theme }) => theme("sepia"),
    backgroundColor: ({ theme }) => theme("colors"),
    backgroundImage: {
      none: "none",
      "gradient-to-t": "linear-gradient(to top, var(--tw-gradient-stops))",
      "gradient-to-tr":
        "linear-gradient(to top right, var(--tw-gradient-stops))",
      "gradient-to-r": "linear-gradient(to right, var(--tw-gradient-stops))",
      "gradient-to-br":
        "linear-gradient(to bottom right, var(--tw-gradient-stops))",
      "gradient-to-b": "linear-gradient(to bottom, var(--tw-gradient-stops))",
      "gradient-to-bl":
        "linear-gradient(to bottom left, var(--tw-gradient-stops))",
      "gradient-to-l": "linear-gradient(to left, var(--tw-gradient-stops))",
      "gradient-to-tl":
        "linear-gradient(to top left, var(--tw-gradient-stops))",
    },
    backgroundOpacity: ({ theme }) => theme("opacity"),
    backgroundPosition: {
      bottom: "bottom",
      center: "center",
      left: "left",
      "left-bottom": "left bottom",
      "left-top": "left top",
      right: "right",
      "right-bottom": "right bottom",
      "right-top": "right top",
      top: "top",
    },
    backgroundSize: {
      auto: "auto",
      cover: "cover",
      contain: "contain",
    },
    blur: {
      0: "0",
      none: "0",
      sm: "4px",
      DEFAULT: "8px",
      md: "12px",
      lg: "16px",
      xl: "24px",
      "2xl": "40px",
      "3xl": "64px",
    },
    borderColor: ({ theme }) => ({
      ...theme("colors"),
      DEFAULT: theme("colors.gray.200", "currentColor"),
    }),
    borderOpacity: ({ theme }) => theme("opacity"),
    borderRadius: {
      none: "0px",
      sm: "0.125rem",
      DEFAULT: "0.25rem",
      md: "0.375rem",
      lg: "0.5rem",
      xl: "0.75rem",
      "2xl": "1rem",
      "3xl": "1.5rem",
      full: "9999px",
    },
    borderSpacing: ({ theme }) => ({
      ...theme("spacing"),
    }),
    borderWidth: {
      DEFAULT: "1px",
      0: "0px",
      2: "2px",
      4: "4px",
      8: "8px",
    },
    boxShadow: {
      sm: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
      DEFAULT: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
      md: "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
      lg: "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)",
      xl: "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)",
      "2xl": "0 25px 50px -12px rgb(0 0 0 / 0.25)",
      inner: "inset 0 2px 4px 0 rgb(0 0 0 / 0.05)",
      none: "none",
    },
    boxShadowColor: ({ theme }) => theme("colors"),
    brightness: {
      0: "0",
      50: ".5",
      75: ".75",
      90: ".9",
      95: ".95",
      100: "1",
      105: "1.05",
      110: "1.1",
      125: "1.25",
      150: "1.5",
      200: "2",
    },
    caretColor: ({ theme }) => theme("colors"),
    colors: ({ colors }) => ({
      inherit: colors.inherit,
      current: colors.current,
      transparent: colors.transparent,
      black: colors.black,
      white: colors.white,
      slate: colors.slate,
      gray: colors.gray,
      zinc: colors.zinc,
      neutral: colors.neutral,
      stone: colors.stone,
      red: colors.red,
      orange: colors.orange,
      amber: colors.amber,
      yellow: colors.yellow,
      lime: colors.lime,
      green: colors.green,
      emerald: colors.emerald,
      teal: colors.teal,
      cyan: colors.cyan,
      sky: colors.sky,
      blue: colors.blue,
      indigo: colors.indigo,
      violet: colors.violet,
      purple: colors.purple,
      fuchsia: colors.fuchsia,
      pink: colors.pink,
      rose: colors.rose,
    }),
    columns: {
      auto: "auto",
      1: "1",
      2: "2",
      3: "3",
      4: "4",
      5: "5",
      6: "6",
      7: "7",
      8: "8",
      9: "9",
      10: "10",
      11: "11",
      12: "12",
      "3xs": "16rem",
      "2xs": "18rem",
      xs: "20rem",
      sm: "24rem",
      md: "28rem",
      lg: "32rem",
      xl: "36rem",
      "2xl": "42rem",
      "3xl": "48rem",
      "4xl": "56rem",
      "5xl": "64rem",
      "6xl": "72rem",
      "7xl": "80rem",
    },
    container: {},
    content: {
      none: "none",
    },
    contrast: {
      0: "0",
      50: ".5",
      75: ".75",
      100: "1",
      125: "1.25",
      150: "1.5",
      200: "2",
    },
    cursor: {
      auto: "auto",
      default: "default",
      pointer: "pointer",
      wait: "wait",
      text: "text",
      move: "move",
      help: "help",
      "not-allowed": "not-allowed",
      none: "none",
      "context-menu": "context-menu",
      progress: "progress",
      cell: "cell",
      crosshair: "crosshair",
      "vertical-text": "vertical-text",
      alias: "alias",
      copy: "copy",
      "no-drop": "no-drop",
      grab: "grab",
      grabbing: "grabbing",
      "all-scroll": "all-scroll",
      "col-resize": "col-resize",
      "row-resize": "row-resize",
      "n-resize": "n-resize",
      "e-resize": "e-resize",
      "s-resize": "s-resize",
      "w-resize": "w-resize",
      "ne-resize": "ne-resize",
      "nw-resize": "nw-resize",
      "se-resize": "se-resize",
      "sw-resize": "sw-resize",
      "ew-resize": "ew-resize",
      "ns-resize": "ns-resize",
      "nesw-resize": "nesw-resize",
      "nwse-resize": "nwse-resize",
      "zoom-in": "zoom-in",
      "zoom-out": "zoom-out",
    },
    divideColor: ({ theme }) => theme("borderColor"),
    divideOpacity: ({ theme }) => theme("borderOpacity"),
    divideWidth: ({ theme }) => theme("borderWidth"),
    dropShadow: {
      sm: "0 1px 1px rgb(0 0 0 / 0.05)",
      DEFAULT: ["0 1px 2px rgb(0 0 0 / 0.1)", "0 1px 1px rgb(0 0 0 / 0.06)"],
      md: ["0 4px 3px rgb(0 0 0 / 0.07)", "0 2px 2px rgb(0 0 0 / 0.06)"],
      lg: ["0 10px 8px rgb(0 0 0 / 0.04)", "0 4px 3px rgb(0 0 0 / 0.1)"],
      xl: ["0 20px 13px rgb(0 0 0 / 0.03)", "0 8px 5px rgb(0 0 0 / 0.08)"],
      "2xl": "0 25px 25px rgb(0 0 0 / 0.15)",
      none: "0 0 #0000",
    },
    fill: ({ theme }) => ({
      none: "none",
      ...theme("colors"),
    }),
    flex: {
      1: "1 1 0%",
      auto: "1 1 auto",
      initial: "0 1 auto",
      none: "none",
    },
    flexBasis: ({ theme }) => ({
      auto: "auto",
      ...theme("spacing"),
      "1/2": "50%",
      "1/3": "33.333333%",
      "2/3": "66.666667%",
      "1/4": "25%",
      "2/4": "50%",
      "3/4": "75%",
      "1/5": "20%",
      "2/5": "40%",
      "3/5": "60%",
      "4/5": "80%",
      "1/6": "16.666667%",
      "2/6": "33.333333%",
      "3/6": "50%",
      "4/6": "66.666667%",
      "5/6": "83.333333%",
      "1/12": "8.333333%",
      "2/12": "16.666667%",
      "3/12": "25%",
      "4/12": "33.333333%",
      "5/12": "41.666667%",
      "6/12": "50%",
      "7/12": "58.333333%",
      "8/12": "66.666667%",
      "9/12": "75%",
      "10/12": "83.333333%",
      "11/12": "91.666667%",
      full: "100%",
    }),
    flexGrow: {
      0: "0",
      DEFAULT: "1",
    },
    flexShrink: {
      0: "0",
      DEFAULT: "1",
    },
    fontFamily: {
      sans: [
        "ui-sans-serif",
        "system-ui",
        "-apple-system",
        "BlinkMacSystemFont",
        '"Segoe UI"',
        "Roboto",
        '"Helvetica Neue"',
        "Arial",
        '"Noto Sans"',
        "sans-serif",
        '"Apple Color Emoji"',
        '"Segoe UI Emoji"',
        '"Segoe UI Symbol"',
        '"Noto Color Emoji"',
      ],
      serif: [
        "ui-serif",
        "Georgia",
        "Cambria",
        '"Times New Roman"',
        "Times",
        "serif",
      ],
      mono: [
        "ui-monospace",
        "SFMono-Regular",
        "Menlo",
        "Monaco",
        "Consolas",
        '"Liberation Mono"',
        '"Courier New"',
        "monospace",
      ],
    },
    fontSize: {
      xs: ["0.75rem", { lineHeight: "1rem" }],
      sm: ["0.875rem", { lineHeight: "1.25rem" }],
      base: ["1rem", { lineHeight: "1.5rem" }],
      lg: ["1.125rem", { lineHeight: "1.75rem" }],
      xl: ["1.25rem", { lineHeight: "1.75rem" }],
      "2xl": ["1.5rem", { lineHeight: "2rem" }],
      "3xl": ["1.875rem", { lineHeight: "2.25rem" }],
      "4xl": ["2.25rem", { lineHeight: "2.5rem" }],
      "5xl": ["3rem", { lineHeight: "1" }],
      "6xl": ["3.75rem", { lineHeight: "1" }],
      "7xl": ["4.5rem", { lineHeight: "1" }],
      "8xl": ["6rem", { lineHeight: "1" }],
      "9xl": ["8rem", { lineHeight: "1" }],
    },
    fontWeight: {
      thin: "100",
      extralight: "200",
      light: "300",
      normal: "400",
      medium: "500",
      semibold: "600",
      bold: "700",
      extrabold: "800",
      black: "900",
    },
    gap: ({ theme }) => theme("spacing"),
    gradientColorStops: ({ theme }) => theme("colors"),
    grayscale: {
      0: "0",
      DEFAULT: "100%",
    },
    gridAutoColumns: {
      auto: "auto",
      min: "min-content",
      max: "max-content",
      fr: "minmax(0, 1fr)",
    },
    gridAutoRows: {
      auto: "auto",
      min: "min-content",
      max: "max-content",
      fr: "minmax(0, 1fr)",
    },
    gridColumn: {
      auto: "auto",
      "span-1": "span 1 / span 1",
      "span-2": "span 2 / span 2",
      "span-3": "span 3 / span 3",
      "span-4": "span 4 / span 4",
      "span-5": "span 5 / span 5",
      "span-6": "span 6 / span 6",
      "span-7": "span 7 / span 7",
      "span-8": "span 8 / span 8",
      "span-9": "span 9 / span 9",
      "span-10": "span 10 / span 10",
      "span-11": "span 11 / span 11",
      "span-12": "span 12 / span 12",
      "span-full": "1 / -1",
    },
    gridColumnEnd: {
      auto: "auto",
      1: "1",
      2: "2",
      3: "3",
      4: "4",
      5: "5",
      6: "6",
      7: "7",
      8: "8",
      9: "9",
      10: "10",
      11: "11",
      12: "12",
      13: "13",
    },
    gridColumnStart: {
      auto: "auto",
      1: "1",
      2: "2",
      3: "3",
      4: "4",
      5: "5",
      6: "6",
      7: "7",
      8: "8",
      9: "9",
      10: "10",
      11: "11",
      12: "12",
      13: "13",
    },
    gridRow: {
      auto: "auto",
      "span-1": "span 1 / span 1",
      "span-2": "span 2 / span 2",
      "span-3": "span 3 / span 3",
      "span-4": "span 4 / span 4",
      "span-5": "span 5 / span 5",
      "span-6": "span 6 / span 6",
      "span-full": "1 / -1",
    },
    gridRowEnd: {
      auto: "auto",
      1: "1",
      2: "2",
      3: "3",
      4: "4",
      5: "5",
      6: "6",
      7: "7",
    },
    gridRowStart: {
      auto: "auto",
      1: "1",
      2: "2",
      3: "3",
      4: "4",
      5: "5",
      6: "6",
      7: "7",
    },
    gridTemplateColumns: {
      none: "none",
      1: "repeat(1, minmax(0, 1fr))",
      2: "repeat(2, minmax(0, 1fr))",
      3: "repeat(3, minmax(0, 1fr))",
      4: "repeat(4, minmax(0, 1fr))",
      5: "repeat(5, minmax(0, 1fr))",
      6: "repeat(6, minmax(0, 1fr))",
      7: "repeat(7, minmax(0, 1fr))",
      8: "repeat(8, minmax(0, 1fr))",
      9: "repeat(9, minmax(0, 1fr))",
      10: "repeat(10, minmax(0, 1fr))",
      11: "repeat(11, minmax(0, 1fr))",
      12: "repeat(12, minmax(0, 1fr))",
    },
    gridTemplateRows: {
      none: "none",
      1: "repeat(1, minmax(0, 1fr))",
      2: "repeat(2, minmax(0, 1fr))",
      3: "repeat(3, minmax(0, 1fr))",
      4: "repeat(4, minmax(0, 1fr))",
      5: "repeat(5, minmax(0, 1fr))",
      6: "repeat(6, minmax(0, 1fr))",
    },
    height: ({ theme }) => ({
      auto: "auto",
      ...theme("spacing"),
      "1/2": "50%",
      "1/3": "33.333333%",
      "2/3": "66.666667%",
      "1/4": "25%",
      "2/4": "50%",
      "3/4": "75%",
      "1/5": "20%",
      "2/5": "40%",
      "3/5": "60%",
      "4/5": "80%",
      "1/6": "16.666667%",
      "2/6": "33.333333%",
      "3/6": "50%",
      "4/6": "66.666667%",
      "5/6": "83.333333%",
      full: "100%",
      screen: "100vh",
      min: "min-content",
      max: "max-content",
      fit: "fit-content",
    }),
    hueRotate: {
      0: "0deg",
      15: "15deg",
      30: "30deg",
      60: "60deg",
      90: "90deg",
      180: "180deg",
    },
    inset: ({ theme }) => ({
      auto: "auto",
      ...theme("spacing"),
      "1/2": "50%",
      "1/3": "33.333333%",
      "2/3": "66.666667%",
      "1/4": "25%",
      "2/4": "50%",
      "3/4": "75%",
      full: "100%",
    }),
    invert: {
      0: "0",
      DEFAULT: "100%",
    },
    keyframes: {
      spin: {
        to: {
          transform: "rotate(360deg)",
        },
      },
      ping: {
        "75%, 100%": {
          transform: "scale(2)",
          opacity: "0",
        },
      },
      pulse: {
        "50%": {
          opacity: ".5",
        },
      },
      bounce: {
        "0%, 100%": {
          transform: "translateY(-25%)",
          animationTimingFunction: "cubic-bezier(0.8,0,1,1)",
        },
        "50%": {
          transform: "none",
          animationTimingFunction: "cubic-bezier(0,0,0.2,1)",
        },
      },
    },
    letterSpacing: {
      tighter: "-0.05em",
      tight: "-0.025em",
      normal: "0em",
      wide: "0.025em",
      wider: "0.05em",
      widest: "0.1em",
    },
    lineHeight: {
      none: "1",
      tight: "1.25",
      snug: "1.375",
      normal: "1.5",
      relaxed: "1.625",
      loose: "2",
      3: ".75rem",
      4: "1rem",
      5: "1.25rem",
      6: "1.5rem",
      7: "1.75rem",
      8: "2rem",
      9: "2.25rem",
      10: "2.5rem",
    },
    listStyleType: {
      none: "none",
      disc: "disc",
      decimal: "decimal",
    },
    margin: ({ theme }) => ({
      auto: "auto",
      ...theme("spacing"),
    }),
    maxHeight: ({ theme }) => ({
      ...theme("spacing"),
      none: "none",
      full: "100%",
      screen: "100vh",
      min: "min-content",
      max: "max-content",
      fit: "fit-content",
    }),
    maxWidth: ({ theme, breakpoints }) => ({
      none: "none",
      0: "0rem",
      xs: "20rem",
      sm: "24rem",
      md: "28rem",
      lg: "32rem",
      xl: "36rem",
      "2xl": "42rem",
      "3xl": "48rem",
      "4xl": "56rem",
      "5xl": "64rem",
      "6xl": "72rem",
      "7xl": "80rem",
      full: "100%",
      min: "min-content",
      max: "max-content",
      fit: "fit-content",
      prose: "65ch",
      ...breakpoints(theme("screens")),
    }),
    minHeight: {
      0: "0px",
      full: "100%",
      screen: "100vh",
      min: "min-content",
      max: "max-content",
      fit: "fit-content",
    },
    minWidth: {
      0: "0px",
      full: "100%",
      min: "min-content",
      max: "max-content",
      fit: "fit-content",
    },
    objectPosition: {
      bottom: "bottom",
      center: "center",
      left: "left",
      "left-bottom": "left bottom",
      "left-top": "left top",
      right: "right",
      "right-bottom": "right bottom",
      "right-top": "right top",
      top: "top",
    },
    opacity: {
      0: "0",
      5: "0.05",
      10: "0.1",
      20: "0.2",
      25: "0.25",
      30: "0.3",
      40: "0.4",
      50: "0.5",
      60: "0.6",
      70: "0.7",
      75: "0.75",
      80: "0.8",
      90: "0.9",
      95: "0.95",
      100: "1",
    },
    order: {
      first: "-9999",
      last: "9999",
      none: "0",
      1: "1",
      2: "2",
      3: "3",
      4: "4",
      5: "5",
      6: "6",
      7: "7",
      8: "8",
      9: "9",
      10: "10",
      11: "11",
      12: "12",
    },
    outlineColor: ({ theme }) => theme("colors"),
    outlineOffset: {
      0: "0px",
      1: "1px",
      2: "2px",
      4: "4px",
      8: "8px",
    },
    outlineWidth: {
      0: "0px",
      1: "1px",
      2: "2px",
      4: "4px",
      8: "8px",
    },
    padding: ({ theme }) => theme("spacing"),
    placeholderColor: ({ theme }) => theme("colors"),
    placeholderOpacity: ({ theme }) => theme("opacity"),
    ringColor: ({ theme }) => ({
      DEFAULT: theme("colors.blue.500", "#3b82f6"),
      ...theme("colors"),
    }),
    ringOffsetColor: ({ theme }) => theme("colors"),
    ringOffsetWidth: {
      0: "0px",
      1: "1px",
      2: "2px",
      4: "4px",
      8: "8px",
    },
    ringOpacity: ({ theme }) => ({
      DEFAULT: "0.5",
      ...theme("opacity"),
    }),
    ringWidth: {
      DEFAULT: "3px",
      0: "0px",
      1: "1px",
      2: "2px",
      4: "4px",
      8: "8px",
    },
    rotate: {
      0: "0deg",
      1: "1deg",
      2: "2deg",
      3: "3deg",
      6: "6deg",
      12: "12deg",
      45: "45deg",
      90: "90deg",
      180: "180deg",
    },
    saturate: {
      0: "0",
      50: ".5",
      100: "1",
      150: "1.5",
      200: "2",
    },
    scale: {
      0: "0",
      50: ".5",
      75: ".75",
      90: ".9",
      95: ".95",
      100: "1",
      105: "1.05",
      110: "1.1",
      125: "1.25",
      150: "1.5",
    },
    screens: {
      sm: "640px",
      md: "768px",
      lg: "1024px",
      xl: "1280px",
      "2xl": "1536px",
    },
    scrollMargin: ({ theme }) => ({
      ...theme("spacing"),
    }),
    scrollPadding: ({ theme }) => theme("spacing"),
    sepia: {
      0: "0",
      DEFAULT: "100%",
    },
    skew: {
      0: "0deg",
      1: "1deg",
      2: "2deg",
      3: "3deg",
      6: "6deg",
      12: "12deg",
    },
    space: ({ theme }) => ({
      ...theme("spacing"),
    }),
    spacing: {
      px: "1px",
      0: "0px",
      0.5: "0.125rem",
      1: "0.25rem",
      1.5: "0.375rem",
      2: "0.5rem",
      2.5: "0.625rem",
      3: "0.75rem",
      3.5: "0.875rem",
      4: "1rem",
      5: "1.25rem",
      6: "1.5rem",
      7: "1.75rem",
      8: "2rem",
      9: "2.25rem",
      10: "2.5rem",
      11: "2.75rem",
      12: "3rem",
      14: "3.5rem",
      16: "4rem",
      20: "5rem",
      24: "6rem",
      28: "7rem",
      32: "8rem",
      36: "9rem",
      40: "10rem",
      44: "11rem",
      48: "12rem",
      52: "13rem",
      56: "14rem",
      60: "15rem",
      64: "16rem",
      72: "18rem",
      80: "20rem",
      96: "24rem",
    },
    stroke: ({ theme }) => ({
      none: "none",
      ...theme("colors"),
    }),
    strokeWidth: {
      0: "0",
      1: "1",
      2: "2",
    },
    supports: {},
    data: {},
    textColor: ({ theme }) => theme("colors"),
    textDecorationColor: ({ theme }) => theme("colors"),
    textDecorationThickness: {
      auto: "auto",
      "from-font": "from-font",
      0: "0px",
      1: "1px",
      2: "2px",
      4: "4px",
      8: "8px",
    },
    textIndent: ({ theme }) => ({
      ...theme("spacing"),
    }),
    textOpacity: ({ theme }) => theme("opacity"),
    textUnderlineOffset: {
      auto: "auto",
      0: "0px",
      1: "1px",
      2: "2px",
      4: "4px",
      8: "8px",
    },
    transformOrigin: {
      center: "center",
      top: "top",
      "top-right": "top right",
      right: "right",
      "bottom-right": "bottom right",
      bottom: "bottom",
      "bottom-left": "bottom left",
      left: "left",
      "top-left": "top left",
    },
    transitionDelay: {
      75: "75ms",
      100: "100ms",
      150: "150ms",
      200: "200ms",
      300: "300ms",
      500: "500ms",
      700: "700ms",
      1000: "1000ms",
    },
    transitionDuration: {
      DEFAULT: "150ms",
      75: "75ms",
      100: "100ms",
      150: "150ms",
      200: "200ms",
      300: "300ms",
      500: "500ms",
      700: "700ms",
      1000: "1000ms",
    },
    transitionProperty: {
      none: "none",
      all: "all",
      DEFAULT:
        "color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter",
      colors:
        "color, background-color, border-color, outline-color, text-decoration-color, fill, stroke",
      opacity: "opacity",
      shadow: "box-shadow",
      transform: "transform",
    },
    transitionTimingFunction: {
      DEFAULT: "cubic-bezier(0.4, 0, 0.2, 1)",
      linear: "linear",
      in: "cubic-bezier(0.4, 0, 1, 1)",
      out: "cubic-bezier(0, 0, 0.2, 1)",
      "in-out": "cubic-bezier(0.4, 0, 0.2, 1)",
    },
    translate: ({ theme }) => ({
      ...theme("spacing"),
      "1/2": "50%",
      "1/3": "33.333333%",
      "2/3": "66.666667%",
      "1/4": "25%",
      "2/4": "50%",
      "3/4": "75%",
      full: "100%",
    }),
    width: ({ theme }) => ({
      auto: "auto",
      ...theme("spacing"),
      "1/2": "50%",
      "1/3": "33.333333%",
      "2/3": "66.666667%",
      "1/4": "25%",
      "2/4": "50%",
      "3/4": "75%",
      "1/5": "20%",
      "2/5": "40%",
      "3/5": "60%",
      "4/5": "80%",
      "1/6": "16.666667%",
      "2/6": "33.333333%",
      "3/6": "50%",
      "4/6": "66.666667%",
      "5/6": "83.333333%",
      "1/12": "8.333333%",
      "2/12": "16.666667%",
      "3/12": "25%",
      "4/12": "33.333333%",
      "5/12": "41.666667%",
      "6/12": "50%",
      "7/12": "58.333333%",
      "8/12": "66.666667%",
      "9/12": "75%",
      "10/12": "83.333333%",
      "11/12": "91.666667%",
      full: "100%",
      screen: "100vw",
      min: "min-content",
      max: "max-content",
      fit: "fit-content",
    }),
    willChange: {
      auto: "auto",
      scroll: "scroll-position",
      contents: "contents",
      transform: "transform",
    },
    zIndex: {
      auto: "auto",
      0: "0",
      10: "10",
      20: "20",
      30: "30",
      40: "40",
      50: "50",
    },
  },
  plugins: [],
};

Note: 🚨 By using the full-config it will be hard to track what changes that you have made on the default tailwindcss config, it's like ejecting create-react-app, you have full control over anything but it will be much harder to maintain! So, instead of that, we use extend option in tailwind.config.js file and doing it this way will allows us seamlessly inherit the new future updates added to tailwindcss:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
  theme: {
    extend: {
      colors: {
        brand: "#0fa9e6",
      },
    },
  },
  plugins: [],
};

Now we can use it like this:

<span className="text-brand">Take advantage of it!</span>

We can define multiple brand color shades and font family etc.:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
  theme: {
    extend: {
      colors: {
        primary: {
          light: "#3fbaeb",
          DEFAULT: "#0fa9e6",
          dark: "#0c87b8",
          100: "#cffafe",
          200: "#a5f3fc",
          300: "#67e8f9",
          400: "#22d3ee",
          500: "#06b6d4",
          600: "#0891b2",
          700: "#0e7490",
          800: "#155e75",
          900: "#164e63",
        },
      },
      fontFamily: {
        headline: "Poppins, sans-serif",
      },
    },
  },
  plugins: [],
};

Note: Don't forget to include fonts inside your project for font-family.

<h1 className="mt-6 text-2xl font-bold text-primary-dark">
  You can work from anywhere.
  <br />
  <span className="text-primary-400 font-headline">Take advantage of it!</span>
</h1>

You can read more here: https://tailwindcss.com/docs/configuration

Optimizing for production

When you run npm run build with vite, you will see the output by default is 25KB and the gzip version is 5.55 KB and it purges un-used css classes by default!

❯ npm run build

> mern-blog-frontend@1.0.0 build
> tsc && vite build

vite v4.1.1 building for production...
✓ 156 modules transformed.
dist/index.html                   0.46 kB
dist/assets/index-5002742c.css   25.25 kB │ gzip:  5.55 kB
dist/assets/index-b47a6358.js   253.88 kB │ gzip: 83.09 kB

Using dynamic classes

Because purgcss is simple, for example if you use this, it can not understand that you have used h-10 class, so it will consider h-10 is an unused class and will remove it from your final generated css.

export default function Temp() {
  const size = 10;
  return (
          <img
            className={`h-${size}`}
            src="../src/assets/logo.svg"
            alt="WorCation Logo"
          />)

to make it work it is better to use an object version which you have mentioned directly the name of the class inside your code project:

export default function Temp() {
  const sizeClasses= {
    height: "h-10"
  }
  return (
          <img
            className={`${sizeClasses.height}`}
            src="../src/assets/logo.svg"
            alt="WorCation Logo"
          />)

It will scan this file and sees there is a texth-10 and will consider that you have used this class in your project and it will keep it in the final generated css!

for more info read this:

https://purgecss.com/


The number of installs of our vscode extensions went beyond of 50,000 downloads:✨🎖️🚀 Frontend- VScode Extensions

If you are a front-end developer and want to make your VS code professional, be sure to install this:

VSCode Pro

Tell me your opinion here or on dev article

#cafdex #for_better_life_experience

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment