frontend/source/sass/components/_tables.scss

Summary

Maintainability
Test Coverage
// A variety of table styles.

@import '../base/variables';

table {
  width: 100%;
  margin-top: 2rem;
  font-size: $medium-font-size;
  border: none; // WDS override
  caption {
    color: $color-gray;
    text-align: left;
    font-weight: bold;
    padding-bottom: 1.5em;
    vertical-align: bottom;
  }
  p {
    font-size: $medium-font-size;
    margin: 0;
  }
  tbody {
    tr {
      &:nth-child(odd) {
        background: $color-gray-lightest;
        td.sorted {
          background: $color-gray-lighter;
        }
      }
    }
    th {
      /* for ths within tbodys */
      font-weight: $font-weight-normal;
    }
  }
  td,
  th {
    border: none; // WDS override
    background: transparent; // WDS override
  }
  th {
    font-weight: $font-weight-bold;
    /* the front page table headers are spans with the .tooltipstered class */
    span:not(.tooltipstered) {
      display: block;
      font-weight: $font-weight-normal;
    }
  }

  /* NOTE: #result_list is for Django admin changelists */
  &.sortable,
  &.price-list-table,
  &#result_list {
    thead {
      .umbrella-header th {
        border-bottom: none;
        border-top: none;
        color: $color-gray;
        font-size: $small-font-size;
        &:first-child,
        &:last-child {
          padding-bottom: $space-half;
          padding-left: 0;
        }
      }
      th {
        color: $color-gray-dark;
        position: relative;
        vertical-align: top;
        border-bottom: 2px solid $color-gray-medium;
        border-top: 2px solid $color-gray-medium;
        &.sortable {
          color: $color-blue;
          text-decoration: none;
          cursor: pointer;
          padding-bottom: 1.3em;
          &:hover {
            text-decoration: underline;
          }
          &:after {
            display: inline-block;
            line-height: 1em;
            height: 1em;
            position: absolute;
            padding-right: 0;
            margin-top: -.5em;
            font-weight: normal;
            font-size: .9em;
          }
        }
        &.number {
          text-align: right;
        }
        &.sorted {
          color: $color-gray-dark;
          background: $color-gray-lightest;
          position: relative;
          &:after {
            content: "▲";
            font-size: .9em;
            padding-left: $space-half;
            position: relative;
          }
          &.descending:after {
            content: "▼";
            position: relative;
          }
        }
      }
    }
  }

  &.hoverable {
    tbody {
      tr {
        &:hover > * {
          background: $color-green-lightest !important;
        }
      }
    }
  }
}

.price-list-table {
  td,
  td:first-child,
  td:last-child,
  th,
  th:first-child,
  th:last-child {
    position: relative;
    padding: $space-2x $space-1x;
  }
  td {
    vertical-align: top;
  }
}

.results-table_return-link {
  font-weight: $font-weight-bold;
  text-align: right;
}

.sortable {
  td,
  td:first-child,
  td:last-child,
  th,
  th:first-child,
  th:last-child {
    position: relative;
    padding: $space-2x $space-2x;
    vertical-align: top;
  }
}

.table-gap {
  border-right: 8px solid white;
}

td.sorted {
  background: $color-gray-lightest;
}

/* price analysis results styles */
.analysis {
  td {
    border-bottom: 3px solid white;
    &:first-of-type {
      font-weight: $font-weight-bold;
    }
    span {
      font-weight: $font-weight-normal;
    }
  }
  td.table-error-severe {
    background-color: $color-red-lightest;
    font-weight: $font-weight-bold;
    padding-left: 1.5rem;
    span {
      font-weight: $font-weight-normal;
    }
    &:before {
      background-color: $color-red-dark;
      content: '';
      height: 100%;
      left: 0;
      position: absolute;
      top: 0;
      width: 4px;
    }
  }
}

/* used on price list upload error page */
td.error {
  color: $color-red-dark;
  font-weight: $font-weight-bold;
  span {
    line-height: 1.2;
    position: relative;
    border-bottom: 1px dotted $color-red-dark;
    &:before {
      width: 13px;
      height: 13px;
      content: url("../../images/info-badge-filled.svg");
    }
    /* this is the tooltip's bottom arrow. It needs to be part of the span so
     * it is aware of the span's width. This is the only way I know of to position
     * the arrow over the info-badge (a background image on the span) when the
     * span is of variable width. */
    &:after {
      display: none;
      position: absolute;
      top: -$space-2x;
      left: -3px; /* positions the tooltip arrow over the info badge */
      border-top: 12px solid $color-red-lightest;
      border-right: 10px solid transparent;
      border-left: 10px solid transparent;
      content: " ";
    }

    &:hover {
      cursor: pointer;
      border-bottom-color: transparent;
      &:before {
        content: url("../../images/info-badge-outline.svg");
      }
      /* tooltip arrow */
      &:after {
        display: block;
      }
      + [role=tooltip] {
        overflow: auto;
        height: auto;
        width: auto;
        margin: 0;
        padding: $space-2x;
        right: 0;
        bottom: 95%;
        left: 0;
        background-color: $color-red-lightest;
        border-radius: $border-radius;
      }
    }
  }
  &:focus {
    span {
      border-bottom-color: transparent;
      &:before {
        content: url("../../images/info-badge-outline.svg");
      }
      /* tooltip arrow */
      &:after {
        display: block;
      }
      + [role=tooltip] {
        overflow: auto;
        height: auto;
        width: auto;
        margin: 0;
        padding: $space-2x;
        right: 0;
        bottom: 95%;
        left: 0;
        background-color: $color-red-lightest;
        border-radius: $border-radius;
      }
    }
  }
  /* make tooltip readable to screen readers */
  [role=tooltip] {
    position: absolute;
    overflow: hidden;
    height: 1px;
    width: 1px;
    margin: -1px;
    padding: 0;
    border: 0;


  }
  .errorlist {
    color: $color-red-dark;
    font-weight: $font-weight-normal;
    font-size: $small-font-size;
    border: none;
    max-width: 220px;
    margin: 0;
  }
}

th.exclude a.restore {
  position: absolute;
  font-size: 1rem;
  margin-bottom: $space-1x;
  margin-left: $space-2x;
  padding: 2px 0;
  text-transform: uppercase;
  text-decoration: underline;
  white-space: nowrap;
  vertical-align: bottom;
  bottom: 100%;
  left: 0;
}

td.column-exclude a {
  color: $color-gray;
  font-size: 1.2em;
  line-height: 1rem;
  font-weight: bold;
  text-decoration: none;
  &:hover {
    color: $color-black;
  }
}

th.number {
  text-align: left;
}


td.number,
.number {
  font-family: $font-family-mono;
  text-align: right;
  &.number--currency:before {
    content: "$";
    display: block;
    float: left;
    width: 1em;
    height: $medium-font-size;
  }
}
// field-base_year_rate applies to Django admin tables
// same styling as number/currency styles
.field-base_year_rate {
  font-family: $font-family-mono;
  text-align: right;
  &:before {
    content: "$";
    display: inline;
    width: 1em;
    height: $medium-font-size;
  }
}
/* Some labor categories (especially the "interpreter" categories)
 * are incredibly long, so we should limit the width. */
th.column-labor_category {
  font-weight: $font-weight-bold;
  word-wrap: break-word;
  padding-left: 0;
  overflow-wrap: break-word;
  max-width: 22rem;
}