Willem D'Haeseleer Willem D'Haeseleer - 4 months ago 24
CSS Question

jumpy css font-size transition

Problem:


  • Hover cursor over the X and wait until the div is fully collapsed.

  • Move away from the X

  • Observe how div jumps to the bottom and straight back up.



Question:

How can I make the div expand without the font wrapping during the animation. ( I'm using a delayed font-size now, which seems to be problematic ).

Constrains:

These constrains are in effect due to the broader design of this isolated snippet.


  • Can not disable wrapping on
    outer
    div ( except for during the animation ).

  • Can not add a fixed width or height to the
    inner
    div.

  • Can not use JS



More context:

I'm using
white-space:nowrap
for the hover state so the text doesn't wrap while the div collapses. I then use a delayed
font-size
transition to prevent the text from wrapping while the div expands again ( since it's not possible to animate
white-space
or
display
).
It seems like somehow the
font-size
of 0 get's lost for a fraction of a second at the beginning of the animation or something. Not sure exactly why it's jumping in the beginning.



.outer {
width: 300px;
overflow: hidden;
border: 1px solid red;
transition: width 2s;
}

.button {
padding: 0px 10px;
display:inline-block;
}

.outer:hover {
width: 30px;
white-space: nowrap;
}
.outer .inner {
font-size: 15px;
display:inline-block;
transition: font-size 0s 2s;
}

.outer:hover .inner {
font-size: 0px;
}

<div class="outer">
<div class="button">
X
</div>
<div class="inner">
This is the variable content of the box
</div>
</div>




Answer

This is one of the problems you get when you change an object's size on hovering over itself. The trick to solving this is to use a parent container to hover on, which will always cover the width of the element, causing a slight ghosting effect, but compared to the visual effects your original solution had, this is much cleaner.

Your list of constraints isn't easy to work with, and the only way I can see this being solved under these circumstances is by making button and element that doesn't interfere with the document flow inside your container, so that nothing will wrap. Here are a few different attempts, one of which hopefully suiting your requirements:

solution fitting all contraints, using white-space

.outer-wrapper {
  width: 350px;
}
.outer {
  width: 350px;
  overflow: hidden;
  border: 1px solid red;
  transition: width 2s;
}
.button {
  width: 0;
  height: 0;
}
.button:after {
  content: 'X';
  padding: 0px 10px;
  display: inline-block;
}
.outer-wrapper:hover .outer {
  width: 30px;
}
.outer .inner {
  font-size: 15px;
  display: inline-block;
  margin-left: 2em;
  transition: font-size 0s 2s;
  white-space: nowrap;
}
.outer-wrapper:hover .outer .inner {
  font-size: 0px;
}
<div class="outer-wrapper">
  <div class="outer">
    <div class="button"></div>
    <div class="inner">
      This is the variable content of the box
    </div>
  </div>
</div>


solution fitting all contraints, changing the markup to use pre

This solution introduces a pre element, which will prevent wrapping on it's own because it inherits a white-space:pre declaration from it's default style.

.outer-wrapper {
  width: 350px;
}
.outer {
  width: 350px;
  overflow: hidden;
  border: 1px solid red;
  transition: width 2s;
}
.button {
  width: 0;
  height: 0;
}
.button:after {
  content: 'X';
  padding: 0px 10px;
  display: inline-block;
}
.outer-wrapper:hover .outer {
  width: 30px;
}
.outer .inner {
  font-size: 15px;
  display: inline-block;
  margin-left: 2em;
  transition: font-size 0s 2s;
}
.outer-wrapper:hover .outer .inner {
  font-size: 0px;
}
pre {
  font-family: inherit;
  margin: 0;
}
<div class="outer-wrapper">
  <div class="outer">
    <div class="button"></div>
    <div class="inner">
      <pre>This is the variable content of the box</pre>
    </div>
  </div>
</div>


hacking your way out

You need to prevent the inner div's contents from wrapping, and if you don't want to do this via white-space, the only option left is to replace all whitespaces in it with non-breaking spaces to make sure the element can't wrap anymore:

<div class="inner">
  This&nbsp;is&nbsp;the&nbsp;variable&nbsp;content&nbsp;of&nbsp;the&nbsp;box
</div>