JSFiddle - React, Tailwind, and code Playground

by stitot

HTML

<table class="demo-table">
  <thead>
    <tr>
      <!-- LEFT aligned -->
      <th class="hcg-th hcg-align-left">
        <div class="hcg-header-inner">
          <span class="hcg-header-text">Very long left aligned header text</span>
          <div class="hcg-header-icons">
            <button class="hcg-icon-btn">▲</button>
            <button class="hcg-icon-btn">⌕</button>
          </div>
        </div>
      </th>

      <!-- CENTER aligned -->
      <th class="hcg-th hcg-align-center">
        <div class="hcg-header-inner">
          <span class="hcg-header-text">Center aligned header text</span>
          <div class="hcg-header-icons">
            <button class="hcg-icon-btn">i</button>
            <button class="hcg-icon-btn">⋮</button>
          </div>
        </div>
      </th>

      <!-- RIGHT aligned -->
      <th class="hcg-th hcg-align-right">
        <div class="hcg-header-inner">
          <span class="hcg-header-text">Right aligned numeric header</span>
          <div class="hcg-header-icons">
            <button class="hcg-icon-btn">⇅</button>
          </div>
        </div>
      </th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>111</td>
      <td>222</td>
      <td>333</td>
    </tr>
  </tbody>
</table>

<script>
  document.addEventListener('DOMContentLoaded', function () {
    const headers = document.querySelectorAll('.hcg-th');

    headers.forEach(th => {
      const inner = th.querySelector('.hcg-header-inner');
      const icons = th.querySelector('.hcg-header-icons');
      const text  = th.querySelector('.hcg-header-text');
      if (!inner || !icons || !text) return;

      function updateCenterMaxWidth() {
        if (!th.classList.contains('hcg-align-center')) return;

        // Reset before measuring so we get natural text width
        text.style.maxWidth = 'none';

        const containerWidth = inner.clientWidth;
        const iconsWidth =...

CSS

/* --------------------------------------------- */
/* Base table                                    */
/* --------------------------------------------- */
.demo-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}

.demo-table th,
.demo-table td {
  border: 1px solid #ccc;
  padding: 6px 8px;
  font-family: system-ui, sans-serif;
  font-size: 13px;
}

.hcg-th {
  position: relative;
}

/* --------------------------------------------- */
/* Shared header structure                       */
/* --------------------------------------------- */

.hcg-header-inner {
  display: flex;
  align-items: center;
  width: 100%;
  min-width: 0;
  gap: 4px;
}

.hcg-header-text {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.hcg-header-icons {
  display: flex;
  align-items: center;
  gap: 2px;
}

.hcg-icon-btn {
  border: none;
  background: transparent;
  padding: 2px;
  width: 18px;
  height: 18px;
  cursor: pointer;
  font-size: 12px;
  border-radius: 4px;
}

.hcg-icon-btn:hover {
  background: rgba(0, 0, 0, 0.06);
}

.hcg-icon-btn:focus-visible {
  outline: 2px solid #2684ff;
  outline-offset: 2px;
}

/* --------------------------------------------- */
/* LEFT aligned                                  */
/* --------------------------------------------- */

.hcg-align-left .hcg-header-inner {
  justify-content: flex-start;
}

.hcg-align-left .hcg-header-text {
  flex: 1 1 auto;
  text-align: left;
}

/* icons slide in by max-width */
.hcg-align-left .hcg-header-icons {
  max-width: 0;
  opacity: 0;
  overflow: hidden;
  transition: max-width 0.2s ease, opacity 0.2s ease;
}

.hcg-align-left.hcg-icons-visible .hcg-header-icons {
  max-width: 200px; /* big enough for your icons */
  opacity: 1;
}

/* --------------------------------------------- */
/* RIGHT aligned                                 */
/*...