Creating a "cut out" at the end of list item that is responsive using CSS

72 Views Asked by At

I have a list of links that when hovered have a stylish underline using :after - however, I need to add another effect when an item is active/ hovered on (pictured below).

When active, a "cut out triangle" should be at the end of the element to connect it visually to the wide expanded content to the right.

DESIRED END OUTCOME

I'm already adding an :after class on the link elements, so not sure how I can achieve this design?

.content-links {
  width: 25%;
  background-color: #fafafa;
  padding-left: 5.6rem;
  padding-top: 8rem;
  min-height: 100vh;
}

.content-links ul li {
  margin-bottom: 32px;
}

.content-links li {
  list-style: none;
}

.content-links a {
  text-decoration: none;
  font-size: 16px;
  line-height: 24px;
}

li a:after {
    z-index: 1;
    position: absolute;
    bottom: -8px;
    left: 0;
    right: 0;
    margin: auto;
    width: 0%;
    content: '.';
    color: transparent;
    background: black;
    height: 3px;
  }

  li a {
    position: relative;
  }

  li a:hover {
    color: green;
    font-weight: 600;
  }

  li a:hover:after,
  li a:active:after {
    transition: all 0.2s;
    width: 100%;
  }
  
  .content-panel {
    background-color: red;
  }
<div class="content-links">
  <ul>
    <li>
      <a href="#">Festivals</a>
    </li>
    <li>
      <a href="#">Events</a>
    </li>
    <li>
      <a href="#">Concerts</a>
    </li>
  </ul>
</div>

<div class="content-panel"></div>

EDIT: having explored :after and :before further, I'm not sure this is the most effective way of making this happen, I intended on using a triangle like so:

width: 0;
height: 0;
border-top: 25px solid transparent;
border-right: 50px solid #555;
border-bottom: 25px solid transparent;

but achieving both the borders (to make it look like a part of the container on the right) and actually the positioning (to be totally responsive) is proving to be a little hacky.

1

There are 1 best solutions below

0
UModeL On

The most correct would be to use a pseudo-element for the list item. It is also desirable to set the display: flex property to the list.

.content-links {
  min-height: 100vh;
  width: 25%;
  padding-top: 8rem;
  padding-left: 5.6rem;
  background-color: #fafafa;
  box-shadow: inset -4px 0 0 -2px #eeeee7;
}

.content-links ul {
  list-style: none;
  margin: 0;
  display: flex;
  flex-flow: column nowrap;
  gap: 32px;
  padding: 0;
}

.content-links ul li {
  position: relative;
}

li::before {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  z-index: 1;
  height: 100%;
  aspect-ratio: 1 / 1;
  background: #fff;
  box-shadow: inset 2px 2px 0 0 #eeeee7;
  transform: translatex(calc(50% - 2px)) rotate(-45deg) scale(0);
  pointer-events: none;
  transition: transform 0.2s ease-in-out;
}

li.active-item::before, /* .active-item for JS */
li:has(> a:hover)::before {
  transform: translatex(calc(50% - 2px)) rotate(-45deg) scale(1);
}

.content-links a {
  font-size: 16px;
  line-height: 24px;
  text-decoration: none;
}

li a {
  position: relative;
}

li a::after {
  content: '.';
  position: absolute;
  bottom: -8px;
  left: 0;
  right: 0;
  z-index: 1;
  margin: auto;
  height: 3px;
  width: 0%;
  color: transparent;
  background: black;
  pointer-events: none;
}

li a:hover {
  font-weight: 600;
  letter-spacing: -.35px;
  color: green;
}

li a:hover::after,
li a:active::after {
  width: 100%;
  transition: all 0.2s;
}

.content-panel {
  background-color: red;
}
<div class="content-links">
  <ul>
    <li>
      <a href="#">Festivals</a>
    </li>
    <li>
      <a href="#">Events</a>
    </li>
    <li>
      <a href="#">Concerts</a>
    </li>
  </ul>
</div>

<div class="content-panel"></div>