DEV Community

Cover image for Building a Card with Gradient Hover Effect Inspired by Nuxt UI
Tanay Karnik
Tanay Karnik

Posted on • Originally published at tanay.xyz

Building a Card with Gradient Hover Effect Inspired by Nuxt UI

I came across an amazing card hover effect on the Nuxt UI website.

Unfortunately, it’s a pro component—so I decided to build it myself!

I tried getting some inspiration by inspecting their code, but it was more complex than I expected.
I'm no CSS wizard, so I came up with a much simpler version of this effect.

Here’s a tutorial on how to make a card with a cool gradient hover effect using TailwindCSS.


1. Set up a simple card component

<template>
  <div :class="['bg-neutral-950', props.class || '']"></div>
</template>

<script setup>
const props = defineProps(["class"]);
</script>
Enter fullscreen mode Exit fullscreen mode

Let’s start by building a basic card component with a black background, arranged in a grid.

This setup will be our testing ground.

I'm using Vue here, but this can be easily replicated with React, Vanilla JS, or any other framework.

Step 1 Demo


2. Add a circular gradient that follows the mouse

<template>
  <div
    ref="target"
    :style="cssVars"
    :class="['shine bg-neutral-950', props.class || '']"
  ></div>
</template>

<script setup>
import { ref, computed } from "vue";
import { useMouseInElement } from "@vueuse/core";
const props = defineProps(["class"]);
const target = ref(null);
const { elementX, elementY } = useMouseInElement(target);
const cssVars = computed(() => ({
  "--x": `${target.value ? elementX.value : -1000}px`,
  "--y": `${target.value ? elementY.value : -1000}px`,
}));
</script>

<style scoped>
.shine {
  background-image: radial-gradient(
    300px circle at var(--x) var(--y),
    #6366f1 0,
    transparent 100%
  );
}
</style>
Enter fullscreen mode Exit fullscreen mode

The next step is adding a circular gradient that follows the mouse.

  1. First, add a shine class to the card div and set it up with a radial-gradient.
  2. This gradient is centered at CSS variables --x and --y, going from bright (any color) at the center to fully transparent, with a 300px radius (adjustable).

To make the gradient follow the mouse, I’m using a useMouseElement composable. Here, elementX and elementY are the mouse coordinates relative to the position of the target element.

Depending on your framework, you could use a different hook or add a mousemove event listener to update these CSS variables.

Step 2 Demo


3. Stacking divs

<template>
  <div
    ref="target"
    :style="cssVars"
    :class="['p-[2px] shine bg-neutral-950', props.class || '']"
  >
    <div class="w-full h-full bg-neutral-950/80"></div>
  </div>
</template>

<script setup>
import { ref, computed } from "vue";
import { useMouseInElement } from "@vueuse/core";
const props = defineProps(["class"]);
const target = ref(null);
const { elementX, elementY } = useMouseInElement(target);
const cssVars = computed(() => ({
  "--x": `${target.value ? elementX.value : -1000}px`,
  "--y": `${target.value ? elementY.value : -1000}px`,
}));
</script>

<style scoped>
.shine {
  background-image: radial-gradient(
    300px circle at var(--x) var(--y),
    #6366f1 0,
    transparent 100%
  );
}
</style>
Enter fullscreen mode Exit fullscreen mode

I know what you’re thinking—it doesn’t look like the final card effect at all.

The trick to achieving the right look is to stack another div on top with a small amount of padding (I used 2px).

There are other ways to achieve this effect, like using ::before pseudo-elements or Tailwind rings, but I found this to be the simplest and cleanest approach.

Step 3 Demo


4. Finishing touches

<template>
  <div
    ref="target"
    :style="cssVars"
    :class="['rounded-[15px] p-[2px] shine', props.class || '']"
  >
    <div
      class="rounded-[13px] w-full h-full bg-gradient-to-b from-neutral-800/50 to-neutral-950/50 bg-neutral-950/80"
    ></div>
  </div>
</template>

<script setup>
import { ref, computed } from "vue";
import { useMouseInElement } from "@vueuse/core";
const props = defineProps(["class"]);
const target = ref(null);
const { elementX, elementY } = useMouseInElement(target);
const cssVars = computed(() => ({
  "--x": `${target.value ? elementX.value : -1000}px`,
  "--y": `${target.value ? elementY.value : -1000}px`,
}));
</script>

<style scoped>
.shine {
  background-image: radial-gradient(
    300px circle at var(--x) var(--y),
    #6366f1 0,
    transparent 100%
  );
}
</style>
Enter fullscreen mode Exit fullscreen mode

Finally, give the outer div a subtle background gradient and rounded borders.

To keep the padding and border-radius looking clean, I added 2px to the border radius of the outer div (general tip: adjust by padding * 1.141 for a proportional look).

Step 4 Demo


Here's the GitHub repo to see the code in action:

Shiny Cards

Nuxt shiny card hover effect.




Top comments (0)