<div class="box">
  <input type="checkbox" id="expanded">
  <p>Hey, don't cut me off like that. I want to speak my mind and don't appreciate being put into a box.</p>
  <!-- Note: I have sadly not found a nice way to put the more button/label inline inside the paragraph, after or before the ellipsis. -->
  <label for="expanded" role="button">read more</label>
</div>

<footer>Inspired by <a href="https://css-tricks.com/almanac/properties/l/line-clamp/">Geoff's demos</a></footer>
.box {
  
  input {
    opacity: 0;
    position: absolute;
    pointer-events: none;
  }
  
  p {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;  
    overflow: hidden;
  }
  
  input:focus ~ label {
    outline: -webkit-focus-ring-color auto 5px;
  }
  
  input:checked + p {
    -webkit-line-clamp: unset;
  }
  
  input:checked ~ label,
  p:not(.truncated) ~ label{
    display: none;
  }
  
}


/* From here on presentation styles */
body {
  align-items: center;
  background: 
    radial-gradient(
      farthest-side at bottom left,
      rgba(255, 0, 255, 0.5), 
      #246756
    ),
    radial-gradient(
      farthest-corner at bottom right,
      rgba(255, 50, 50, 0.5), 
      #246756 400px
    );
  display: flex;
  flex-direction: column;
  height: 100vh;
  justify-content: center;
  line-height: 1.5;
}

.box {
  background-color: #fff;
  box-shadow: 2px 2px 10px #246756;
  padding: 2em;
  width: 15vw;
  max-width: 250px;
  min-width: 150px;
  
  p {
    margin: 0;
  }
  
  label {
    border-radius: 4px;
    padding: 0.2em 0.6em;
    border: 1px solid #009ce2;
    background-color: #00acff;
    color: #fff;
    font-size: 0.8em;
  }
  
}

footer {
  margin-top: 2em;
  color: #ffffff66;
  a {
    color: #fff;
  }
}
/*
 * Since the pseudo class :truncated is sadly not a thing, if your truncated box is responsive or the text in the box is of arbitrary size, the following code adds or removes a "truncated" class to simulate the feature.
 *
 * Warning: Doesn't work in all browsers due to the use of ResizeObserver, use something like the window's resize event if you need it to be moar cross browser.
 */
const ps = document.querySelectorAll('p');
const observer = new ResizeObserver(entries => {
  for (let entry of entries) {
    entry.target.classList[entry.target.scrollHeight > entry.contentRect.height ? 'add' : 'remove']('truncated');
  }
});

ps.forEach(p => {
  observer.observe(p);
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.