search

Home  >  Q&A  >  body text

Button with centered text and icon

<p>I am developing a button component with an icon and text. </p> <p>The icon is placed on the left side of the button, and the text is aligned in the middle of the button. </p> <p>If the text cannot fit into the button, it should not wrap but can be truncated. </p> <p>The key condition for truncating text is that the text should be the same distance from the edge of the button as the icon is from the edge of the button. </p> <p>So you cannot add equal inline padding in buttons. </p> <p>If the text is not truncated, it will be aligned in the middle of the button. </p> <p>However, once it starts overlapping the icon or failing to fit into the button, the text will be cut off. </p> <p>In other words, in the normal state, the position of the icon remains unchanged, but when there is not enough space for the text, the icon will push the text to the truncated position. </p> <p>Visual example of button</p> <p>I tried using CSS to achieve this effect, but failed. </p> <p>Finally, I added a ResizeObserver to each button that calculates whether there is enough space for the text. </p> <p>If there is not enough space, a different style will be applied to the icon. </p> <p>My ResizeObserver solution, try resizing the window</p> <p>CodePen</p> <pre class="brush:php;toolbar:false;">const CLASS_TEXT = `button__text`; const CLASS_NO_FREE_SPACE = `button_no-free-space`; const FREE_SPACE_SIZE = 70; const buttons = document.querySelectorAll(`.${CLASS_ROOT}`); const handleButtonResize = (entries) => { entries.forEach((entry) => { const { target } = entry; const text = target.querySelector(`.${CLASS_TEXT}`); const { width: widthButton } = entry.contentRect; if (!(text instanceof HTMLElement)) { return; } const widthText = text.offsetWidth; const freeSpaceLeft = (widthButton - widthText) / 2; const noFreeSpace = freeSpaceLeft <= FREE_SPACE_SIZE; target.classList.toggle(CLASS_NO_FREE_SPACE, noFreeSpace); }); }; const resizeObserver = new ResizeObserver(handleButtonResize); [...buttons].forEach((button) => { resizeObserver.observe(button); });</pre> <p><strong>My question is:</strong></p> <p>Is it possible to achieve the same effect using pure CSS? </p>
P粉033429162P粉033429162472 days ago517

reply all(1)I'll reply

  • P粉615886660

    P粉6158866602023-08-16 09:08:45

    Since the width of the icon is always the same (2em), we can use the ::after pseudo-element as a "buffer" for space balancing on the right.

    Set .button__icon to the flexible layout flow. This is crucial when "pushing" other elements. Give it margin-right to balance the left padding of the button.

    Create a ::after pseudo-element with flex-basis: calc(2em 20px). Among them, 2em is the width of .button__icon, and 20px is the margin-right of .button__icon. This balances the left and right spaces when .button__text is short.

    Apply justify-content: space-between to the parent element to help balance .button__icon, .button__text and ::afterWhen .button__text is shorter.

    Add flex-shrink: 999, a huge shrink factor so that the layout engine will shrink ::after elements first when .button__text is longer.

    *,
    *::before,
    *::after {
      box-sizing: border-box;
    }
    
    body {
      padding: 20px;
      min-height: 100vh;
      display: flex;
      flex-direction: column;
    }
    
    .grid {
      display: grid;
      grid-template-columns: repeat(3, minmax(0, 1fr));
      gap: 20px;
      margin: auto 0;
    }
    
    .button {
      position: relative;
      display: flex;
      justify-content: space-between;
      align-items: center;
      cursor: pointer;
      outline: none;
      border: 1px solid #808080;
      padding: 20px;
      width: 100%;
      background-color: transparent;
      min-height: 80px;
    }
    
    .button::before {
      content: "";
      width: 1px;
      position: absolute;
      top: 0;
      left: 50%;
      bottom: 0;
      background-color: red;
    }
    
    .button::after {
      flex: 0 999 calc(2em + 20px);
      content: "";
    }
    
    .button__icon {
      flex-shrink: 0;
      margin-right: 20px;
    }
    
    .button__text {
      white-space: nowrap;
      text-overflow: ellipsis;
      overflow: hidden;
      min-width: 0;
      text-align: center;
      border: 1px solid blue;
    }
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"/>
    
    <div class="grid">
      <button class="button">
        <i class="fa-solid fa-heart fa-2xl button__icon"></i>
       
        <span class="button__text">
          长文本,应该被截断
        </span>
      </button>
    
      <button class="button">
        <i class="fa-solid fa-thumbs-up fa-2xl button__icon"></i>
       
        <span class="button__text">
          中等长度
        </span>
      </button>
    
      <button class="button">
        <i class="fa-solid fa-house fa-2xl button__icon"></i>
       
        <span class="button__text">
          短
        </span>
      </button>
    </div>

    reply
    0
  • Cancelreply