Menu

Tailwind CSS Box Shadow

Box shadows are a fundamental styling property in CSS, allowing you to add depth and visual appeal to your elements by creating an illusion of elevation. In traditional CSS, the box-shadow property defines one or more shadows to an element's box. With Tailwind CSS, you get a predefined set of utilities to handle box shadows effortlessly, ranging from subtle shadows to deep, dramatic effects. Tailwind provides intuitive class names to customize shadows quickly, while still offering full flexibility for specific customizations.

In this article, you’ll learn how to use Tailwind’s box shadow utilities effectively, how to conditionally apply shadows with states and responsive modifiers, and how to extend your theme or use arbitrary values to implement custom shadows.

ClassPropertiesExample
shadow-smbox-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);<div className="shadow-sm"></div>
shadowbox-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);<div className="shadow"></div>
shadow-mdbox-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);<div className="shadow-md"></div>
shadow-lgbox-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);<div className="shadow-lg"></div>
shadow-xlbox-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);<div className="shadow-xl"></div>
shadow-2xlbox-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);<div className="shadow-2xl"></div>
shadow-innerbox-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.05);<div className="shadow-inner"></div>
shadow-nonebox-shadow: 0 0 #0000;<div className="shadow-none"></div>

Overview of Box Shadow

The box shadow utilities in Tailwind CSS make it easy to add, modify, and even remove shadows on elements. Let’s explore some common ways you can use these utilities.

Adding the Box Shadow

A basic outer shadow can be added by using Tailwind's predefined shadow utilities. These shadows give your elements an aesthetic lift and can range from small, subtle effects to large, prominent ones.

This is a live editor. Play around with it!
export default function Card() {
  return (
    <div className="flex items-center justify-center h-screen w-screen bg-gray-50">
      <div className="shadow-lg p-6 rounded-md bg-white">
        <img 
          className="rounded-t-md" 
          src="https://images.unsplash.com/photo-1508873699372-7aeab60b44ab" 
          alt="Sample" 
        />
        <h2 className="mt-4 text-xl font-semibold">Title with Shadow</h2>
        <p className="mt-2 text-gray-600">
          This element has an **outer shadow** using Tailwind's shadow utilities.
        </p>
      </div>
    </div>
  );
}

Above configuration corresponds to:

  • shadow-lg: Applies a large shadow with respectively larger offsets and blurs.

Creating an Inner Shadow Effect

Inner shadows can make elements appear recessed or carved-in. In Tailwind CSS, this effect is achieved using the shadow-inner utility.

This is a live editor. Play around with it!
export default function InnerShadowCard() {
  return (
    <div className="flex items-center justify-center h-screen w-screen bg-gray-50">
      <div className="shadow-inner shadow-red-200 bg-gray-200 h-60 w-60 flex items-center justify-center rounded-lg">
        <p className="text-gray-800 font-medium">
          Inner Shadow Applied
        </p>
      </div>
    </div>
  );
}

How this works:

  • shadow-inner: Places the shadow inside the element, creating a recessed effect.

Disabling Shadows Entirely

If you need an element without any shadow, Tailwind offers shadow-none to remove all shadow styles.

This is a live editor. Play around with it!
export default function PlainCard() {
  return (
    <div className="flex items-center justify-center h-screen w-screen bg-gray-50">
      <div className="shadow-none bg-gray-100 h-40 w-40 p-4 rounded-lg">
        <p className="text-gray-600 text-center">
          No Shadow Here
        </p>
      </div>
    </div>
  );
}

States and Responsiveness

Tailwind allows you to conditionally apply box shadows based on interactive states like hover and focus, as well as responsive breakpoints for dynamic designs.

Hover and Focus States

Using state modifiers such as hover: or focus:, you can create interactive effects when a user interacts with your elements.

This is a live editor. Play around with it!
export default function HoverShadowCard() {
  return (
    <div className="flex items-center justify-center h-screen w-screen bg-gray-50">
      <div className="bg-white p-6 rounded-lg shadow-md hover:shadow-2xl transition-shadow duration-300">
        <h2 className="text-gray-800 font-bold">Hover Over Me</h2>
        <p className="text-gray-500 mt-2">
          The shadow becomes **bigger** when hovered!
        </p>
      </div>
    </div>
  );
}

State configuration:

  • hover:shadow-2xl: Intensifies the shadow upon hovering.

Breakpoint Modifiers

Tailwind also enables conditional shadowing via breakpoint-specific modifiers, letting you define shadows for different screen sizes.

This is a live editor. Play around with it!
export default function ResponsiveShadowCard() {
  return (
    <div className="flex items-center justify-center h-screen w-screen bg-gray-50">
      <div className="bg-white p-6 rounded-lg shadow-sm md:shadow lg:shadow-2xl">
        <h2 className="text-gray-900 font-bold">Responsive Shadows</h2>
        <p className="text-gray-600 mt-2">
          Shadows vary based on screen size!
        </p>
      </div>
    </div>
  );
}

Breakpoint behaviour:

  • shadow-sm: Default shadow for small screens.
  • md:shadow, lg:shadow-2xl: Gradually increases shadow strength on medium and large screens.

Custom Box Shadow

Aside from its predefined utilities, Tailwind CSS provides tools for customization when default shadows don’t meet your specific design requirements. You can accomplish this by extending your theme or using arbitrary values.

Extending the Theme

You can define custom shadow values in your tailwind.config.js to create reusable shadow classes tailored to your preferences. This customization keeps your code DRY while meeting your unique design needs.

Then use it as follows:

This is a live editor. Play around with it!
import tailwindConfig from "./tailwind.config.js";
tailwind.config = tailwindConfig;

export default function CustomShadowCard() {
  return (
    <div className="flex items-center justify-center h-screen w-screen bg-gray-50">
      <div className="shadow-custom-dark bg-gray-800 h-40 w-40 rounded-lg flex justify-center items-center">
        <p className="text-white text-center font-medium">
          Custom Shadow
        </p>
      </div>
    </div>
  );
}

By defining shadow-custom-dark in your theme, you standardize a reusable custom shadow.

Using Arbitrary Values

For quick, one-off shadow requirements, Tailwind supports arbitrary values directly in your HTML or JSX using square brackets.

This is a live editor. Play around with it!
export default function ArbitraryShadowCard() {
  return (
    <div className="flex items-center justify-center h-screen w-screen bg-gray-50">
      <div className="bg-white h-40 w-40 shadow-[10px_10px_15px_rgba(0,0,0,0.4)] rounded-lg flex items-center justify-center px-4 text-center">
        <p>
          Arbitrary Value Shadow
        </p>
      </div>
    </div>
  );
}

How the arbitrary utility works:

  • shadow-[10px_10px_15px_rgba(0,0,0,0.4)]: Directly applies a shadow offset, blur, and color in one line.

Real World Examples

Product Cards with Hover Effect

This example shows a grid of product cards with a subtle shadow that intensifies on hover, creating a floating effect.

This is a live editor. Play around with it!
export default function ProductGrid() {
  const products = [
    {
      id: 1,
      name: "Leather Backpack",
      price: "$129.99",
      src: "https://images.unsplash.com/photo-1548036328-c9fa89d128fa",
      alt: "Brown leather backpack"
    },
    {
      id: 2,
      name: "Wireless Headphones",
      price: "$199.99",
      src: "https://images.unsplash.com/photo-1505740420928-5e560c06d30e",
      alt: "Black wireless headphones"
    },
    {
      id: 3,
      name: "Smart Watch",
      price: "$299.99",
      src: "https://images.unsplash.com/photo-1523275335684-37898b6baf30",
      alt: "Modern smartwatch"
    },
    {
      id: 4,
      name: "Digital Camera",
      price: "$799.99",
      src: "https://images.unsplash.com/photo-1516035069371-29a1b244cc32",
      alt: "Professional digital camera"
    },
    {
      id: 5,
      name: "Laptop Stand",
      price: "$49.99",
      src: "https://images.unsplash.com/photo-1527864550417-7fd91fc51a46",
      alt: "Aluminum laptop stand"
    },
    {
      id: 6,
      name: "Mechanical Keyboard",
      price: "$159.99",
      src: "https://images.unsplash.com/photo-1511467687858-23d96c32e4ae",
      alt: "RGB mechanical keyboard"
    }
  ];

  return (
    <div className="grid grid-cols-1 md:grid-cols-3 gap-6 p-6">
      {products.map((product) => (
        <div
          key={product.id}
          className="bg-white rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 ease-in-out"
        >
          <img
            src={product.src}
            alt={product.alt}
            className="w-full h-48 object-cover rounded-t-lg"
          />
          <div className="p-4">
            <h3 className="text-lg font-semibold">{product.name}</h3>
            <p className="text-gray-600">{product.price}</p>
          </div>
        </div>
      ))}
    </div>
  );
}

Floating Action Menu

This example demonstrates a floating action button that expands into a menu with multiple options.

This is a live editor. Play around with it!
export default function FloatingActionMenu() {
  const menuItems = [
    {
      id: 1,
      label: "Add Post",
      icon: "https://images.unsplash.com/photo-1618477247222-acbdb0e159b3",
      alt: "Add post icon"
    },
    {
      id: 2,
      label: "Upload Photo",
      icon: "https://images.unsplash.com/photo-1618477247222-acbdb0e159b3",
      alt: "Upload photo icon"
    },
    {
      id: 3,
      label: "Share Location",
      icon: "https://images.unsplash.com/photo-1618477247222-acbdb0e159b3",
      alt: "Location icon"
    },
    {
      id: 4,
      label: "Start Live",
      icon: "https://images.unsplash.com/photo-1618477247222-acbdb0e159b3",
      alt: "Live streaming icon"
    },
    {
      id: 5,
      label: "Create Poll",
      icon: "https://images.unsplash.com/photo-1618477247222-acbdb0e159b3",
      alt: "Poll icon"
    },
    {
      id: 6,
      label: "Settings",
      icon: "https://images.unsplash.com/photo-1618477247222-acbdb0e159b3",
      alt: "Settings icon"
    }
  ];

  return (
    <div className="fixed bottom-6 right-6">
      <div className="group">
        <button className="w-14 h-14 bg-blue-500 rounded-full shadow-lg hover:shadow-xl transition-shadow duration-300">
          <span className="text-white text-2xl">+</span>
        </button>
        <div className="absolute bottom-16 right-0 hidden group-hover:block">
          {menuItems.map((item) => (
            <button
              key={item.id}
              className="block w-48 mb-2 p-3 bg-white rounded-lg shadow-lg hover:shadow-xl transition-shadow duration-300"
            >
              <span>{item.label}</span>
            </button>
          ))}
        </div>
      </div>
    </div>
  );
}

Stacked Notification Cards

This example shows a stack of notification cards with layered shadows creating depth.

This is a live editor. Play around with it!
export default function NotificationStack() {
  const notifications = [
    {
      id: 1,
      title: "New Message",
      message: "John Doe sent you a message",
      time: "2 minutes ago",
      type: "message"
    },
    {
      id: 2,
      title: "Payment Received",
      message: "Payment of $500 received",
      time: "15 minutes ago",
      type: "payment"
    },
    {
      id: 3,
      title: "New Order",
      message: "Order #12345 has been placed",
      time: "1 hour ago",
      type: "order"
    },
    {
      id: 4,
      title: "System Update",
      message: "System maintenance scheduled",
      time: "2 hours ago",
      type: "system"
    },
    {
      id: 5,
      title: "New Follow",
      message: "Jane Smith started following you",
      time: "3 hours ago",
      type: "social"
    },
    {
      id: 6,
      title: "Storage Alert",
      message: "Storage space running low",
      time: "4 hours ago",
      type: "alert"
    }
  ];

  return (
    <div className="p-6">
      {notifications.map((notification, index) => (
        <div
          key={notification.id}
          className={`bg-white rounded-lg p-4 mb-3 transform transition-all duration-300 hover:-translate-y-1 ${
            index === 0 ? 'shadow-2xl' : 
            index === 1 ? 'shadow-xl' : 
            'shadow-md'
          }`}
        >
          <h4 className="font-semibold">{notification.title}</h4>
          <p className="text-gray-600">{notification.message}</p>
          <span className="text-sm text-gray-400">{notification.time}</span>
        </div>
      ))}
    </div>
  );
}

Pricing Table with Emphasized Tier

This example displays a pricing table where the recommended tier has a pronounced shadow.

This is a live editor. Play around with it!
export default function PricingTable() {
  const plans = [
    {
      id: 1,
      name: "Basic",
      price: "$9.99",
      features: ["1 User", "5GB Storage", "Basic Support", "Email Access", "Community Forums", "Basic Analytics"],
      recommended: false
    },
    {
      id: 2,
      name: "Pro",
      price: "$29.99",
      features: ["5 Users", "50GB Storage", "Priority Support", "Email & Phone Access", "Advanced Analytics", "API Access"],
      recommended: true
    },
    {
      id: 3,
      name: "Enterprise",
      price: "$99.99",
      features: ["Unlimited Users", "500GB Storage", "24/7 Support", "Dedicated Manager", "Custom Analytics", "White Labeling"],
      recommended: false
    }
  ];

  return (
    <div className="flex flex-col md:flex-row gap-6 p-6">
      {plans.map((plan) => (
        <div
          key={plan.id}
          className={`flex-1 bg-white rounded-lg p-6 ${
            plan.recommended
              ? 'shadow-2xl transform -translate-y-4'
              : 'shadow-md hover:shadow-lg transition-shadow duration-300'
          }`}
        >
          <h3 className="text-xl font-bold">{plan.name}</h3>
          <p className="text-3xl font-bold my-4">{plan.price}</p>
          <ul className="space-y-2">
            {plan.features.map((feature, index) => (
              <li key={index} className="flex items-center">
                <span className="mr-2">✓</span>
                {feature}
              </li>
            ))}
          </ul>
        </div>
      ))}
    </div>
  );
}

This example shows a modal dialog with a backdrop shadow and elevated content.

This is a live editor. Play around with it!
export default function ModalDialog() {
  const modalContent = {
    title: "Confirm Action",
    message: "Are you sure you want to proceed with this action?",
    options: [
      {
        id: 1,
        label: "Cancel",
        type: "secondary"
      },
      {
        id: 2,
        label: "Confirm",
        type: "primary"
      }
    ],
    additionalInfo: [
      "This action cannot be undone",
      "All related data will be affected",
      "System notifications will be sent",
      "Audit logs will be updated",
      "Backup will be created automatically",
      "Process may take a few minutes"
    ]
  };

  return (
    <div className="fixed inset-0 flex items-center justify-center">
      <div className="fixed inset-0 bg-black bg-opacity-50"></div>
      <div className="relative bg-white rounded-lg shadow-2xl w-full max-w-md m-4 p-6">
        <h2 className="text-xl font-bold mb-4">{modalContent.title}</h2>
        <p className="text-gray-600 mb-4">{modalContent.message}</p>
        <ul className="mb-6">
          {modalContent.additionalInfo.map((info, index) => (
            <li key={index} className="text-sm text-gray-500 mb-1">
              • {info}
            </li>
          ))}
        </ul>
        <div className="flex justify-end space-x-4">
          {modalContent.options.map((option) => (
            <button
              key={option.id}
              className={`px-4 py-2 rounded ${
                option.type === 'primary'
                  ? 'bg-blue-500 text-white shadow-md hover:shadow-lg'
                  : 'bg-gray-200 text-gray-800'
              } transition-shadow duration-300`}
            >
              {option.label}
            </button>
          ))}
        </div>
      </div>
    </div>
  );
}

Customization Examples

Here are three distinct examples of customizing box shadows in Tailwind CSS through theme configuration. Each example demonstrates different approaches to implementing custom shadows for specific UI components.

Gradient-Enhanced Product Card Shadow

This example shows how to create a product card with a sophisticated shadow that includes subtle gradient effects.

This is a live editor. Play around with it!
import tailwindConfig from "./tailwind.config.js";
tailwind.config = tailwindConfig;

export default function ProductCard() {
  return (
    <div className="flex justify-center items-center min-h-screen bg-gray-100">
      <div className="w-80 bg-white rounded-xl shadow-product transform hover:scale-105 transition-all duration-300">
        <img 
          src="https://images.unsplash.com/photo-1523275335684-37898b6baf30"
          alt="Premium Watch"
          className="w-full h-48 object-cover rounded-t-xl"
        />
        <div className="p-6">
          <h2 className="text-2xl font-bold text-gray-800">Premium Watch</h2>
          <p className="mt-2 text-gray-600">Limited Edition Timepiece</p>
          <div className="mt-4 flex justify-between items-center">
            <span className="text-xl font-bold text-blue-600">$299.99</span>
            <button className="px-4 py-2 bg-blue-500 text-white rounded-lg">
              Add to Cart
            </button>
          </div>
        </div>
      </div>
    </div>
  )
}

Floating Navigation Menu Shadow

This example demonstrates a floating navigation menu with a sophisticated multi-layered shadow effect.

This is a live editor. Play around with it!
import tailwindConfig from "./tailwind.config.js";
tailwind.config = tailwindConfig;

export default function FloatingNavigation() {
  return (
    <nav className="fixed bottom-8 left-1/2 transform -translate-x-1/2 bg-white shadow-float-nav rounded-full px-8 py-4">
      <ul className="flex space-x-12">
        {['Home', 'Search', 'Profile', 'Settings'].map((item) => (
          <li key={item} className="flex flex-col items-center">
            <img 
              src={`https://images.unsplash.com/photo-1512941937669-90a1b58e7e9c?w=20`}
              alt={item}
              className="w-6 h-6 mb-1"
            />
            <span className="text-sm text-gray-600">{item}</span>
          </li>
        ))}
      </ul>
    </nav>
  )
}

Notification Toast Shadow

This example showcases a notification toast with a directional shadow effect.

This is a live editor. Play around with it!
import tailwindConfig from "./tailwind.config.js";
tailwind.config = tailwindConfig;

export default function NotificationToast() {
  return (
    <div className="fixed top-8 right-8 max-w-md">
      <div className="bg-white shadow-toast rounded-lg p-6 transform transition-transform hover:-translate-y-1">
        <div className="flex items-start">
          <div className="flex-shrink-0">
            <img 
              src="https://images.unsplash.com/photo-1560250097-0b93528c311a?w=50"
              alt="Profile"
              className="w-10 h-10 rounded-full"
            />
          </div>
          <div className="ml-4">
            <h3 className="text-lg font-semibold text-gray-900">New Message</h3>
            <p className="mt-1 text-gray-600">
              You have received a new message from John Doe.
            </p>
            <div className="mt-4 flex space-x-3">
              <button className="px-4 py-2 bg-blue-500 text-white rounded-md">
                View
              </button>
              <button className="px-4 py-2 bg-gray-200 text-gray-700 rounded-md">
                Dismiss
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}

These examples demonstrate how you can create custom box shadows for different UI components using Tailwind CSS's theme customization. Each shadow is carefully crafted to enhance the visual hierarchy and interaction states of the components while maintaining a consistent design language.

Best Practices

Maintain Design Consistency

When implementing Box Shadow in your project with Tailwind CSS, ensure all shadow effects align with your design system. Consistency facilitates a cohesive user experience and reinforces your brand identity. Opt for utilizing pre-configured shadow classes like shadow-md or shadow-lg across similar elements, such as cards, buttons, and modals. This avoids a mismatched visual hierarchy and simplifies team collaboration.

If unique shadows are needed, extend them through tailwind.config.js. For instance, define shadows specific to your theme or brand aesthetic:

Use these utilities sparingly throughout your project so that design remains harmonious. Avoid combining stylistically conflicting shadow effects that can confuse or distract your audience.

Leverage Utility Combinations

Design complex visual effects by mixing Tailwind's other utilities with Box Shadow. Combine utility classes such as rounded-xl, bg-gradient-to-r, or border alongside shadows to create polished, professional designs. For example, a notification card may pair a shadow with a side border:

This is a live editor. Play around with it!
import tailwindConfig from "./tailwind.config.js";
tailwind.config = tailwindConfig;

export default function NotificationCard() {
  return (
    <div className="max-w-md mx-auto p-4 bg-white rounded-lg shadow-brand-subtle border-l-4 border-blue-500">
      <h3 className="text-lg font-semibold">Update Available</h3>
      <p className="mt-2 text-gray-600">
        Your system has a new software update. Keep everything up-to-date for optimal performance.
      </p>
    </div>
  );
}

Thoughtful utility combinations allow you to achieve intricate styles without custom CSS, ensuring maintainable and scalable code. Keep semantic classes like hover:, sm:, or focus: as part of your combinations to enhance both interactivity and responsiveness.

Accessibility Considerations

Enhance Readability and Navigability

Using Box Shadow strategically can improve readability and navigability for all users, including individuals with visual impairments. Shadows create separation between elements, reducing cognitive load when scanning content. For example, in a card-based layout, distributing subtle shadows (shadow-sm) around cards helps users distinguish interactive regions.

Contrast can also be enhanced with shadows to prioritize accessibility on low-contrast backgrounds. For instance, pairing a larger shadow (shadow-lg) with light background colors boosts clarity.

This is a live editor. Play around with it!
export default function AccessibleCard() {
  return (
    <div className="max-w-sm p-4 mx-auto bg-white rounded-xl shadow-lg">
      <img
        className="h-48 w-full object-cover rounded-lg"
        src="https://images.unsplash.com/photo-1517059224940-d4af9eec41b7"
        alt="Accessible Content"
      />
      <h3 className="mt-3 text-gray-900 font-bold">Enhanced Readability</h3>
      <p className="text-gray-700">
        Consistent use of shadows reduces visual clutter while enhancing key content.
      </p>
    </div>
  );
}

By choosing softer shadows, you guide user attention without overwhelming the interface.

Focus on High Contrast

Proper implementation of shadows can improve contrast ratios, ensuring sufficient differentiation between interactive and non-interactive elements. Particularly for buttons or input elements, shadows such as shadow-md clarify hover states or on-click feedback.

For visually impaired users, hover effects in conjunction with shadows create dynamic depth and make the active component noticeable:

This is a live editor. Play around with it!
export default function ContrastButton() {
  return (
    <div className="flex justify-center items-center h-screen w-screen">
      <button className="bg-blue-500 hover:shadow-xl shadow-md text-white font-semibold py-2 px-6 rounded-lg">
        Confirm Action
      </button>
    </div>
  );
}

High-contrast edges are particularly significant for reducing confusion on interactive controls, ensuring they remain accessible to all users regardless of vision conditions.