Search code examples
htmlcssflexboxtransition

CSS can I transition to flex-end?


I'm trying to add a transition property to a checkbox switch. It's start position is flex-start and I want it to transition smoothly to flex-end. I just don't seem to be able to make it work on the flex-end property, yet bizarrely, the switch moves to flex-end and the background color transitions correctly.

I have looked at previous answers to problems similar to this, but I just can't crack this nut!

Can someone please let me know what I am missing here?

Thanks in advance.

/* the container controls the switch size */
.switch-container {
  width: 100%;
  height: 34px;
  background-color: coral;
}

/* hide the default checkbox */
.checkbox {
  position: absolute;
  left: -9999px;
}

/* target the label i.e. box around the slider */
.switch {
  position: relative;
  display: flex;
  width: 100%;
  height: 100%;
}

/* target the span element */
.slider {
  display: flex;
  position: absolute;
  cursor: pointer;
  /* set size of slider track */
  top: 10px;
  left: 0;
  right: 0;
  bottom: 10px;
  /* === end set size === */
  background-color: #ccc;
  align-items: center;
  justify-content: flex-start;
  transition: var(--transition);
}

.slider:before {
  position: absolute;
  content: "";
  height: 26px;
  width: 26px;
  background-color: rgb(199, 36, 36);
  box-shadow: 2px 2px cadetblue inset, -2px -2px cadetblue inset;
  transition: var(--transition);
}

input:checked + .slider:before {
  transform: translateX(129%);
  background-color: darkgreen;
  transition: var(--transition);
}

input:checked + .slider {
  background-color: #2196f3;
  transition: var(--transition);
}
<section class="switch-container">
      <label class="switch">
        <input type="checkbox" class="checkbox"/>
        <span class="slider"></span>
      </label>
    </section>
</html>

The problem I have is that If I make the switch size responsive to the size of the parent container, then using transform: translateX(some-value) to move the toggle slider the (some-value) has to be re-calculated to accommodate the current track size.


Solution

  • You cannot animate such values. I suggest, you animate margin-left instead

    .switch {
      --slider-width: 26px;
      --switch-width: calc(100% - var(--slider-width) / 2);
      /* --- */
      width: var(--switch-width);
      /* --- */
    }
    /* --- */
    .slider:before {
      /* --- */
      width: var(--slider-width);
      /* --- */
    }
    /* --- */
    input:checked + .slider {
      margin-left: calc(var(--switch-width) - var(--slider-width) / 2);
    }
    

    .checkbox {
      position: absolute;
      left: -9999px;
    }
    
    /* target the label i.e. box around the slider */
    .switch {
      --slider-width: 26px;
      --switch-width: calc(100% - var(--slider-width) / 2);
      position: relative;
      display: flex;
      width: var(--switch-width);
      height: 34px;
    }
    
    /* target the span element */
    .slider {
      display: flex;
      position: absolute;
      cursor: pointer;
      /* set size of slider track */
      top: 10px;
      left: 0;
      right: 0;
      bottom: 10px;
      /* === end set size === */
      background-color: #ccc;
      align-items: center;
      justify-content: flex-start;
      transition: 0.5s;
    }
    
    .slider:before {
      position: absolute;
      content: "";
      height: 26px;
      width: var(--slider-width);
      background-color: rgb(199, 36, 36);
      box-shadow: 2px 2px cadetblue inset, -2px -2px cadetblue inset;
      transition: 0.5s;
    }
    
    input:checked + .slider:before {
      /* transform: translateX(125%); */ /* note this transitions correctly */
      background-color: darkgreen;
      /* transition: var(--transition); */
      transition: 0.5s;
    }
    
    input:checked + .slider {
      background-color: #2196f3;
      margin-left: calc(var(--switch-width) - var(--slider-width) / 2);
      transition: 0.5s;
    }
    <label class="switch">
      <input type="checkbox" class="checkbox"/>
      <span class="slider"></span>
    </label>