Search code examples
csshovercss-transitionsletter-spacing

Keep width when using letter-spacing on hover


I have a some basic button styles where on :hover I add the letter-spacing property:

.btn {
  display: inline-block;
  text-align: center;
  background-color: transparent;
  border: 1px solid transparent;
  padding: 0.375rem 0.75rem;
  font-size: 1rem;
  border-radius: 0.25rem;
  cursor: pointer;
  transition: all 0.2s ease;
}

.btn-primary {
  background-color: #8065F1;
  color: #FFFFFF;
}

.btn-large {
  border-radius: 32px;
  box-shadow: 0 2px 80px 0 rgba(74, 74, 74, 0.23);
  padding: 0.25rem 3rem;
  font-size: 1.5rem;
  text-transform: uppercase;
}

.btn:hover {
  letter-spacing: 4px;
}
<button type="button" class="btn btn-primary btn-large">Lorem</button>

Is there a way that width doesn't expand? Like adding min/max-width? However the problem is that button elements can contain different string length:

.btn {
  display: inline-block;
  text-align: center;
  background-color: transparent;
  border: 1px solid transparent;
  padding: 0.375rem 0.75rem;
  box-shadow: 0 2px 80px 0 rgba($grey, 0.23);
  font-size: 1rem;
  border-radius: 0.25rem;
  cursor: pointer;
  transition: all 0.2s ease;
}

.btn-primary {
  background-color: #8065F1;
  color: #FFFFFF;
}

.btn-large {
  border-radius: 32px;
  -webkit-box-shadow: 0 2px 80px 0 rgba(74, 74, 74, 0.23);
  box-shadow: 0 2px 80px 0 rgba(74, 74, 74, 0.23);
  padding: 0.25rem 3rem;
  font-size: 1.5rem;
  text-transform: uppercase;
  min-width: 240px;
}

.btn:hover {
  letter-spacing: 4px;
}
<p>I need this "effect" (I added some min-width):</p>
<button type="button" class="btn btn-primary btn-large">Lorem</button>

<p>However it won't work for larger strings</p>
<button type="button" class="btn btn-primary btn-large">Lorem Ipsum</button><br><br>
<button type="button" class="btn btn-primary btn-large">Lorem Ipsum Dolor</button>

I know I can use JS and append the elements fixed width to it, however I'm looking for a CSS solution - if there is one?


Solution

  • On idea to approximate this is to duplicate the text considering a hidden one that has already the letter-spacing and another one on the top that you animate to fill the space already defined by the hidden text:

    Here is an idea by making the text color the same as background:

    .btn {
      display: inline-block;
      text-align: center;
      background-color: transparent;
      border: 1px solid transparent;
      padding: 0.375rem 0.75rem;
      font-size: 1rem;
      border-radius: 0.25rem;
      cursor: pointer;
      transition: all 0.2s ease;
      margin-bottom:10px;
    }
    
    .btn-primary {
      background-color: #8065F1;
      color: #FFFFFF;
    }
    
    .btn-large {
      border-radius: 32px;
      box-shadow: 0 2px 80px 0 rgba(74, 74, 74, 0.23);
      padding: 0.25rem 3rem;
      font-size: 1.5rem;
      text-transform: uppercase;
    }
    
    .btn::before {
      content:attr(data-text);
      position:absolute;
      left:0;
      right:0;
      text-align:center;
      letter-spacing: initial;
      color:#fff;
      transition: all 0.2s ease;
    }
    .btn {
      letter-spacing: 4px;
      color:#8065F1;
      position:relative;
    }
    .btn:hover::before {
      letter-spacing: 4px;
    }
    <div><button type="button" class="btn btn-primary btn-large" data-text="Lorem">Lorem</button></div>
    
    <div><button type="button" class="btn btn-primary btn-large" data-text="Lorem Ipsum">Lorem Ipsum</button></div>
    <div><button type="button" class="btn btn-primary btn-large" data-text="Lorem Ipsum Dolor">Lorem Ipsum Dolor</button></div>

    Another one using opacity and both pseudo element in case the background is not a solid color:

    .btn {
      display: inline-block;
      text-align: center;
      background-color: transparent;
      border: 1px solid transparent;
      padding: 0.375rem 0.75rem;
      font-size: 1rem;
      border-radius: 0.25rem;
      cursor: pointer;
      transition: all 0.2s ease;
      margin-bottom:10px;
    }
    
    .btn-primary {
      background: linear-gradient(#8065F1,purple);
      color: #FFFFFF;
    }
    
    .btn-large {
      border-radius: 32px;
      box-shadow: 0 2px 80px 0 rgba(74, 74, 74, 0.23);
      padding: 0.25rem 3rem;
      font-size: 1.5rem;
      text-transform: uppercase;
    }
    .btn {
      position:relative;
      font-size:0;
    }
    .btn::before {
      content:attr(data-text);
      position:absolute;
      left:0;
      right:0;
      text-align:center;
      letter-spacing: initial;
      font-size: 1.5rem;
      transition: all 0.2s ease;
    }
    .btn::after {
      content:attr(data-text);
      letter-spacing: 4px;
      opacity:0;
      font-size: 1.5rem;
    }
    .btn:hover::before {
      letter-spacing: 4px;
    }
    <div><button type="button" class="btn btn-primary btn-large" data-text="Lorem">Lorem</button></div>
    
    <div><button type="button" class="btn btn-primary btn-large" data-text="Lorem Ipsum">Lorem Ipsum</button></div>
    <div><button type="button" class="btn btn-primary btn-large" data-text="Lorem Ipsum Dolor">Lorem Ipsum Dolor</button></div>