From f7ac18164c6c8f8fcc99dc5a290a1cb22ddbd4b6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?"J.=20Rene=CC=81e=20Beach"?= <splendidnoise@gmail.com>
Date: Fri, 14 Sep 2012 12:22:05 -0400
Subject: [PATCH] Issue #1276908 by jessebeach, moshe weitzman, nod_,
 lewisnyman: make admin tables in Drupal core display well
 on all devices.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>
---
 core/includes/theme.inc                  |   67 ++++++++++++-
 core/misc/tabledrag.js                   |    4 +-
 core/misc/tableresponsive.js             |  150 ++++++++++++++++++++++++++++++
 core/modules/comment/comment.admin.inc   |    6 +-
 core/modules/dblog/dblog.admin.inc       |    8 +-
 core/modules/field_ui/field_ui.admin.inc |    6 +-
 core/modules/node/node.admin.inc         |   30 ++++--
 core/modules/system/system.admin.inc     |    4 +-
 core/modules/system/system.module        |   14 +++
 core/modules/user/user.admin.inc         |    8 +-
 core/themes/bartik/css/style.css         |   18 ++++
 core/themes/seven/style-rtl.css          |    5 -
 core/themes/seven/style.css              |   37 +++++++-
 core/themes/stark/css/layout.css         |   18 ++++
 14 files changed, 343 insertions(+), 32 deletions(-)
 create mode 100644 core/misc/tableresponsive.js

diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 7766952..ddf9627 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -34,6 +34,24 @@ const MARK_NEW = 1;
 const MARK_UPDATED = 2;
 
 /**
+ * A responsive table class.
+ *
+ * Indicates that a column has medium priority and thus
+ * can be hidden on narrow width devices and shown on medium+ width devices
+ * (i.e. tablets and desktops).
+ */
+const RESPONSIVE_PRIORITY_MEDIUM = 'priority-medium';
+
+/**
+ * A responsive table class.
+ *
+ * Indicates that a column has low priority and thus
+ * can be hidden on narrow and medium viewports and shown on wide
+ * devices (i.e. desktops).
+ */
+const RESPONSIVE_PRIORITY_LOW = 'priority-low';
+
+/**
  * @} End of "defgroup content_flags".
  */
 
@@ -1759,6 +1777,14 @@ function theme_breadcrumb($variables) {
  *     - "field": The database field represented in the table column (required
  *       if user is to be able to sort on this column).
  *     - "sort": A default sort order for this column ("asc" or "desc").
+ *     - "class": An array of values for 'class' attribute. In particular,
+ *        the least important columns that can be hidden on narrow and medium
+ *        width screens should have a 'priority-low' class, referenced with the
+ *        RESPONSIVE_PRIORITY_LOW constant. Columns that should be shown on
+ *        medium+ wide screens should be marked up with a class of
+ *        'priority-medium', referenced by with the RESPONSIVE_PRIORITY_MEDIUM
+ *        constant. Themes may hide columns with one of these two classes on
+ *        narrow viewports to save horizontal space.
  *     - Any HTML attributes, such as "colspan", to apply to the column header
  *       cell.
  *   - rows: An array of table rows. Every row is an array of cells, or an
@@ -1829,6 +1855,7 @@ function theme_table($variables) {
   $caption = $variables['caption'];
   $colgroups = $variables['colgroups'];
   $sticky = $variables['sticky'];
+  $responsive = $variables['responsive'];
   $empty = $variables['empty'];
 
   // Add sticky headers, if applicable.
@@ -1838,6 +1865,15 @@ function theme_table($variables) {
     // This is needed to target tables constructed by this function.
     $attributes['class'][] = 'sticky-enabled';
   }
+  // If the table has headers and it should react responsively to columns hidden
+  // with the classes represented by the constants RESPONSIVE_PRIORITY_MEDIUM
+  // and RESPONSIVE_PRIORITY_LOW, add the tableresponsive behaviors.
+  if (count($header) && $responsive) {
+    drupal_add_library('system', 'drupal.tableresponsive');
+    // Add 'responsive-enabled' class to the table to identify it for JS.
+    // This is needed to target tables constructed by this function.
+    $attributes['class'][] = 'responsive-enabled';
+  }
 
   $output = '<table' . new Attribute($attributes) . ">\n";
 
@@ -1894,13 +1930,28 @@ function theme_table($variables) {
     $rows[] = array(array('data' => $empty, 'colspan' => $header_count, 'class' => array('empty', 'message')));
   }
 
+  $responsive = array();
   // Format the table header:
   if (count($header)) {
     $ts = tablesort_init($header);
     // HTML requires that the thead tag has tr tags in it followed by tbody
     // tags. Using ternary operator to check and see if we have any rows.
     $output .= (count($rows) ? ' <thead><tr>' : ' <tr>');
+    $i = 0;
     foreach ($header as $cell) {
+      $i++;
+      // Track responsive classes for each column as needed. Only the header
+      // cells for a column are marked up with the responsive classes by a
+      // module developer or themer. The responsive classes on the header cells
+      // must be transferred to the content cells.
+      if (!empty($cell['class']) && is_array($cell['class'])) {
+        if (in_array(RESPONSIVE_PRIORITY_MEDIUM, $cell['class'])) {
+          $responsive[$i] =  RESPONSIVE_PRIORITY_MEDIUM;
+        }
+        elseif (in_array(RESPONSIVE_PRIORITY_LOW, $cell['class'])) {
+          $responsive[$i] =  RESPONSIVE_PRIORITY_LOW;
+        }
+      }
       $cell = tablesort_header($cell, $header, $ts);
       $output .= _theme_table_cell($cell, TRUE);
     }
@@ -1944,7 +1995,19 @@ function theme_table($variables) {
         $output .= ' <tr' . new Attribute($attributes) . '>';
         $i = 0;
         foreach ($cells as $cell) {
-          $cell = tablesort_cell($cell, $header, $ts, $i++);
+          $i++;
+          // Add active class if needed for sortable tables.
+          $cell = tablesort_cell($cell, $header, $ts, $i);
+          // Copy RESPONSIVE_PRIORITY_LOW/RESPONSIVE_PRIORITY_MEDIUM
+          // class from header to cell as needed.
+          if (isset($responsive[$i])) {
+            if (is_array($cell)) {
+              $cell['class'][] = $responsive[$i];
+            }
+            else {
+              $cell = array('data' => $cell, 'class' => $responsive[$i]);
+            }
+          }
           $output .= _theme_table_cell($cell);
         }
         $output .= " </tr>\n";
@@ -2865,7 +2928,7 @@ function drupal_common_theme() {
       'variables' => array(),
     ),
     'table' => array(
-      'variables' => array('header' => NULL, 'rows' => NULL, 'attributes' => array(), 'caption' => NULL, 'colgroups' => array(), 'sticky' => TRUE, 'empty' => ''),
+      'variables' => array('header' => NULL, 'rows' => NULL, 'attributes' => array(), 'caption' => NULL, 'colgroups' => array(), 'sticky' => TRUE, 'responsive' => TRUE, 'empty' => ''),
     ),
     'meter' => array(
       'variables' => array('display_value' => NULL, 'form' => NULL, 'high' => NULL, 'low' => NULL, 'max' => NULL, 'min' => NULL, 'optimum' => NULL, 'value' => NULL, 'percentage' => NULL, 'attributes' => array()),
diff --git a/core/misc/tabledrag.js b/core/misc/tabledrag.js
index 07c50d5..230f588 100644
--- a/core/misc/tabledrag.js
+++ b/core/misc/tabledrag.js
@@ -50,6 +50,7 @@ Drupal.tableDrag = function (table, tableSettings) {
   var $table = $(table);
 
   // Required object variables.
+  this.$table = $(table);
   this.table = table;
   this.tableSettings = tableSettings;
   this.dragObject = null; // Used to hold information about a current drag operation.
@@ -143,7 +144,8 @@ Drupal.tableDrag = function (table, tableSettings) {
  * 'Drupal.tableDrag.showWeight' localStorage value.
  */
 Drupal.tableDrag.prototype.initColumns = function () {
-  var $table = $(this.table), hidden, cell, columnIndex;
+  var $table = this.$table;
+  var hidden, cell, columnIndex;
   for (var group in this.tableSettings) {
     if (this.tableSettings.hasOwnProperty(group)) { // Find the first field in this group.
       for (var d in this.tableSettings[group]) {
diff --git a/core/misc/tableresponsive.js b/core/misc/tableresponsive.js
new file mode 100644
index 0000000..2bc507f
--- /dev/null
+++ b/core/misc/tableresponsive.js
@@ -0,0 +1,150 @@
+/**
+ * @file tableresponsive.js
+ *
+ * Behaviors to facilitate the presentation of tables across screens of any
+ * size.
+ */
+(function ($) {
+
+"use strict";
+
+/**
+ * Attach the tableResponsive function to Drupal.behaviors.
+ */
+Drupal.behaviors.tableResponsive = {
+  attach: function (context, settings) {
+    var $tables = $(context).find('table.responsive-enabled').once('tableresponsive');
+    if ($tables.length) {
+      for (var i = 0, il = $tables.length; i < il; i++) {
+        TableResponsive.tables.push(new TableResponsive($tables[i]));
+      }
+    }
+  }
+};
+
+/**
+ * The TableResponsive object optimizes table presentation for all screen sizes.
+ *
+ * A responsive table hides columns at small screen sizes, leaving the most
+ * important columns visible to the end user. Users should not be prevented from
+ * accessing all columns, however. This class adds a toggle to a table with
+ * hidden columns that exposes the columns. Exposing the columns will likely
+ * break layouts, but it provides the user with a means to access data, which
+ * is a guiding principle of responsive design.
+ */
+function TableResponsive (table) {
+  this.table = table;
+  this.$table = $(table);
+  this.showText = Drupal.t('Show all columns');
+  this.hideText = Drupal.t('Hide unimportant columns');
+  // Store a reference to the header elements of the table so that the DOM is
+  // traversed only once to find them.
+  this.$headers = this.$table.find('th');
+  // Build a link that will toggle the column visibility.
+
+  // Add a link before the table for users to show or hide weight columns.
+  this.$link = $('<a href="#" class="tableresponsive-toggle"></a>')
+    .attr({
+      'title': Drupal.t('Expose table cells that were hidden to make the table fit within a small screen.'),
+      'aria-disabled': 'false'
+    })
+    .on('click', $.proxy(this, 'eventhandlerToggleColumns'));
+
+  this.$table.before($('<div class="tableresponsive-toggle-columns"></div>').append(this.$link));
+
+  // Attach a resize handler to the window.
+  $(window)
+    .on('resize.tableresponsive', $.proxy(this, 'eventhandlerEvaluateColumnVisibility'))
+    .trigger('resize.tableresponsive');
+}
+
+/**
+ * Extend the TableResponsive function with a list of managed tables.
+ */
+$.extend(TableResponsive, {
+  tables: []
+});
+
+  /**
+   * Associates an action link with the table that will show hidden columns.
+   *
+   * Columns are assumed to be hidden if their header has the class priority-low
+   * or priority-medium.
+   */
+$.extend(TableResponsive.prototype, {
+  eventhandlerEvaluateColumnVisibility: function (e) {
+    var sticky = +this.$link.data('sticky');
+    var hiddenLength = this.$headers.filter('.priority-medium:hidden, .priority-low:hidden').length;
+    // If the table has hidden columns, associate an action link with the table
+    // to show the columns.
+    if (hiddenLength > 0) {
+      this.$link.show().text(this.showText).attr('aria-disabled', 'false');
+    }
+    // When the toggle is sticky, its presence is maintained because the user
+    // has interacted with it. This is necessary to keep the link visible if the
+    // user adjusts screen size and changes the visibilty of columns.
+    if (!sticky && hiddenLength === 0) {
+      this.$link.hide().text(this.hideText).attr('aria-disabled', 'true');
+    }
+  },
+  // Toggle the visibility of columns classed with either 'priority-low' or
+  // 'priority-medium'.
+  eventhandlerToggleColumns: function (e) {
+    e.preventDefault();
+    var self = this;
+    var $hiddenHeaders = this.$headers.filter('.priority-medium:hidden, .priority-low:hidden');
+    this.$revealedCells = this.$revealedCells || $();
+    // Reveal hidden columns.
+    if ($hiddenHeaders.length > 0) {
+      $hiddenHeaders.each(function (index, element) {
+        var $header = $(this);
+        var position = $header.prevAll('th').length;
+        self.$table.find('tbody tr').each(function () {
+          var $cells = $(this).find('td:eq(' + position + ')');
+          $cells.show();
+          // Keep track of the revealed cells, so they can be hidden later.
+          self.$revealedCells = $().add(self.$revealedCells).add($cells);
+        });
+        $header.show();
+        // Keep track of the revealed headers, so they can be hidden later.
+        self.$revealedCells = $().add(self.$revealedCells).add($header);
+      });
+      this.$link.text(this.hideText).data('sticky', 1);
+    }
+    // Hide revealed columns.
+    else {
+      this.$revealedCells.hide();
+      // Strip the 'display:none' declaration from the style attributes of
+      // the table cells that .hide() added.
+      this.$revealedCells.each(function (index, element) {
+        var $cell = $(this);
+        var properties = $cell.attr('style').split(';');
+        var newProps = [];
+        // The hide method adds display none to the element. The element should
+        // be returned to the same state it was in before the columns were
+        // revealed, so it is necessary to remove the display none
+        // value from the style attribute.
+        var match =  /^display\s*\:\s*none$/;
+        for (var i = 0; i < properties.length; i++) {
+          var prop = properties[i];
+          prop.trim();
+          // Find the display:none property and remove it.
+          var isDisplayNone = match.exec(prop);
+          if (isDisplayNone) {
+            continue;
+          }
+          newProps.push(prop);
+        }
+        // Return the rest of the style attribute values to the element.
+        $cell.attr('style', newProps.join(';'));
+      });
+      this.$link.text(this.showText).data('sticky', 0);
+      // Refresh the toggle link.
+      $(window).trigger('resize.tableresponsive');
+    }
+  }
+});
+// Make the TableResponsive object available in the Drupal namespace.
+Drupal.TableResponsive = TableResponsive;
+
+})(jQuery);
diff --git a/core/modules/comment/comment.admin.inc b/core/modules/comment/comment.admin.inc
index 6210cc5..4a3220f 100644
--- a/core/modules/comment/comment.admin.inc
+++ b/core/modules/comment/comment.admin.inc
@@ -73,9 +73,9 @@ function comment_admin_overview($form, &$form_state, $arg) {
   $status = ($arg == 'approval') ? COMMENT_NOT_PUBLISHED : COMMENT_PUBLISHED;
   $header = array(
     'subject' => array('data' => t('Subject'), 'field' => 'subject'),
-    'author' => array('data' => t('Author'), 'field' => 'name'),
-    'posted_in' => array('data' => t('Posted in'), 'field' => 'node_title'),
-    'changed' => array('data' => t('Updated'), 'field' => 'c.changed', 'sort' => 'desc'),
+    'author' => array('data' => t('Author'), 'field' => 'name', 'class' => array(RESPONSIVE_PRIORITY_MEDIUM)),
+    'posted_in' => array('data' => t('Posted in'), 'field' => 'node_title', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
+    'changed' => array('data' => t('Updated'), 'field' => 'c.changed', 'sort' => 'desc', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
     'operations' => array('data' => t('Operations')),
   );
 
diff --git a/core/modules/dblog/dblog.admin.inc b/core/modules/dblog/dblog.admin.inc
index 15f2354..012d01e 100644
--- a/core/modules/dblog/dblog.admin.inc
+++ b/core/modules/dblog/dblog.admin.inc
@@ -33,11 +33,11 @@ function dblog_overview() {
 
   $header = array(
     '', // Icon column.
-    array('data' => t('Type'), 'field' => 'w.type'),
-    array('data' => t('Date'), 'field' => 'w.wid', 'sort' => 'desc'),
+    array('data' => t('Type'), 'field' => 'w.type', 'class' => array(RESPONSIVE_PRIORITY_MEDIUM)),
+    array('data' => t('Date'), 'field' => 'w.wid', 'sort' => 'desc', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
     t('Message'),
-    array('data' => t('User'), 'field' => 'u.name'),
-    array('data' => t('Operations')),
+    array('data' => t('User'), 'field' => 'u.name', 'class' => array(RESPONSIVE_PRIORITY_MEDIUM)),
+    array('data' => t('Operations'), 'class' => array(RESPONSIVE_PRIORITY_LOW)),
   );
 
   $query = db_select('watchdog', 'w')
diff --git a/core/modules/field_ui/field_ui.admin.inc b/core/modules/field_ui/field_ui.admin.inc
index bb99234..4de8017 100644
--- a/core/modules/field_ui/field_ui.admin.inc
+++ b/core/modules/field_ui/field_ui.admin.inc
@@ -17,7 +17,11 @@ function field_ui_fields_list() {
 
   $modules = system_rebuild_module_data();
 
-  $header = array(t('Field name'), t('Field type'), t('Used in'));
+  $header = array(
+    t('Field name'),
+    array('data' => t('Field type'), 'class' => array(RESPONSIVE_PRIORITY_MEDIUM)),
+    t('Used in'),
+  );
   $rows = array();
   foreach ($instances as $entity_type => $type_bundles) {
     foreach ($type_bundles as $bundle => $bundle_instances) {
diff --git a/core/modules/node/node.admin.inc b/core/modules/node/node.admin.inc
index 5cbe4ef..313dabe 100644
--- a/core/modules/node/node.admin.inc
+++ b/core/modules/node/node.admin.inc
@@ -436,14 +436,32 @@ function node_admin_nodes() {
 
   // Build the sortable table header.
   $header = array(
-    'title' => array('data' => t('Title'), 'field' => 'n.title'),
-    'type' => array('data' => t('Content type'), 'field' => 'n.type'),
-    'author' => t('Author'),
-    'status' => array('data' => t('Status'), 'field' => 'n.status'),
-    'changed' => array('data' => t('Updated'), 'field' => 'n.changed', 'sort' => 'desc')
+    'title' => array(
+      'data' => t('Title'),
+      'field' => 'n.title',
+    ),
+    'type' => array(
+      'data' => t('Content type'),
+      'field' => 'n.type',
+      'class' => array(RESPONSIVE_PRIORITY_MEDIUM),
+    ),
+    'author' => array(
+      'data' => t('Author'),
+      'class' => array(RESPONSIVE_PRIORITY_LOW),
+    ),
+    'status' => array(
+      'data' => t('Status'),
+      'field' => 'n.status',
+    ),
+    'changed' => array(
+      'data' => t('Updated'),
+      'field' => 'n.changed',
+      'sort' => 'desc',
+      'class' => array(RESPONSIVE_PRIORITY_LOW)
+    ,)
   );
   if ($multilingual) {
-    $header['language_name'] = array('data' => t('Language'), 'field' => 'n.langcode');
+    $header['language_name'] = array('data' => t('Language'), 'field' => 'n.langcode', 'class' => array(RESPONSIVE_PRIORITY_LOW));
   }
   $header['operations'] = array('data' => t('Operations'));
 
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index 2bf4343..6440210 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -921,8 +921,8 @@ function system_modules($form, $form_state = array()) {
       '#header' => array(
         array('data' => t('Enabled'), 'class' => array('checkbox')),
         t('Name'),
-        t('Version'),
-        t('Description'),
+        array('data' => t('Version'), 'class' => array(RESPONSIVE_PRIORITY_MEDIUM)),
+        array('data' => t('Description'), 'class' => array(RESPONSIVE_PRIORITY_LOW)),
         array('data' => t('Operations'), 'colspan' => 3),
       ),
       // Ensure that the "Core" package fieldset comes first.
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index e5dc768..9d5a67a 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -1313,6 +1313,20 @@ function system_library_info() {
     ),
   );
 
+  // Drupal's responsive table API.
+  $libraries['drupal.tableresponsive'] = array(
+    'title' => 'Drupal responsive table API',
+    'version' => VERSION,
+    'js' => array(
+      'core/misc/tableresponsive.js' => array('group' => JS_LIBRARY),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'drupal'),
+      array('system', 'jquery.once'),
+    ),
+  );
+
   // Drupal's collapsible fieldset.
   $libraries['drupal.collapse'] = array(
     'title' => 'Drupal collapsible fieldset',
diff --git a/core/modules/user/user.admin.inc b/core/modules/user/user.admin.inc
index f9b1c23..180b9f1 100644
--- a/core/modules/user/user.admin.inc
+++ b/core/modules/user/user.admin.inc
@@ -145,10 +145,10 @@ function user_admin_account() {
 
   $header = array(
     'username' => array('data' => t('Username'), 'field' => 'u.name'),
-    'status' => array('data' => t('Status'), 'field' => 'u.status'),
-    'roles' => array('data' => t('Roles')),
-    'member_for' => array('data' => t('Member for'), 'field' => 'u.created', 'sort' => 'desc'),
-    'access' => array('data' => t('Last access'), 'field' => 'u.access'),
+    'status' => array('data' => t('Status'), 'field' => 'u.status', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
+    'roles' => array('data' => t('Roles'), 'class' => array(RESPONSIVE_PRIORITY_LOW)),
+    'member_for' => array('data' => t('Member for'), 'field' => 'u.created', 'sort' => 'desc', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
+    'access' => array('data' => t('Last access'), 'field' => 'u.access', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
     'operations' => array('data' => t('Operations')),
   );
 
diff --git a/core/themes/bartik/css/style.css b/core/themes/bartik/css/style.css
index 7532a4a..db25614 100644
--- a/core/themes/bartik/css/style.css
+++ b/core/themes/bartik/css/style.css
@@ -1708,3 +1708,21 @@ div.admin-panel .description {
     padding-bottom: 2em;
   }
 }
+
+/**
+ * Responsive tables
+ */
+@media screen and (max-width:28.125em) { /* 450px */
+  th.helpful,
+  td.helpful,
+  th.priority-medium,
+  td.priority-medium {
+    display: none;
+  }
+}
+@media screen and (max-width:45em) { /* 720px */
+  th.helpful,
+  td.helpful {
+    display: none;
+  }
+}
diff --git a/core/themes/seven/style-rtl.css b/core/themes/seven/style-rtl.css
index fd9f2ee..2f034b8 100644
--- a/core/themes/seven/style-rtl.css
+++ b/core/themes/seven/style-rtl.css
@@ -71,11 +71,6 @@ ul.primary {
 /**
  * Page layout.
  */
-#page {
-  padding: 20px 0 40px 0;
-  margin-left: 40px;
-  margin-right: 40px;
-}
 ul.links li,
 ul.inline li {
   padding-left: 1em;
diff --git a/core/themes/seven/style.css b/core/themes/seven/style.css
index 86f3fc5..b4a9c0b 100644
--- a/core/themes/seven/style.css
+++ b/core/themes/seven/style.css
@@ -314,12 +314,24 @@ ul.secondary li.active a.active {
  * Page layout.
  */
 #page {
-  padding: 20px 0 40px 0; /* LTR */
-  margin-right: 40px; /* LTR */
-  margin-left: 40px; /* LTR */
   background: #fff;
-  position: relative;
   color: #333;
+  margin-left: 0.8125em;
+  margin-right: 0.8125em;
+  padding: 20px 0 40px 0;
+  position: relative;
+}
+@media screen and (min-width:28.125em) { /* 450px */
+  #page {
+    margin-left: 1.25em;
+    margin-right: 1.25em;
+  }
+}
+@media screen and (min-width:45em) { /* 720px */
+  #page {
+    margin-left: 2.5em;
+    margin-right: 2.5em;
+  }
 }
 ul.links li,
 ul.inline li {
@@ -477,6 +489,23 @@ tr td:last-child {
 
 
 /**
+ * Responsive tables
+ */
+@media screen and (max-width:28.125em) { /* 450px */
+  th.priority-low,
+  td.priority-low,
+  th.priority-medium,
+  td.priority-medium {
+    display: none;
+  }
+}
+@media screen and (max-width:45em) { /* 720px */
+  th.priority-low,
+  td.priority-low {
+    display: none;
+  }
+}
+/**
  * Fieldsets.
  *
  * Fieldset legends are displayed like containers in Seven. However, several
diff --git a/core/themes/stark/css/layout.css b/core/themes/stark/css/layout.css
index a31a774..2e09a63 100644
--- a/core/themes/stark/css/layout.css
+++ b/core/themes/stark/css/layout.css
@@ -93,3 +93,21 @@ img {
     width: 20%;
   }
 }
+
+/**
+ * Responsive tables
+ */
+@media screen and (max-width:28.125em) { /* 450px */
+  th.priority-low,
+  td.priority-low,
+  th.priority-medium,
+  td.priority-medium {
+    display: none;
+  }
+}
+@media screen and (max-width:45em) { /* 720px */
+  th.priority-low,
+  td.priority-low {
+    display: none;
+  }
+}
-- 
1.7.10.4

