JSFiddle - React, Tailwind, and code Playground

by bodine30

HTML

<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.3.0/css/font-awesome.css">
<table class="table-grid">
    <thead>
        <tr>
            <th sort-field="id" class="sortable">Row 1</th>
            <th sort-field="street" class="sortable">Title 2</th>
            <th>Title 3</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td data-field="id">0</td>
            <td><span class="label">custom styled column 0</span>
            </td>
            <td data-field="street">another lengthy column 0</td>
        </tr>
        <tr>
            <td data-field="id">1</td>
            <td><span class="label">custom styled column 1</span>
            </td>
            <td data-field="street">another lengthy column 1</td>
        </tr>        
        <tr>
            <td data-field="id">2</td>
            <td><span class="label">custom styled column 2</span>
            </td>
            <td data-field="street">another lengthy column 2</td>
        </tr>
    </tbody>
</table>

CSS

* {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}
*:before,
*:after {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}
html {
  font-size: 62.5%;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
body {
  font-family: "Segoe UI", "Open Sans", Arial, sans-serif;
  font-size: 1.4rem;
  line-height: 1.42857143;
  color: #ffffff;
  background-color: #424242;
}
table.table-grid {
    width: 100%;
}

table.table-grid th, table.table-grid td {
    padding: 4px 5px;
    line-height: 20px;
    text-align: center;
    vertical-align: middle;
    border-top: 1px solid #7ea4d3;
    white-space: nowrap;
}

table.table-grid th {
    font-weight: bold;
}
table.table-grid th.sortable {
    cursor: pointer;
    position: relative;
    padding-right: 30px;
    padding-left: 30px;
}
table.table-grid th.sortable::after {
    transition: all 750ms cubic-bezier(0.23, 1, 0.32, 1) 800ms;
    position: absolute;
    right: 10px;
    top: 0;
    line-height: 2.8rem;
    content:"\f0dc";
    font-family:"FontAwesome";
    font-size: 2rem;
    opacity: 0;
    transform: scale(0) rotateX(0deg);
}
table.table-grid th.sort-desc::after {
    transform: rotateX(0deg);
    content:"\f0dd";
    opacity: 1;
    transition: all 300ms cubic-bezier(0.23, 1, 0.32, 1);
}
table.table-grid th.sort-asc::after {
    transform: rotateX(180deg);
    content:"\f0dd";
    opacity: 1;
    transition: all 300ms cubic-bezier(0.23, 1, 0.32, 1);
}
table.table-grid tr:hover th.sortable:not(.sort-asc):not(.sort-desc)::after {
    opacity: 0.4;
    content:"\f0dc";
    transition: all 750ms cubic-bezier(0.23, 1, 0.32, 1);
    transform: scale(1) rotateX(0deg);
}

td::before {
    content: "before";
}
td::after {
    content: "after";
}

JavaScript

var sortAsc = document.getElementsByClassName('sort-asc');
var sortDesc = document.getElementsByClassName('sort-desc');
[].forEach.call(document.querySelectorAll('th.sortable'), function(el){
    el.addEventListener('click', function(e){
        var cl = e.target.classList, newSortDirection;
        if(!cl.contains('sort-asc') && !cl.contains('sort-desc')) {
            cleanClassNames();
            cl.add('sort-asc');
        } else {
            if(cl.contains('sort-desc')) newSortDirection = 'sort-asc';
            if(cl.contains('sort-asc')) newSortDirection = 'sort-desc';
            cleanClassNames();            
            cl.add(newSortDirection);
        }
    });
});
function cleanClassNames(){
[].forEach.call([].concat.call([].slice.call(sortAsc), [].slice.call(sortDesc)), function(el){
                el.classList.remove('sort-desc');
                el.classList.remove('sort-asc');                
            });
}