Here is the Tutorial on Tailwindcss youtube channel
Here is the article on dev.to
Here is the code for each episode
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)
- 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
- we use a
mt-2
instead ofmt-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>
-
for setting gap between items with margin, we can use
space-y-6
-
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>
- 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>
- 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>
- 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 setsm:w-full
and to prevent stretchingsm: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"
/>
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
at this break point we want to change the layout and put the image on right and text on the left of the page:
- 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"
/>
- 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>
- now we want to make these to next to each other, we can use
flex
but we usegrid grid-cols-2
on their parents:
<div className="bg-gray-100 grid lg:grid-cols-2">...</div>
-
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
: -
now we want to make the heading smaller because it is too big for the left section
lg:text-3xl
-
and also to make image take the full height with out distortion we set
h-full object-cover object-center
on it
it seems ok but we can tweak it :
- 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>
- 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>
we want to make images bigger on 2xl screens
- 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 and2xl:col-span-2
on the left section and2xl: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>
-
hover: we decrease the bg color of anchor button on hover by on layer from 600 to 500
hover:bg-indigo-500
; -
transition: we want a smooth translate to up in y axis:
hover:-translate-y-0.5 transition
-
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
-
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>
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.
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:
- 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
- you have to always think where in which files you should put these classes, (layout folder, layout.css, components etc...)
- 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! - 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>);
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
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
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:
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:
Tell me your opinion here or on dev article
#cafdex #for_better_life_experience