<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>

<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;
      farthest-side at bottom left,
      rgba(255, 0, 255, 0.5), 
      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;
View Compiled
 * 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 => {

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.