Table

Element that displays tabular data in a structured format of rows and columns.

Class name Type
.prs-table-container Component Container
.prs-table Component For <table>
.prs-cell-actions Component Header cell actions container
.prs-cell-info Component Info tooltip container
.prs-cell-sort Component Sorting container
.prs-table-bordered Modifier Add divider lines
.prs-table-striped Modifier Even/odd highlight
.prs-table-compact Modifier Less padding
.prs-table-pin-rows Modifier Sticky <thead> and <tfoot>
.prs-table-pin-cols Modifier Sticky <th> columns
.prs-cell-sort Modifier Sortable
.prs-cell-name Modifier Name/email
.prs-cell-stacked Modifier Stacked content
.prs-cell-end Modifier Justify end/right
  • Default

    Here's a decent starting point with a little help from Alpine.js (just to toggle the asd/desc caret direction).

    <div x-data="{ sort: 'asc' }" class="w-screen max-w-lg"> <!-- <- this is just for demo purposes -->
      <div class="prs-table-container">
        <table class="prs-table prs-table-bordered">
          <thead>
            <tr>
              <th class="prs-cell-stacked">
                <span class="prs-cell-actions">
                  <span>Header</span>
                  <a x-tooltip="'More info'" @click.prevent href="#tooltip" class="prs-cell-info" aria-label="More info"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" role="img"><use href="/_assets/prs-icons.svg#content-type-info-outline" /></svg></a>
                  <a
                    @click.prevent="sort = (sort === 'asc' ? 'desc' : 'asc')"
                    :aria-label="'Sorted '+ sort"
                    :class="sort"
                    class="prs-cell-sort"
                    href="#sort"
                  ><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" role="img"><use href="/_assets/prs-icons.svg#caret-down" /></svg></a>
                </span>
                <strong>Sub label</strong>
              </th>
              <th>Header</th>
              <th class="prs-cell-stacked prs-cell-end">
                <span>Header</span>
                <strong>Sub label</strong>
              </th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td class="prs-cell-stacked">
                <span>Cell</span>
                <em>Some data</em>
              </td>
              <td>Cell</td>
              <td class="prs-cell-end">Cell</td>
            </tr>
            <tr>
              <td class="prs-cell-stacked">
                <span>Cell</span>
                <em>Some data</em>
              </td>
              <td>Cell</td>
              <td class="prs-cell-end">Cell</td>
            </tr>
            <tr>
              <td class="prs-cell-name prs-cell-stacked">
                <span>First <strong>Last</strong></span>
                <em>me@domin.ext</em>
              </td>
              <td>Cell</td>
              <td class="prs-cell-end">Cell</td>
            </tr>
            <tr>
              <td class="prs-cell-stacked">
                <span>Cell</span>
                <em>Some data</em>
              </td>
              <td>Cell</td>
              <td class="prs-cell-end">Cell</td>
            </tr>
            <tr>
              <td class="prs-cell-stacked">
                <span>Cell</span>
                <em>Some data</em>
              </td>
              <td>Cell</td>
              <td class="prs-cell-end">Cell</td>
            </tr>
          </tbody>
          <tfoot>
            <tr>
              <th class="prs-cell-stacked">
                <span>Header</span>
                <strong>Sub label</strong>
              </th>
              <th>Header</th>
              <th class="prs-cell-stacked prs-cell-end">
                <span>Header</span>
                <strong>Sub label</strong>
              </th>
            </tr>
          </tfoot>
        </table>
      </div>
    </div>
    
  • Compact

    Less padding.

    <div class="w-screen max-w-lg"> <!-- <- this is just for demo purposes -->
      <div class="prs-table-container">
        <table class="prs-table prs-table-bordered prs-table-compact">
          <thead>
            <tr><th>Header</th><th>Header</th><th>Header</th></tr>
          </thead>
          <tbody>
            <tr><td>Cell</td><td>Cell</td><td>Cell</td></tr>
            <tr><td>Cell</td><td>Cell</td><td>Cell</td></tr>
            <tr><td>Cell</td><td>Cell</td><td>Cell</td></tr>
            <tr><td>Cell</td><td>Cell</td><td>Cell</td></tr>
          </tbody>
        </table>
      </div>
    </div>
    
  • Striped

    Event rows highlighted.

    <div class="w-screen max-w-lg"> <!-- <- this is just for demo purposes -->
      <div class="prs-table-container">
        <table class="prs-table prs-table-bordered prs-table-striped">
          <thead>
            <tr><th>Header</th><th>Header</th><th>Header</th></tr>
          </thead>
          <tbody>
            <tr><td>Cell</td><td>Cell</td><td>Cell</td></tr>
            <tr><td>Cell</td><td>Cell</td><td>Cell</td></tr>
            <tr><td>Cell</td><td>Cell</td><td>Cell</td></tr>
            <tr><td>Cell</td><td>Cell</td><td>Cell</td></tr>
          </tbody>
        </table>
      </div>
    </div>
    
  • Pinned Rows

    Scroll the table vertically.

    <div class="w-screen max-w-xs"> <!-- <- this is just for demo purposes -->
      <div class="prs-table-container" style="height: 18rem;"> <!-- <- give the container an explicit height -->
        <table class="prs-table prs-table-bordered prs-table-pin-rows">
          <thead><tr><th>A</th></tr></thead>
          <tbody>
            <tr><td>Ant-Man</td></tr>
            <tr><td>Aquaman</td></tr>
            <tr><td>Asterix</td></tr>
            <tr><td>The Atom</td></tr>
            <tr><td>The Avengers</td></tr>
          </tbody>
          <thead><tr><th>B</th></tr></thead>
          <tbody>
            <tr><td>Batgirl</td></tr>
            <tr><td>Batman</td></tr>
            <tr><td>Batwoman</td></tr>
            <tr><td>Black Canary</td></tr>
            <tr><td>Black Panther</td></tr>
          </tbody>
          <thead><tr><th>C</th></tr></thead>
          <tbody>
            <tr><td>Captain America</td></tr>
            <tr><td>Captain Marvel</td></tr>
            <tr><td>Catwoman</td></tr>
            <tr><td>Conan the Barbarian</td></tr>
          </tbody>
          <thead><tr><th>D</th></tr></thead>
          <tbody>
            <tr><td>Daredevil</td></tr>
            <tr><td>The Defenders</td></tr>
            <tr><td>Doc Savage</td></tr>
            <tr><td>Doctor Strange</td></tr>
          </tbody>
          <thead><tr><th>E</th></tr></thead>
          <tbody>
            <tr><td>Elektra</td></tr>
          </tbody>
          <thead><tr><th>F</th></tr></thead>
          <tbody>
            <tr><td>Fantastic Four</td></tr>
          </tbody>
          <thead><tr><th>G</th></tr></thead>
          <tbody>
            <tr><td>Ghost Rider</td></tr>
            <tr><td>Green Arrow</td></tr>
            <tr><td>Green Lantern</td></tr>
            <tr><td>Guardians of the Galaxy</td></tr>
          </tbody>
          <thead><tr><th>H</th></tr></thead>
          <tbody>
            <tr><td>Hawkeye</td></tr>
            <tr><td>Hellboy</td></tr>
            <tr><td>Incredible Hulk</td></tr>
          </tbody>
          <thead><tr><th>I</th></tr></thead>
          <tbody>
            <tr><td>Iron Fist</td></tr>
            <tr><td>Iron Man</td></tr>
          </tbody>
          <thead><tr><th>M</th></tr></thead>
          <tbody>
            <tr><td>Marvelman</td></tr>
          </tbody>
          <thead><tr><th>R</th></tr></thead>
          <tbody>
            <tr><td>Robin</td></tr>
            <tr><td>The Rocketeer</td></tr>
          </tbody>
          <thead><tr><th>S</th></tr></thead>
          <tbody>
            <tr><td>The Shadow</td></tr>
            <tr><td>Spider-Man</td></tr>
            <tr><td>Sub-Mariner</td></tr>
            <tr><td>Supergirl</td></tr>
            <tr><td>Superman</td></tr>
          </tbody>
          <thead><tr><th>T</th></tr></thead>
          <tbody>
            <tr><td>Teenage Mutant Ninja Turtles</td></tr>
            <tr><td>Thor</td></tr>
          </tbody>
          <thead><tr><th>W</th></tr></thead>
          <tbody>
            <tr><td>The Wasp</td></tr>
            <tr><td>Watchmen</td></tr>
            <tr><td>Wolverine</td></tr>
            <tr><td>Wonder Woman</td></tr>
          </tbody>
          <thead><tr><th>X</th></tr></thead>
          <tbody>
            <tr><td>X-Men</td></tr>
          </tbody>
          <thead><tr><th>Z</th></tr></thead>
          <tbody>
            <tr><td>Zatanna</td></tr>
            <tr><td>Zatara</td></tr>
          </tbody>
        </table>
      </div>
    </div>
    
  • All Modifiers

    Compact, Bordered, Striped, Pinned rows, Pinned columns. Scroll the table in any direction to see the pinned cells.

    <div class="w-screen max-w-md"> <!-- <- this is just for demo purposes -->
      <div class="prs-table-container h-72">
        <table class="prs-table prs-table-compact prs-table-bordered prs-table-striped prs-table-pin-cols prs-table-pin-rows">
          <thead><tr><th></th><td>Pinned</td><td>Pinned</td><td>Pinned</td><td>Pinned</td><td>Pinned</td><td>Pinned</td><th></th></tr></thead>
          <tbody>
            <tr><th>Pinned</th><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><th>Pinned</th></tr>
            <tr><th>Pinned</th><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><th>Pinned</th></tr>
            <tr><th>Pinned</th><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><th>Pinned</th></tr>
            <tr><th>Pinned</th><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><th>Pinned</th></tr>
            <tr><th>Pinned</th><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><th>Pinned</th></tr>
            <tr><th>Pinned</th><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><th>Pinned</th></tr>
            <tr><th>Pinned</th><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><th>Pinned</th></tr>
            <tr><th>Pinned</th><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><td>Cell</td><th>Pinned</th></tr>
          </tbody>
          <tfoot><tr><th></th><td>Pinned</td><td>Pinned</td><td>Pinned</td><td>Pinned</td><td>Pinned</td><td>Pinned</td><th></th></tr></tfoot>
        </table>
      </div>
    </div>
    

CSS

Full Library

Component Only

.prs-table-container {
  font-size: 1rem;
  line-height: 1.5rem;
  overflow-x: auto;
}

.prs-table {
  width: 100%;
  font-size: inherit;
  line-height: inherit;

  :where(th, td) {
    padding: 1rem;
    color: var(--prs-c-gray-800);
    vertical-align: middle;
  }

  &.prs-table-compact {
    :where(th, td) {
      padding: 0.5rem;
    }
  }

  :where(th, thead td, tfoot td) {
    color: var(--prs-c-gray-600);
    font-weight: 600;
    text-align: start;
    vertical-align: bottom;
    position: relative;
  }

  :where(tfoot th) {
    vertical-align: top;
  }

  &.prs-table-bordered {
    :where(thead tr, tbody tr:not(:last-child), tbody tr:first-child:last-child) {
      border-bottom: 1px solid var(--prs-c-gray-300);
    }

    :where(tfoot tr:last-child) {
      border-top: 1px solid var(--prs-c-gray-300);
    }
  }

  &.prs-table-striped {
    tbody tr:nth-child(2n) {
      background-color: var(--prs-c-gray-100);
    }

    tbody tr:nth-child(2n) :where(.prs-table-pin-cols tr th) {
      background-color: var(--prs-c-gray-100);
    }
  }

  :where(.prs-table-pin-rows thead tr, .prs-table-pin-rows tfoot tr) {
    background: var(--prs-c-white);
    position: sticky;
    top: 0;
    z-index: 1;
  }

  :where(.prs-table-pin-rows thead th, .prs-table-pin-rows tfoot th) {
    z-index: 1;
  }

  :where(.prs-table-pin-rows tfoot tr) {
    top: auto;
    bottom: 0;
  }

  :where(.prs-table-pin-cols tr th) {
    background: var(--prs-c-white);
    position: sticky;
    left: 0;
    right: 0;
  }
}

.prs-cell-name > :where(:first-child) {
  color: var(--prs-c-aqua);
}

.prs-cell-stacked {
  display: flex;
  flex-direction: column;

  &:where(td) > :where(:not(:first-child)) {
    color: var(--prs-c-gray-600);
    font-size: 0.75em;
    line-height: 1.125em;
  }

  &:where(th) > :where(:not(:first-child)) {
    font-weight: 600;
    font-size: inherit;
    color: var(--prs-c-gray-800);
  }
}

.prs-cell-end {
  text-align: end;
  align-items: flex-end;
  justify-content: flex-end;
}

.prs-cell-actions {
  display: flex;
  align-items: center;
  gap: 0.25rem;
}

.prs-cell-info {
  color: inherit;
  display: block;
  flex-shrink: 0;
  position: relative;
  z-index: 2;
}

.prs-cell-sort {
  color: inherit;
  display: block;
  flex-shrink: 0;

  &:after {
    display: block;
    position: absolute;
    inset: 0;
    z-index: 1;
    content: '';
  }

  svg,
  .icon,
  iconify-icon {
    width: 1rem;
    height: 1rem;
    display: block;
    transition-property: var(--prs-transition-property);
    transition-timing-function: var(--prs-transition-timing);
    transition-duration: var(--prs-transition-duration);
  }

  &.desc {
    svg,
    .icon,
    iconify-icon {
      transform: rotate(180deg);
    }
  }
}

Figma

Coming soon...