skip to Main Content

TL; DR: Trying to make a dummy button with hover effect in a clickable container.

A preface: I’m looking for HTML/CSS solutions ONLY (I already know how to do this with JS).

I am working on a simple card design made of a container and a button link. I want the whole container to act as a link, i.e., the user can click anywhere inside the container to get to their destination. My goal is to do this without using multiple a elements.

The requirements for the behavior of this card are:

  1. The user hovers over the button, and they see the button’s hover effect.
  2. The user hovers over the container, and they see NO hover effect on the button.
  3. The user clicks anywhere within the container, including on the button, and it takes them to the linked page.

— What I’ve tried —-

To avoid multiple a elements or wrapping the entire container in an a element, I went with the following approach:

  • I absolutely positioned the real a element to accept clicks from anywhere inside the container.

  • I created a dummy button div instead to replace the real a element, and brought it to the front so it can be hovered (using z-index).

.container {
  position: relative;
  /* This is the important one, the rest is styling */
  width: 200px;
  height: 200px;
  padding: 16px;
  border: solid 1px black;
  display: flex;
  justify-content: center;
  align-items: center;
}

.button {
  z-index: 1;
  /* This is the important one, the rest is styling */
  width: min-content;
  height: min-content;
  padding: 8px;
  background: red;
  white-space: nowrap;
  color: white;
}

.button:hover {
  background: darkred;
  cursor: pointer;
}

a.cover {
  display: block;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
<div class="container">
  <a class="cover" href="#">Real link</a>
  <div class="button">Dummy link</div>
</div>

This approach satisfies the 1st and 2nd of the required behaviors listed above, but not the 3rd. Clicks on the dummy button are not passed to the a element below.

If I use pointer-events: none on the dummy button to pass the click through to the a element, then there is no hover animation on the dummy button, which violates the 1st requirement. Applying that same property with the :hover or :active pseudo-selectors gets me close, but then the 3rd requirement is violated once again.

I tried placing the a element on top (i.e., removing z-index: 1 ; from the dummy button) to meet the 3rd requirement; then, no clicks would need to pass through the dummy button to reach the link. However, the dummy button was then underneath the a element, so it no longer received hovers, which violated the 1st requirement.

I attempted to address this by using sibling selectors to turn on the dummy button’s hover effect when the a element is hovered. However, the dummy button then appeared "hovered" when the cursor was anywhere in the container, not only when the cursor was directly over the button, violating the 2nd requirement.

This has got me stumped. I would appreciate any recommendations on how to achieve this affect with the above requirements (once again, looking for HTML/CSS solutions ONLY).

EDIT: Right under "What I’ve tried," I mentioned that I wanted to avoid wrapping the entire thing in an a tag, but I neglected to mention why. It is because I will have additional, unrelated links inside the container, and since I can’t nest a tags, the container cannot be an a element. I just want to avoid having multiple links that point to the same place. Apologies for the confusion.

3

Answers


  1. Make the container a link and swap out the inner link for a span since it’s redundant anyway.

    .container {
      position: relative;
      width: 200px;
      height: 200px;
      padding: 16px;
      border: solid 1px black;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      text-decoration: none;
    }
    
    .button {
      z-index: 1;
      width: min-content;
      height: min-content;
      padding: 8px;
      background: red;
      white-space: nowrap;
      color: white;
    }
    
    .button:hover {
      background: darkred;
      cursor: pointer;
    }
    <a href="#" class="container">
      <span class="cover">Not a real link</span>
      <div class="button">Dummy link</div>
    </a>
    Login or Signup to reply.
  2. You can safely place <div> inside <a> – it will be valid. Next, you can disable the styles for <a> and provide a cursor: pointer to the <div> button:

    .container {
      all: unset;
      position: relative;
      /* This is the important one, the rest is styling */
      width: 200px;
      height: 200px;
      padding: 16px;
      border: solid 1px black;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    .button {
      padding: 8px;
      background: red;
      white-space: nowrap;
      color: white;
      cursor: pointer;
      
    }
    
    .button:hover {
      background: darkred;
      cursor: pointer;
    }
    <a href="#" class="container">
      <div class="button">Dummy link</div>
    </a>
    Login or Signup to reply.
  3. Flexbox and position is overkill for OP’s criteria. The following demo is a simplified solution. It appears that links in Snippets no longer work, so go here if you want a functioning demo. Details are commented in the demo.

    /**
     * Flexbox has been removed because position is used for centering
     * <button>.
     */
    
    
    /** <div class="card">
     * A simple <div> that wraps around the <a>.
     * A light grey background is added to show that it doesn't
     * affect dimensions, position, etc..
     */
    
    .card {
      max-width: min-content;
      background: lightgrey;
    }
    
    
    /** <a>
     * <a> is the actual container with simple parameters.
     * The cursor is default (arrow).
     */
    
    .card a {
      position: relative;
      display: block;
      width: 200px;
      height: 200px;
      border: solid 1px black;
      cursor: default;
    }
    
    
    /** <button>
     * <button> is positioned to the center of <a> with top and left.
     * transform: translate() offsets <button> by 50% of it's width
     * and height.
     * z-index isn't needed because the <button> is inside the <a>.
     */
    
    .card button {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      padding: 8px;
      color: white;
      background: red;
    }
    
    .card button:hover {
      background: darkred;
      cursor: pointer;
    }
    <!--
      The <button> is inside the <a> since everthing within the actual
      card is a link.
    -->
    <div class="card">
      <a href="https://google.com" target="_blank">Real Link
        <button>Dummy Link</button>
      </a>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search