diff --git a/assets/js/tables.js b/assets/js/tables.js new file mode 100644 index 0000000..1db82fc --- /dev/null +++ b/assets/js/tables.js @@ -0,0 +1,28 @@ +/** + * @file + * A JavaScript file for responsive tables. + */ + +(function () { + + 'use strict'; + + const tables = document.querySelectorAll('.responsive-table'); + + tables.forEach(function (table) { + const headers = table.querySelectorAll('th'); + const rows = table.querySelectorAll('tr:has(td)'); + + rows.forEach(function (row) { + const cells = row.querySelectorAll('td'); + cells.forEach(function (cell, i) { + cell.setAttribute('role','cell'); + if (headers[i].innerText) { + cell.setAttribute('aria-label', headers[i].innerText); + } + }); + }); + }); + + +})(); diff --git a/assets/sass/base/tables/_tables.scss b/assets/sass/base/tables/_tables.scss index d064087..ca81642 100644 --- a/assets/sass/base/tables/_tables.scss +++ b/assets/sass/base/tables/_tables.scss @@ -5,9 +5,8 @@ table { // Add vertical rhythm margins. @include margin-block(1); - // Remove most spacing between table cells. + // Remove default spacing between table cells. border-collapse: collapse; - border-spacing: 0; // Prevent cramped-looking tables width: 100%; } @@ -16,4 +15,17 @@ td, th { // Remove most spacing between table cells. padding: 0; + // Align content to top of cells. + vertical-align: text-top; +} + +th { + // Align content to bottom of headers. + vertical-align: bottom; +} + +caption, +th { + // Align all text the same. + text-align: start; } diff --git a/assets/sass/components/tables/_tables.scss b/assets/sass/components/tables/_tables.scss new file mode 100644 index 0000000..4ae3925 --- /dev/null +++ b/assets/sass/components/tables/_tables.scss @@ -0,0 +1,50 @@ +.zebra-table, +%zebra-table { + --padding: .5rem; + + th, + td { + padding: var(--padding); + } + + tr:nth-child(odd) { + background-color: var(--color-row-odd); + } + + tr:nth-child(even) { + background-color: var(--color-row-even); + } + + // Add first-of-type to increase specificity. + tr:first-of-type:has(th) { + background-color: var(--color-row-header); + } +} + +.responsive-table, +%responsive-table { + --padding: .5rem; + + @include respond-to-max(s) { + th, + td { + padding: var(--padding); + } + + th { + display: none; + } + + td { + display: grid; + gap: var(--padding); + grid-template-columns: 12ch auto; + + &::before { + content: attr(aria-label) ':'; + font-weight: var(--fw-bold); + } + } + } + +} diff --git a/assets/sass/mixins/breakpoint/_respond-to.scss b/assets/sass/mixins/breakpoint/_respond-to.scss index 7ac27ab..874ee74 100644 --- a/assets/sass/mixins/breakpoint/_respond-to.scss +++ b/assets/sass/mixins/breakpoint/_respond-to.scss @@ -11,3 +11,17 @@ } } } + +@mixin respond-to-max($breakpoint) { + // If the key exists in the map + @if map-has-key($breakpoints, $breakpoint) { + // Prints a media query based on the value + @media (max-width: map-get($breakpoints, $breakpoint)) { + @content; + } + } @else { + @media (max-width: $breakpoint) { + @content; + } + } +} diff --git a/assets/sass/styles.scss b/assets/sass/styles.scss index a28e965..fba8c6f 100644 --- a/assets/sass/styles.scss +++ b/assets/sass/styles.scss @@ -55,6 +55,7 @@ @import 'components/responsive-video/responsive-video'; @import 'components/spacing/spacing'; @import 'components/stretch/stretch'; +@import 'components/tables/tables'; @import 'components/tags/tags'; @import 'components/visually-hidden/visually-hidden'; diff --git a/layouts/partials/scripts.html b/layouts/partials/scripts.html index 52f4040..094342d 100644 --- a/layouts/partials/scripts.html +++ b/layouts/partials/scripts.html @@ -33,6 +33,11 @@ {{ end -}} +{{ if $.Param "tables" -}} +{{ $tables := resources.Get "js/tables.js" | minify | fingerprint -}} + +{{ end -}} + {{ if .HasShortcode "contact" -}} {{ $contact := resources.Get "js/contact.js" | minify | fingerprint -}}