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 -}}