5

So imagine you have 3 buttons next to each other.

In CSS you give it a style of a border of 3px and on hover that the border changes color. In render you see that the top and bottom, left, of first child, and right, of last child, borders are all 3px as set. But in between its 6px, as both buttons have there own border next to each other.

Lets call this a Situation-a

So to fix it you make it so every button, that is not first, would move the same -3px to the left so make it share the same border visualy.

But then on hover the first button right side of the border does not change the color as the button next to him is covering it.

Lets call this a Situation-b

But both solutons are wrong. What should happen is that the border between them is still 3px and on hover, no matter witch button, all borders around that 1 button would change color.

BUT HOW you do it?

Adding z-index to the button with :hover does NOT solve it since once you unhover, the border that is being overlapped instantly changes back the color while the rest of the borders slowly changes back the color.

Basically, buttons next to each other need to share the same border and on hover change the color of same border but to not create a 6px gap.

Here is a snippet of both Situations

* {
  padding: 0;
  margin: 0;
}
.parent {
  font-size: 0;
  margin: 10px;
}
.child {
  font-size: 24px;
  padding: 5px 15px;
  border-radius: unset;
  border: 3px solid black;
  background-color: lightgray;
  transition: border 0.25s ease-in-out;
}
.child:hover {
  border: 3px solid cyan;
}
.situation-b .child:not(:first-child) {
  margin-left: -3px;
}
Situation A (border between is 6px)
<div class="parent situation-a">
  <button class="child">action a</button>
  <button class="child">action b</button>
  <button class="child">action c</button>
</div>
Situation B (on button A hover, right border is not seen)
<div class="parent situation-b">
  <button class="child">action a</button>
  <button class="child">action b</button>
  <button class="child">action c</button>
</div>

Here is a created exmaple online of the issue: https://codesandbox.io/s/optimistic-dubinsky-1ji85?file=/index.html

Lith
  • 1,251
  • 6
  • 19

5 Answers5

3

If you've support for CSS grid, you can do something like the following:

.buttons {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  column-gap: 3px;
  width: 300px;
  padding: 3px;
  background-color: black;
}

button {
  border: 0;
  outline: 3px solid transparent;
  transition: all 1s ease 0s;
}

button:hover {
  outline-color: cyan;
}
<div class="buttons">
  <button>action a</button>
  <button>action b</button>
  <button>action c</button>
</div>

Explanation

The wrapping element, .buttons, provides the black color as a background. Paired with padding and column-gap, it appears like the buttons have a 3px border. Use outline which has no effect on document layout to allow the overlap during transition.


You can also do this with flexbox:

.buttons {
  display: flex;
  width: 300px;
  padding: 3px;
  background-color: black;
}

button {
  flex: 1;
  border: 0;
  outline: 3px solid transparent;
  transition: all 1s ease 0s;
}

button:not(:first-child) {
  margin-left: 3px;
}

button:hover {
  outline-color: cyan;
}
<div class="buttons">
  <button>action a</button>
  <button>action b</button>
  <button>action c</button>
</div>
chazsolo
  • 7,873
  • 1
  • 20
  • 44
  • 1
    While the outcome, in both grid and flex, situations is perfect, it has to be done with borders as the buttons themself come from 3rd party and I am not allowed to strip there borders nor change how they are rendered. Its a part of the requirement. Sorry for not specifying it in the post. I still give you a up vote as this would work if not for the requirements – Lith Jun 11 '21 at 17:07
  • 1
    @Lith there's always something ;) glad you were able to get an answer! – chazsolo Jun 11 '21 at 17:14
3

You can actually do it with the z-index. You just set a base z-index and put the transition on both the border and the z-index. See the example below where I exaggerated the transition time so that you can see that it works:

* {
  padding: 0;
  margin: 0;
}
.parent {
  font-size: 0;
  margin: 10px;
}
.child {
  font-size: 24px;
  padding: 5px 15px;
  border-radius: unset;
  border: 3px solid black;
  background-color: lightgray;
  transition: all 1s ease-in-out;
  position: relative;
  z-index:1;
}
.child:hover {
  border: 3px solid cyan;
  z-index:2;
}
.situation-b .child:not(:first-child) {
  margin-left: -3px;
}
Situation A (border between is 6px)
<div class="parent situation-a">
  <button class="child">action a</button>
  <button class="child">action b</button>
  <button class="child">action c</button>
</div>
Situation B (on button A hover, right border is not seen)
<div class="parent situation-b">
  <button class="child">action a</button>
  <button class="child">action b</button>
  <button class="child">action c</button>
</div>
j08691
  • 204,283
  • 31
  • 260
  • 272
1

Answer to how to delay an "out" transition on hover:

You can also use z-index in the transition property. It doesn't support easing the way other properties do but it DOES support the transition delay.

* {
  padding: 0;
  margin: 0;
}
.parent {
  font-size: 0;
  margin: 10px;
}
button {
    position: relative;
  font-size: 24px;
  padding: 5px 15px;
  border-radius: unset;
  border: 3px solid black;
  background-color: lightgray;
    margin-right: -3px;
    z-index:1;
    transition: z-index 0s 1s, border 1s ease-in-out;
}

button:hover {
  border: 3px solid cyan;
    z-index: 10;
    transition: z-index 0s, border 1s ease-in-out;
}
<div class="parent">
  <button>action a</button>
  <button>action b</button>
  <button>action c</button>
</div>

Alternative method for solving the layout issue (next sibling)

Since a delay doesn't solve the actual layout issue and z-index is posing problems, there are other ways. Maybe this will help someone whose issue can't be solved by stacking elements.

In CSS we can use the Next sibling selector (+) to pick the NEXT DOM element of a type/class for interaction.

* {
  padding: 0;
  margin: 0;
}
.parent {
  font-size: 0;
  margin: 10px;
}
button {
    position: relative;
  font-size: 24px;
  padding: 5px 15px;
  border-radius: unset;
  border: 3px solid black;
  background-color: lightgray;
    margin-right: -3px;
    z-index:1;
    transition: border 0.25s ease-in-out;
}

button:hover {
  border-color: cyan;
}

/* next element in the dom */
button:hover + button {
    border-left-color: cyan;
}
<div class="parent">
  <button>action a</button>
  <button>action b</button>
  <button>action c</button>
</div>

Note: Due to the way borders render, this results in a strange cut-out look on larger border widths, so I'd probably solve this with a box-shadow instead of a border on the elements.

Bryce Howitson
  • 7,339
  • 18
  • 40
  • 1
    Sadly this creates the same issue as mentioned to JPortillo. The right border of the first button insta fades in or out. Here is a gif of how it looks like https://imgur.com/a/8wO27ju – Lith Jun 11 '21 at 17:04
  • Not if you add a delay to the z-index transition... I slowed the animation down so you can see the effect. – Bryce Howitson Jun 11 '21 at 17:05
  • 1
    Sadly, this still creates an issue. If you hover on `action b` button, wait till full transition plays out and then quicly hover to `action a` then the right border of button `action a` will be black for some time. Here is a gif: https://imgur.com/a/5yH80W3 – Lith Jun 11 '21 at 17:09
  • @Lith since adding a delay doesn't really solve your issue, here's another way to do it that may help you in the future. – Bryce Howitson Jun 11 '21 at 17:57
0

You actually can do it with your z-index idea. You must only set position:absolute, position:relative, or position:fixed to element to work:

button {
      border: 4px solid black;
      height: 50px;
      width: 100px;
      border-radius: 0;
      margin-left: -8px;
      
      position: relative; /* Important */
}

button:hover {
      border: 4px solid blue;
      z-index: 2;
}
<div style="border-collapse: collapse;">
    <button>aa</button>
    <button>bb</button>
</div>
MetropolisCZ
  • 567
  • 1
  • 8
  • 20
  • 2
    You omitted the transition. When you add it back in your solution fails the same way another user's answer fails. – j08691 Jun 11 '21 at 17:10
-1

I think all you were missing was position: relative; so that z-index was taken.

* {
  padding: 0;
  margin: 0;
  font-size: 0;
}

.parent {
  font-size: 0;
  margin: 10px;
}

.child {
  font-size: 24px;
  padding: 5px 15px;
  border-radius: unset;
  border: 3px solid black;
  background-color: lightgray;
  transition: border 0.25s ease-in-out;
  position: relative;
  z-index: 1;
}

.child:hover {
  z-index: 2;
  border-color: red;
}

.situation-b .child:not(:first-child) {
  margin-left: -3px;
}
    <div class="parent situation-a">
      <button class="child">action a</button>
      <button class="child">action b</button>
      <button class="child">action c</button>
    </div>
    <div class="parent situation-b">
      <button class="child">action a</button>
      <button class="child">action b</button>
      <button class="child">action c</button>
    </div>
JPortillo
  • 543
  • 3
  • 9
  • If you hover on the button A `action a` and then unhover, you can see that the right side of the button `action a` instantly chnages to black as the rest slowly turns to red. – Lith Jun 11 '21 at 16:48
  • You must've missed the OP's point, _"Adding z-index to the button with :hover does NOT solve it since once you unhover, the border that is being overlapped instantly changes back the color while the rest of the borders slowly changes back the color."_ – j08691 Jun 11 '21 at 16:48
  • @JPortillo sorry, alrho i am not the one to devote but, as explained above, the second set of buttons do not work as wanted. change the transition time form 0.25 to something slower like 1s and you will notice that on button `action a` unhover, the right side of the same button INSTANTLY changes color as the erst of his borders SLOWLY changes color. Ites because the `z-index` is lost instantly rather then after a set amount of seconds hence the `action b` buttons borders overlaps. – Lith Jun 11 '21 at 16:54
  • 1
    Your answer *is* wrong. When you hover out, the right border doesn't fade; it immediately changes. – j08691 Jun 11 '21 at 16:54
  • 1
    If you're referring to me then no, I didn't copy you at all. I provided a complete and working example. – j08691 Jun 11 '21 at 17:11
  • 1
    it was not you, indeed. my apologies. – JPortillo Jun 11 '21 at 17:13