DEV Community

Cover image for How to create animated image cards with Tailwind CSS and Astrojs
Michael Andreuzza
Michael Andreuzza

Posted on

How to create animated image cards with Tailwind CSS and Astrojs

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>
Enter fullscreen mode Exit fullscreen mode

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
  },
];
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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.

See it live and get the code

Top comments (4)

Collapse
 
subaash_b profile image
Subaash

This urges me to learn Tailwind.

Collapse
 
mike_andreuzza profile image
Michael Andreuzza

haha no need man. You can achieve this with plain CSS :-)

Collapse
 
subaash_b profile image
Subaash

This looks minimal though. This may eliminate the new module files for every component created in libraries like React. Don't know about the downsides about Tailwind, but your post has impressed me.

Thread Thread
 
mike_andreuzza profile image
Michael Andreuzza • Edited

Thank you for that!

The goal was indeed to simplify the process of creating dynamic components...

Regarding Tailwind CSS, it’s known for its utility-first approach which allows for rapid UI development. Some developers find it incredibly productive as it avoids the need to leave HTML to write custom CSS....which you probably know.... However, it can lead to long class names in your markup and can have a learning curve for those who are accustomed to traditional CSS or preprocessor-based workflows.

One potential downside is that if not managed well, Tailwind can lead to larger CSS file sizes due to the extensive list of utility classes. But with proper customisation, purging, and optimisation, Tailwind's impact on performance can be significantly mitigated

Said this.....I use Tailwind daily