Lexington has been awarded a grant from Astro, to celebrate. Get a 30% discount. Apply code LEXINGTON30 ( uppercase ) at checkout.

← Back to all tutorials

How to create animated image cards with Tailwind CSS and Astrojs

A collage of four floating images against a white background. From left to right: the first image shows a vintage van with surfboards stacked on top, parked by a beach. The second image depicts a person's feet with a retro boombox radio on the ground. The third image shows a fence with multiple colorful, used surfboards leaning against it. The fourth image features red cable cars hanging on a line with a clear sky in the background.
Published and written on Feb 20 2024 by Michael Andreuzza

Let’s elevate your website with a captivating image component. If your Astro.js project is already set up, we’ll dive straight into using Tailwind CSS to craft the animations for our cards.

The main classes

All our images will use this classes in common:

  • rounded-xl to apply rounded corners.
  • hover:rotate-0 to ensure the image resets to a default rotation on hover.
  • duration-500 to control the speed of the animation over 500 milliseconds.
  • hover:-translate-y-12 to lift the image upwards on hover.
  • h-full and w-full to ensure the image occupies the full height & width of its container.
  • object-cover to ensure the image covers the area of its container without distortion.
  • hover:scale-150 to magnify the image on hover.
  • transform to enable CSS transformations.
  • origin-bottom to set the transformation origin to the bottom of the image.

To customize and enhance the animation effect, incorporate these additional classes:

  • rotate-6 for a slight right tilt.
  • -rotate-12 for a more pronounced left tilt.

By combining these classes, you can create a dynamic, interactive image component that adds a layer of sophistication and engagement to your website. Tailwind CSS makes these animations straightforward to implement, offering a seamless way to enhance your site’s visual appeal.

Let’s get to the code

This is how the whole component will look like

<div class="hidden lg:grid mt-12 lg:grid-cols-4 mx-auto scale-125">
   <a href="...">
   <img
      src="..."
      class="rounded-xl w-full rotate-6 hover:rotate-0 duration-500 hover:-translate-y-12 h-full object-cover hover:scale-150 transform origin-bottom"
      alt="..."
      />
   </a
      ><a href="...">
   <img
      src="..."
      class="rounded-xl w-full -rotate-12 hover:rotate-0 duration-500 hover:-translate-y-12 h-full  object-cover hover:scale-150 transform origin-bottom"
      alt="..."
      />
   </a
      ><a href="...">
   <img
      src="..."
      class="rounded-xl w-full rotate-6 hover:rotate-0 duration-500 hover:-translate-y-12 h-full object-cover hover:scale-150 transform origin-bottom"
      alt="..."
      />
   </a
      ><a href="...">
   <img
      src="..."
      class="rounded-xl w-full -rotate-12 hover:rotate-0 duration-500 hover:-translate-y-12 h-full  object-cover hover:scale-150 transform origin-bottom"
      alt="..."
      />
   </a>
</div>

Simplifying the code

To avoid repetitive markup and class definitions, let’s streamline our process by using an array. This allows us to dynamically add classes, making the component more flexible and easier to manage.

// Define an array of image objects to make the component more dynamic and easily editable.
const images = [
   {
    url: "...", // Placeholder for the image URL
    rotation: "rotate-6", // Class for a slight right tilt
  },
  {
    url: "...", // Placeholder for the image URL
    rotation: "-rotate-12", // Class for a pronounced left tilt
  },
  {
    url: "...", // Placeholder for the image URL
    rotation: "rotate-6", // Repeat class for consistency or thematic design
  },
  {
    url: "...", // Placeholder for the image URL
    rotation: "-rotate-12", // Repeat class for consistency or thematic design
  },
];

Now that we’ve established our array, let’s dynamically render each image within our component using the map method. This approach injects each image’s unique properties directly into the markup, streamlining the creation of our interactive image gallery.

<div class="hidden lg:grid mt-12 lg:grid-cols-4 mx-auto scale-125">
  {
    images.map((image) => (
      <a href="#_">
        <img
          src={image.url}
          class={`rounded-xl  ${image.rotation} hover:rotate-0 duration-500 hover:-translate-y-12 h-full w-full object-cover hover:scale-150 transform origin-bottom`}
          alt="#_"
        />
      </a>
    ))
  }
</div>

The Template Literal

The expression uses a template literal, which is denoted by backticks (`). Template literals allow you to embed expressions inside strings, making it easier to construct strings dynamically. The ${} syntax within the template literal is used to interpolate JavaScript expressions into the string.

Dynamic Class Assignment

In your code, image.rotation is the JavaScript expression being dynamically inserted into the class list of the <img> element. Depending on the value of image.rotation for each object in the images array, a different rotation class (e.g., rotate-6 or -rotate-12) is applied to each image. This allows each image to have its unique rotation effect as specified in your data array.

Full Class String Explanation

  • rounded-xl: Applies rounded corners to the image.
  • ${image.rotation}: Dynamically adds a rotation class based on the image object’s property. This is where your dynamic class assignment occurs.
  • hover:rotate-0: Resets the image rotation to 0 degrees on hover, effectively canceling out any rotation applied by ${image.rotation} when the user hovers over the image.
  • duration-500: Sets the transition duration to 500 milliseconds, making any hover effects (like rotation or scale changes) transition smoothly.
  • hover:-translate-y-12: Moves the image up (translates it along the Y-axis in a negative direction) on hover.
  • h-full w-full: Sets the height and width of the image to fully occupy its container’s dimensions.
  • object-cover: Ensures the image covers the area of the container without distorting its aspect ratio, cropping the image if necessary.
  • hover:scale-150: Enlarges the image to 150% of its original size on hover.
  • transform: Enables CSS transforms on the element, a prerequisite for applying transformation effects like rotation and scale.
  • origin-bottom: Sets the origin point for transformations to the bottom of the element, affecting how it rotates or scales.

This dynamic class string effectively combines static classes that apply to all images with a dynamic class that varies per image, enabling a rich, interactive styling approach that can be easily managed and extended.

/Michael Andreuzza

Did you like this tutorial? Please share it with your friends!

Get lifetime access to every theme available today for $199 and own them forever. Plus, new themes, lifetime updates, use on unlimited projects and enjoy lifetime support.

— No subscription required!