R

Rob Austin - Feb 18, 2025

Updating shadcn/ui to Tailwind 4 at Shadcnblocks

We recently updated all of our shadcnblocks.com blocks to Tailwind 4. Here’s a detailed guide of the steps we took to make Tailwind 4 work seamlessly with shadcn/ui themeing.

Install Tailwind 4

For the official upgrade guide from Tailwind 3 => 4 visit the Tailwind docs Tailwind 4 Upgrade Guide.

At Shadcnblocks, we use Next.js, so we opted for the PostCSS installation (not the Vite version). Install the latest version of Tailwind via NPM.

Terminal window
npm install tailwindcss@next @tailwindcss/postcss@next

Next, update your PostCSS configuration to include the Tailwind plugin:

postcss.config.mjs
export default {
plugins: {
"@tailwindcss/postcss": {},
}
}

Now let’s migrate our tailwind config.

Migrating the Tailwind Config to CSS

The biggest change in Tailwind 4 is they have moved to to a CSS-first-configuration — Whcih means the tailwind.config.js has been removed and all configuration now happens directly in the main CSS file.

In Next.js, the default stylesheet is located at src/app/globals.css. So well be moving all our variables and values from the old tailwind config file into this CSS file. This shift simplifies the workflow by consolidating your configuration into a single CSS file, but it does require some initial adjustment.

The old Tailwind 3 stylesheet and config

@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--radius: 0.5rem;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
/* Shadcnblocks.com extra colors */
--muted-2: 0 0% 90%;
--muted-2-foreground: 240 3.8% 46.1%;
}
.dark {
--background: 240 10% 3.9%;
--foreground: 0 0% 98%;
--card: 240 10% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
--muted: 240 3.7% 15.9%;
--muted-foreground: 240 5% 64.9%;
--accent: 240 3.7% 15.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--ring: 240 4.9% 83.9%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
/* Shadcnblocks.com extra colors */
--muted-2: 0, 0%, 18%;
--muted-2-foreground: 240, 2%, 75%;
}
}

The old Tailwind 3 config

tailwind.config.mjs
import type { Config } from "tailwindcss";
const config = {
darkMode: ["class"],
content: [
"./pages/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}",
"./app/**/*.{ts,tsx}",
"./src/**/*.{ts,tsx}",
],
theme: {
container: {
center: true,
padding: "1rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
chart: {
"1": "hsl(var(--chart-1))",
"2": "hsl(var(--chart-2))",
"3": "hsl(var(--chart-3))",
"4": "hsl(var(--chart-4))",
"5": "hsl(var(--chart-5))",
},
muted-2: {
DEFAULT: "hsl(var(--muted-2))",
foreground: "hsl(var(--muted-2-foreground))",
},
},
keyframes: {
"accordion-down": {
from: { height: "0" },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" },
},
"fade-in-out": {
"0%": {
opacity: "0",
},
"20%": {
opacity: "1",
},
"80%": {
opacity: "1",
},
"100%": {
opacity: "0",
},
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
"fade-in-out": "fade-in-out 4s ease-in-out",
},
},
},
plugins: [
require("tailwindcss-animate"),
require("@tailwindcss/typography")
],
} satisfies Config;
export default config;

The new Tailwind 4 stylesheet

@import "tailwindcss";
@custom-variant dark (&:is(.dark *));
@plugin "@tailwindcss/typography";
@plugin "tailwindcss-animate";
:root {
--background: hsl(0 0% 100%);
--foreground: hsl(0 0% 3.9%);
--card: hsl(0 0% 100%);
--card-foreground: hsl(0 0% 3.9%);
--popover: hsl(0 0% 100%);
--popover-foreground: hsl(0 0% 3.9%);
--primary: hsl(0 0% 9%);
--primary-foreground: hsl(0 0% 98%);
--secondary: hsl(0 0% 96%);
--secondary-foreground: hsl(0 0% 9%);
--muted: hsl(0 0% 96%);
--muted-foreground: hsl(0 0% 45.1%);
--accent: hsl(0 0% 96%);
--accent-foreground: hsl(0 0% 9%);
--destructive: hsl(0 84.2% 60.2%);
--destructive-foreground: hsl(0 0% 98%);
--border: hsl(0, 0%, 90%);
--input: hsl(0 0% 89.8%);
--ring: hsl(0 0% 3.9%);
--radius: 8px;
--chart-1: hsl(12 76% 61%);
--chart-2: hsl(173 58% 39%);
--chart-3: hsl(197 37% 24%);
--chart-4: hsl(43 74% 66%);
--chart-5: hsl(27 87% 67%);
/* Shadcnblocks.com */
--muted-2: hsl(0 0% 90%);
--muted-2-foreground: hsl(240 3.8% 46.1%);
}
.dark {
--background: hsl(240 10% 3.9%);
--foreground: hsl(0 0% 98%);
--card: hsl(240 10% 3.9%);
--card-foreground: hsl(0 0% 98%);
--popover: hsl(240 10% 3.9%);
--popover-foreground: hsl(0 0% 98%);
--primary: hsl(0 0% 98%);
--primary-foreground: hsl(240 5.9% 10%);
--secondary: hsl(240 3.7% 15.9%);
--secondary-foreground: hsl(0 0% 98%);
--muted: hsl(240 3.7% 15.9%);
--muted-foreground: hsl(240 5% 64.9%);
--accent: hsl(240 3.7% 15.9%);
--accent-foreground: hsl(0 0% 98%);
--destructive: hsl(0 62.8% 30.6%);
--destructive-foreground: hsl(0 0% 98%);
--border: hsl(240 3.7% 15.9%);
--input: hsl(240 3.7% 15.9%);
--ring: hsl(240 4.9% 83.9%);
--chart-1: hsl(220 70% 50%);
--chart-2: hsl(160 60% 45%);
--chart-3: hsl(30 80% 55%);
--chart-4: hsl(280 65% 60%);
--chart-5: hsl(340 75% 55%);
/* Shadcnblocks.com */
--muted-2: hsl(0, 0%, 18%);
--muted-2-foreground: hsl(240, 2%, 75%);
}
@theme {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: hsl(12 76% 61%);
--color-chart-2: hsl(173 58% 39%);
--color-chart-3: hsl(197 37% 24%);
--color-chart-4: hsl(43 74% 66%);
--color-chart-5: hsl(27 87% 67%);
/* Shadcnblocks.com extra colors */
--color-muted-2: var(--muted-2);
--color-muted-2-foreground: var(--muted-2-foreground);
--color-transparent: transparent;
/* theme overrides */
--radius-xs: calc(var(--radius) - 4px);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1400px; /* default is 1536px */
--animate-accordion-down: accordion-down 0.2s ease-out;
--animate-accordion-up: accordion-up 0.2s ease-out;
--animate-fade-in-out: fade-in-out 4s ease-in-out;
@keyframes accordion-down {
from { height: 0; }
to { height: var(--radix-accordion-content-height); }
}
@keyframes accordion-up {
from { height: var(--radix-accordion-content-height); }
to { height: 0; }
}
@keyframes fade-in-out {
0% { opacity: 0; }
20% { opacity: 1; }
80% { opacity: 1; }
100% { opacity: 0; }
}
}
@utility container {
margin-inline: auto;
padding-inline: 2rem;
}
@layer components {
button {
cursor: pointer;
}
[class*="border"] {
@apply border-border;
}
p {
@apply text-muted-foreground;
}
}
@layer base {
body {
@apply bg-background text-foreground;
}
}

Let’s breakdown the steps we took.

1. Wrap the Shadcn color variables with the hsl color function

In Tailwind 3, the hsl() color function needed to be used inside the tailwind.config.js. Which meant that in your stylesheet the Shadcn css variables needed to defined without the color space function, ie in the css they would look like this --background: 0 0% 100%; instead of hsl(--background: 0 0% 100%);

Which was really frustrating, because it meant the vscode highlighting and vscode color picker didn’t work with Shadcn theming. It also broke the color picker for css variables in chrome dev tools! It seems like this wasnt a big deal for some people, but it was almost a deal breaker for me, its difficult enough already to remember what colors the shadcn theme color variables are 😅. A workaround was to declare the css variable with the full hsl color function in the css but If you use the color function in CSS you won’t be able to do opacity modifiers like bg-background/50.

But anyway, in Tailwind 4, we once again wrap the color variables in hsl() inside the CSS file. Which solves the above problems!

2. Add —color prefix

You will need to either modify the the Shadcn CSS variable names to start with --color OR you can opt to declare the Shadcn theme variables as provided in the :root and re-map them to Tailwind compatible variable names inside of @theme. We opted for the second approach as it keeps a clear seperation with the Shadn theme variables as provided, and our projects variables. in theory you could also move the shadn variables to their own CSS file like shadcn.css and then import that for even clearer seperation ie

3. Migrate radius

The Shadcn radius variable isnt a color, so we dont remap that to --color-radius instead we update it to work with the new Taliwind radius values

@import "tailwindcss";
@theme {
...
--radius-xs: calc(var(--radius) - 4px);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
}

4. Migrate animation and keyframes

The syntax for the keyframes can be touchy, you mostly dont need to use quotes around the keys or values.

@import "tailwindcss";
@theme {
...
--animate-accordion-down: accordion-down 0.2s ease-out;
--animate-accordion-up: accordion-up 0.2s ease-out;
--animate-fade-in-out: fade-in-out 4s ease-in-out;
@keyframes accordion-down {
from {
height: 0;
}
to {
height: var(--radix-accordion-content-height);
}
}
@keyframes accordion-up {
from {
height: var(--radix-accordion-content-height);
}
to {
height: 0;
}
}
@keyframes fade-in-out {
0%: {
opacity: 0;
}
20%: {
opacity: 1;
}
80%: {
opacity: 1;
}
100%: {
opacity: 0;
}
}
}

5. Map the rest of your tailwind config values to css

Here’s a complete list of the theme variables included by default https://tailwindcss.com/docs/theme#default-theme-variable-reference

For example if you had defined a custom class font-display in Tailwind 3 like so.

tailwind.config.js
const config = {
...
theme: {
extend: {
fontFamily: {
display: ["Inter",'ui-sans-serif','system-ui',],
},
}
}
}

It would be mapped to a CSS variable like this in Tailwind 4.

@theme {
..
--font-display: ["Inter",'ui-sans-serif','system-ui',]
}

6. Update the .container class

We prefer to use a centered container. In Tailwind 3 you could specify the centered option.

tailwind.config.mjs
const config = {
theme: {
container: {
center: true,
},
},
}

In Tailwind 4 you need to extend the default container class this using the @utility function.

@utility container {
margin-inline: auto;
padding-inline: 2rem;
}

7. Global extras (Missing borders and cursors)

Tailwind 4 made a few changes

  1. removed cursor on buttons
  2. removed border colors by default

Which means in Shadcn we need to reset these (at least for the time being)

@layer components {
button {
cursor: pointer;
}
[class*="border"] {
@apply border-border;
}
}

Complete CSS

Final Tailwind 4 CSS with shadcn/ui themeing example

@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
@plugin "@tailwindcss/typography";
@plugin "tailwindcss-animate";
:root {
--background: hsl(0 0% 100%);
--foreground: hsl(0 0% 3.9%);
--card: hsl(0 0% 100%);
--card-foreground: hsl(0 0% 3.9%);
--popover: hsl(0 0% 100%);
--popover-foreground: hsl(0 0% 3.9%);
--primary: hsl(0 0% 9%);
--primary-foreground: hsl(0 0% 98%);
--secondary: hsl(0 0% 96%);
--secondary-foreground: hsl(0 0% 9%);
--muted: hsl(0 0% 96%);
--muted-foreground: hsl(0 0% 45.1%);
--accent: hsl(0 0% 96%);
--accent-foreground: hsl(0 0% 9%);
--destructive: hsl(0 84.2% 60.2%);
--destructive-foreground: hsl(0 0% 98%);
--border: hsl(0, 0%, 90%);
--input: hsl(0 0% 89.8%);
--ring: hsl(0 0% 3.9%);
--radius: 8px;
--chart-1: hsl(12 76% 61%);
--chart-2: hsl(173 58% 39%);
--chart-3: hsl(197 37% 24%);
--chart-4: hsl(43 74% 66%);
--chart-5: hsl(27 87% 67%);
--sidebar: hsl(0 0% 98%);
--sidebar-foreground: hsl(240 5.3% 26.1%);
--sidebar-primary: hsl(240 5.9% 10%);
--sidebar-primary-foreground: hsl(0 0% 98%);
--sidebar-accent: hsl(240 4.8% 95.9%);
--sidebar-accent-foreground: hsl(240 5.9% 10%);
--sidebar-border: hsl(220 13% 91%);
--sidebar-ring: hsl(217.2 91.2% 59.8%);
/* Shadcnblocks.com */
--muted-2: hsl(0 0% 90%);
--muted-2-foreground: hsl(240 3.8% 46.1%);
}
.dark {
--background: hsl(240 10% 3.9%);
--foreground: hsl(0 0% 98%);
--card: hsl(240 10% 3.9%);
--card-foreground: hsl(0 0% 98%);
--popover: hsl(240 10% 3.9%);
--popover-foreground: hsl(0 0% 98%);
--primary: hsl(0 0% 98%);
--primary-foreground: hsl(240 5.9% 10%);
--secondary: hsl(240 3.7% 15.9%);
--secondary-foreground: hsl(0 0% 98%);
--muted: hsl(240 3.7% 15.9%);
--muted-foreground: hsl(240 5% 64.9%);
--accent: hsl(240 3.7% 15.9%);
--accent-foreground: hsl(0 0% 98%);
--destructive: hsl(0 62.8% 30.6%);
--destructive-foreground: hsl(0 0% 98%);
--border: hsl(240 3.7% 15.9%);
--input: hsl(240 3.7% 15.9%);
--ring: hsl(240 4.9% 83.9%);
--chart-1: hsl(220 70% 50%);
--chart-2: hsl(160 60% 45%);
--chart-3: hsl(30 80% 55%);
--chart-4: hsl(280 65% 60%);
--chart-5: hsl(340 75% 55%);
/* Shadcnblocks.com */
--muted-2: hsl(0, 0%, 18%);
--muted-2-foreground: hsl(240, 2%, 75%);
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
/* Shadcnblocks.com */
--color-muted-2: var(--muted-2);
--color-muted-2-foreground: var(--muted-2-foreground);
--color-transparent: transparent;
--radius-xs: calc(var(--radius) - 4px);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1400px; /* default is 1536px */
--animate-accordion-down: accordion-down 0.2s ease-out;
--animate-accordion-up: accordion-up 0.2s ease-out;
--animate-fade-in-out: fade-in-out 4s ease-in-out;
--animate-progress: progress 8s linear;
--animate-infinite-slider: infiniteSlider 20s linear infinite;
--animate-infinite-slider-reverse: infiniteSliderReverse 20s linear infinite;
--animate-shadow-ping: shadow-ping 1.5s ease-in-out infinite;
--animate-flip-btn: flip-btn 6s infinite steps(2, end);
--animate-rotate-btn: rotate-btn 3s linear infinite both;
--animate-light-to-right-top: light-to-right 4s linear infinite;
--animate-light-to-right-bottom: light-to-right 4s linear infinite;
--animate-marquee: marquee 25s linear infinite;
--animate-slide-to-right: slide-to-right 3s linear infinite;
--animate-slide-to-top: slide-to-top 3s linear infinite;
@keyframes accordion-down {
from {
height: 0;
}
to {
height: var(--radix-accordion-content-height);
}
}
@keyframes accordion-up {
from {
height: var(--radix-accordion-content-height);
}
to {
height: 0;
}
}
@keyframes fade-in-out {
0% {
opacity: 0;
}
20% {
opacity: 1;
}
80% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes progress {
from {
width: "0%";
}
to {
width: "100%";
}
}
@keyframes infiniteSlider {
0% {
transform: "translateX(0)";
}
100% {
transform: "translateX(calc(-250px * 5))";
}
}
@keyframes infiniteSliderReverse {
0% {
transform: "translateX(calc(-250px * 5))";
}
100% {
transform: "translateX(0)";
}
}
@keyframes fade-in-scale {
0% {
opacity: 0;
transform: scale(0.95);
}
100% {
opacity: 1;
transform: scale(1);
}
}
@keyframes transform1 {
0%,
25%,
100% {
width: 100%;
padding-bottom: 120%;
}
33.33%,
58.33% {
width: 0%;
padding-bottom: 0%;
}
66.66%,
91.66% {
width: 90%;
padding-bottom: 100%;
}
}
@keyframes transform2 {
0%,
25%,
100% {
width: 65%;
padding-bottom: 65%;
}
33.33%,
58.33% {
width: 95%;
padding-bottom: 114%;
}
66.66%,
91.66% {
width: 52%;
padding-bottom: 52%;
}
}
@keyframes transform3 {
0%,
25%,
100% {
width: 78%;
padding-bottom: 100%;
}
33.33%,
58.33% {
width: 78%;
padding-bottom: 94%;
}
66.66%,
91.66% {
width: 95%;
padding-bottom: 76%;
}
}
@keyframes transform4 {
0%,
25%,
66.66%,
91.66%,
100% {
width: 0%;
padding-bottom: 0%;
}
33.33%,
58.33% {
width: 65%;
padding-bottom: 46%;
}
}
@keyframes image1 {
0%,
25%,
100% {
opacity: 1;
}
33.33%,
58.33%,
66.66%,
91.66% {
opacity: 0;
}
}
@keyframes image2 {
0%,
25%,
33.33%,
58.33%,
100% {
opacity: 0;
}
66.66%,
91.66% {
opacity: 1;
}
}
@keyframes image3 {
0%,
25%,
66.66%,
91.66%,
100% {
opacity: 0;
}
33.33%,
58.33% {
opacity: 1;
}
}
@keyframes gradient-spin {
0% {
transform: translateX(-50%) translateY(-50%) rotate(0deg);
}
100% {
transform: translateX(-50%) translateY(-50%) rotate(360deg);
}
}
@keyframes shadow-ping {
0% {
boxshadow: 0 0 0 0px theme("colors.neutral.100");
}
50% {
boxshadow: 0 0 0 12px theme("colors.neutral.300");
}
100% {
boxshadow: 0 0 0 12px transparent;
}
}
@keyframes show-text {
0%,
14.28% {
opacity: 0;
}
17%,
26% {
opacity: 1;
}
28.58%,
100% {
opacity: 0;
}
}
@keyframes flip-btn {
to {
transform: rotate(360deg);
}
}
@keyframes rotate-btn {
to {
transform: rotate(90deg);
}
}
@keyframes slide-to-right {
0% {
opacity: 0;
left: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
left: 80%;
}
}
@keyframes slide-to-top {
0% {
opacity: 0;
bottom: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
bottom: 80%;
}
}
@keyframes light-to-right {
0% {
transform: translate(0%);
opacity: 0;
}
50% {
opacity: 1;
}
100% {
transform: translate(100%);
opacity: 0;
}
}
@keyframes marquee {
0% {
transform: translateX(0%);
}
100% {
transform: translateX(-100%);
}
}
}
@utility container {
margin-inline: auto;
padding-inline: 2rem;
}
@layer components {
button {
cursor: pointer;
}
[class*="border"] {
@apply border-border;
}
p {
@apply text-muted-foreground;
}
}