Thomas Bruederli
2014-06-04 72afe3153cfaf0f8aaa0a4db115fea62959ff6d1
Use <th> tags for table headers as suggested by the WCAG 2.0 Guidelines
8 files modified
340 ■■■■ changed files
program/js/app.js 2 ●●● patch | view | raw | blame | history
program/js/list.js 6 ●●●● patch | view | raw | blame | history
program/lib/Roundcube/html.php 15 ●●●● patch | view | raw | blame | history
program/steps/mail/func.inc 4 ●●●● patch | view | raw | blame | history
skins/classic/common.css 2 ●●●●● patch | view | raw | blame | history
skins/classic/mail.css 152 ●●●● patch | view | raw | blame | history
skins/larry/mail.css 137 ●●●● patch | view | raw | blame | history
skins/larry/styles.css 22 ●●●● patch | view | raw | blame | history
program/js/app.js
@@ -6712,7 +6712,7 @@
        tr = document.createElement('tr');
        for (c=0, len=repl.length; c < len; c++) {
          cell = document.createElement('td');
          cell = document.createElement('th');
          cell.innerHTML = repl[c].html || '';
          if (repl[c].id) cell.id = repl[c].id;
          if (repl[c].className) cell.className = repl[c].className;
program/js/list.js
@@ -199,7 +199,7 @@
    if (this.fixed_header) {  // copy (modified) fixed header back to the actual table
      $(this.list.tHead).replaceWith($(this.fixed_header).find('thead').clone());
      $(this.list.tHead).find('tr td').attr('style', '').find('a').attr('tabindex', '-1');  // remove fixed widths
      $(this.list.tHead).find('th,td').attr('style', '').find('a').attr('tabindex', '-1');  // remove fixed widths
    }
    else if (!bw.touch && this.list.className.indexOf('fixedheader') >= 0) {
      this.init_fixed_header();
@@ -855,9 +855,9 @@
get_first_row: function()
{
  if (this.rowcount) {
    var i, len, uid, rows = this.tbody.childNodes;
    var i, uid, rows = this.tbody.childNodes;
    for (i=0, len=rows.length-1; i<len; i++)
    for (i=0; i<rows.length; i++)
      if (rows[i].id && (uid = this.get_row_uid(rows[i])))
        return uid;
  }
program/lib/Roundcube/html.php
@@ -837,7 +837,7 @@
        if (!empty($this->header)) {
            $rowcontent = '';
            foreach ($this->header as $c => $col) {
                $rowcontent .= self::tag($this->_col_tagname(), $col->attrib, $col->content);
                $rowcontent .= self::tag($this->_head_tagname(), $col->attrib, $col->content);
            }
            $thead = $this->tagname == 'table' ? self::tag('thead', null, self::tag('tr', null, $rowcontent, parent::$common_attrib)) :
                self::tag($this->_row_tagname(), array('class' => 'thead'), $rowcontent, parent::$common_attrib);
@@ -890,7 +890,16 @@
    private function _row_tagname()
    {
        static $row_tagnames = array('table' => 'tr', 'ul' => 'li', '*' => 'div');
        return $row_tagnames[$this->tagname] ? $row_tagnames[$this->tagname] : $row_tagnames['*'];
        return $row_tagnames[$this->tagname] ?: $row_tagnames['*'];
    }
    /**
     * Getter for the corresponding tag name for table row elements
     */
    private function _head_tagname()
    {
        static $head_tagnames = array('table' => 'th', '*' => 'span');
        return $head_tagnames[$this->tagname] ?: $head_tagnames['*'];
    }
    /**
@@ -899,7 +908,7 @@
    private function _col_tagname()
    {
        static $col_tagnames = array('table' => 'td', '*' => 'span');
        return $col_tagnames[$this->tagname] ? $col_tagnames[$this->tagname] : $col_tagnames['*'];
        return $col_tagnames[$this->tagname] ?: $col_tagnames['*'];
    }
}
program/steps/mail/func.inc
@@ -509,8 +509,8 @@
        $a_sort_cols = array('subject', 'date', 'from', 'to', 'fromto', 'size', 'cc');
    if (!empty($attrib['optionsmenuicon'])) {
        $onclick = 'return ' . rcmail_output::JS_OBJECT_NAME . ".command('menu-open', 'messagelistmenu', null, event)";
        $inner   = 'v';
        $onclick = 'return ' . rcmail_output::JS_OBJECT_NAME . ".command('menu-open', 'messagelistmenu', this, event)";
        $inner   = $RCMAIL->gettext('listoptions');
        if (is_string($attrib['optionsmenuicon']) && $attrib['optionsmenuicon'] != 'true') {
            $inner = html::img(array('src' => $skin_path . $attrib['optionsmenuicon'], 'alt' => $RCMAIL->gettext('listoptions')));
        }
skins/classic/common.css
@@ -607,6 +607,7 @@
/***** common table settings ******/
table.records-table thead tr th,
table.records-table thead tr td
{
  height: 20px;
@@ -617,6 +618,7 @@
  background: url(images/listheader.gif) top left repeat-x #CCC;
  font-size: 11px;
  font-weight: bold;
  text-align: left;
}
table.records-table tbody tr td
skins/classic/mail.css
@@ -719,6 +719,7 @@
  z-index: 2;
}
.messagelist thead tr th,
.messagelist thead tr td
{
  height: 20px;
@@ -729,25 +730,26 @@
  background: url(images/listheader.gif) top left repeat-x #CCC;
  font-size: 11px;
  font-weight: bold;
  text-align: left;
}
.messagelist thead tr td.sortedASC,
.messagelist thead tr td.sortedDESC
.messagelist thead tr > .sortedASC,
.messagelist thead tr > .sortedDESC
{
  background-position: 0 -26px;
}
.messagelist thead tr td.sortedASC a
.messagelist thead tr > .sortedASC a
{
  background: url(images/icons/sort.gif) right 0 no-repeat;
}
.messagelist thead tr td.sortedDESC a
.messagelist thead tr > .sortedDESC a
{
  background: url(images/icons/sort.gif) right -14px no-repeat;
}
.messagelist thead tr td a
.messagelist thead tr a
{
  display: block;
  width: auto !important;
@@ -756,18 +758,19 @@
  text-decoration: none;
}
.messagelist thead tr td.size.sortedASC a,
.messagelist thead tr td.size.sortedDESC a
.messagelist thead tr > .size.sortedASC a,
.messagelist thead tr > .size.sortedDESC a
{
  padding-right: 18px;
}
.messagelist thead tr td.subject
.messagelist thead tr > .subject
{
  padding-left: 18px;
  width: 99%;
}
.messagelist tbody tr th,
.messagelist tbody tr td
{
  height: 20px;
@@ -804,10 +807,10 @@
  cursor: pointer;
}
.messagelist tr td.flag span,
.messagelist tr td.status span,
.messagelist tr td.attachment span,
.messagelist tr td.priority span
.messagelist tr > .flag span,
.messagelist tr > .status span,
.messagelist tr > .attachment span,
.messagelist tr > .priority span
{
  display: block;
  width: 15px;
@@ -815,29 +818,29 @@
.messagelist tr td div.collapsed,
.messagelist tr td div.expanded,
.messagelist tr td.threads .listmenu,
.messagelist tr td.attachment span.attachment,
.messagelist tr td.attachment span.report,
.messagelist tr td.priority span.priority,
.messagelist tr td.priority span.prio1,
.messagelist tr td.priority span.prio2,
.messagelist tr td.priority span.prio3,
.messagelist tr td.priority span.prio4,
.messagelist tr td.priority span.prio5,
.messagelist tr td.flag span.flagged,
.messagelist tr td.flag span.unflagged,
.messagelist tr td.flag span.unflagged:hover,
.messagelist tr td.status span.status,
.messagelist tr td.status span.msgicon,
.messagelist tr td.status span.deleted,
.messagelist tr td.status span.unread,
.messagelist tr td.status span.unreadchildren,
.messagelist tr td.subject span.msgicon,
.messagelist tr td.subject span.deleted,
.messagelist tr td.subject span.unread,
.messagelist tr td.subject span.replied,
.messagelist tr td.subject span.forwarded,
.messagelist tr td.subject span.unreadchildren
.messagelist tr > .threads .listmenu,
.messagelist tr > .attachment span.attachment,
.messagelist tr > .attachment span.report,
.messagelist tr > .priority span.priority,
.messagelist tr > .priority span.prio1,
.messagelist tr > .priority span.prio2,
.messagelist tr > .priority span.prio3,
.messagelist tr > .priority span.prio4,
.messagelist tr > .priority span.prio5,
.messagelist tr > .flag span.flagged,
.messagelist tr > .flag span.unflagged,
.messagelist tr > .flag span.unflagged:hover,
.messagelist tr > .status span.status,
.messagelist tr > .status span.msgicon,
.messagelist tr > .status span.deleted,
.messagelist tr > .status span.unread,
.messagelist tr > .status span.unreadchildren,
.messagelist tr > .subject span.msgicon,
.messagelist tr > .subject span.deleted,
.messagelist tr > .subject span.unread,
.messagelist tr > .subject span.replied,
.messagelist tr > .subject span.forwarded,
.messagelist tr > .subject span.unreadchildren
{
  display: inline-block;
  vertical-align: middle;
@@ -846,99 +849,99 @@
  background: url(images/messageicons.png) center no-repeat;
}
.messagelist tr td.attachment span.attachment
.messagelist tr > .attachment span.attachment
{
  background-position: 0 -170px;
}
.messagelist tr td.attachment span.report
.messagelist tr > .attachment span.report
{
  background-position: 0 -255px;
}
.messagelist tr td.priority span.priority
.messagelist tr > .priority span.priority
{
  background-position: 0 -309px;
}
.messagelist tr td.priority span.prio5
.messagelist tr > .priority span.prio5
{
  background-position: 0 -358px;
}
.messagelist tr td.priority span.prio4
.messagelist tr > .priority span.prio4
{
  background-position: 0 -340px;
}
.messagelist tr td.priority span.prio3
.messagelist tr > .priority span.prio3
{
  background-position: 0 -324px;
}
.messagelist tr td.priority span.prio2
.messagelist tr > .priority span.prio2
{
  background-position: 0 -309px;
}
.messagelist tr td.priority span.prio1
.messagelist tr > .priority span.prio1
{
  background-position: 0 -290px;
}
.messagelist tr td.flag span.flagged
.messagelist tr > .flag span.flagged
{
  background-position: 0 -153px;
}
.messagelist tr td.flag span.unflagged:hover
.messagelist tr > .flag span.unflagged:hover
{
  background-position: 0 -136px;
}
.messagelist tr td.subject span.msgicon,
.messagelist tr td.subject span.unreadchildren
.messagelist tr > .subject span.msgicon,
.messagelist tr > .subject span.unreadchildren
{
  background-position: 0 -51px;
  margin: 0 2px;
}
.messagelist tr td.subject span.replied
.messagelist tr > .subject span.replied
{
  background-position: 0 -85px;
}
.messagelist tr td.subject span.forwarded
.messagelist tr > .subject span.forwarded
{
  background-position: 0 -68px;
}
.messagelist tr td.subject span.replied.forwarded
.messagelist tr > .subject span.replied.forwarded
{
  background-position: 0 -102px;
}
.messagelist tr td.status span.msgicon,
.messagelist tr td.flag span.unflagged,
.messagelist tr td.status span.unreadchildren
.messagelist tr > .status span.msgicon,
.messagelist tr > .flag span.unflagged,
.messagelist tr > .status span.unreadchildren
{
  background-position: 0 17px; /* no icon */
}
.messagelist tr td.status span.msgicon:hover
.messagelist tr > .status span.msgicon:hover
{
  background-position: 0 -272px;
}
.messagelist tr td.status span.deleted,
.messagelist tr td.subject span.deleted
.messagelist tr > .status span.deleted,
.messagelist tr > .subject span.deleted
{
  background-position: 0 -187px;
}
.messagelist tr td.status span.status,
.messagelist tr td.status span.unread,
.messagelist tr td.subject span.unread
.messagelist tr > .status span.status,
.messagelist tr > .status span.unread,
.messagelist tr > .subject span.unread
{
  background-position: 0 -119px;
}
@@ -955,7 +958,7 @@
  cursor: pointer;
}
.messagelist tr td.threads .listmenu
.messagelist tr > .threads .listmenu
{
  background-position: 0 -238px;
  cursor: pointer;
@@ -980,45 +983,45 @@
  text-decoration: underline;
}
.messagelist tr td.attachment,
.messagelist tr td.threads,
.messagelist tr td.status,
.messagelist tr td.flag,
.messagelist tr td.priority
.messagelist tr > .attachment,
.messagelist tr > .threads,
.messagelist tr > .status,
.messagelist tr > .flag,
.messagelist tr > .priority
{
  width: 17px;
  padding: 0 0 0 2px;
}
.messagelist tr td.size
.messagelist tr > .size
{
  width: 60px;
  text-align: right;
  padding: 0 2px;
}
.messagelist tr td.fromto,
.messagelist tr td.from,
.messagelist tr td.to,
.messagelist tr td.cc,
.messagelist tr td.replyto
.messagelist tr > .fromto,
.messagelist tr > .from,
.messagelist tr > .to,
.messagelist tr > .cc,
.messagelist tr > .replyto
{
  width: 180px;
  padding: 0 2px;
}
.messagelist tr td.date
.messagelist tr > .date
{
  width: 135px;
  padding: 0 2px;
}
.messagelist tr td.folder
.messagelist tr > .folder
{
  width: 135px;
}
.messagelist tr td.hidden
.messagelist tr > .hidden
{
  display: none;
}
@@ -1041,6 +1044,7 @@
}
/* This padding-left minus the focused padding left should be half of the focused border-left */
.messagelist thead tr th:first-child,
.messagelist thead tr td:first-child,
.messagelist tbody tr td:first-child {
    border-left: 0;
skins/larry/mail.css
@@ -477,66 +477,66 @@
    z-index: 2;
}
.messagelist thead td:first-child {
.messagelist thead th:first-child {
    border-radius: 4px 0 0 0; /* for Chrome */
}
.messagelist tr td.attachment,
.messagelist tr td.threads,
.messagelist tr td.status,
.messagelist tr td.flag,
.messagelist tr td.priority {
.messagelist tr > .attachment,
.messagelist tr > .threads,
.messagelist tr > .status,
.messagelist tr > .flag,
.messagelist tr > .priority {
    width: 20px;
    padding: 2px 3px;
}
.webkit .messagelist tr td.attachment,
.webkit .messagelist tr td.threads,
.webkit .messagelist tr td.status,
.webkit .messagelist tr td.flag,
.webkit .messagelist tr td.priority {
.webkit .messagelist tr > .attachment,
.webkit .messagelist tr > .threads,
.webkit .messagelist tr > .status,
.webkit .messagelist tr > .flag,
.webkit .messagelist tr > .priority {
    width: 26px;
}
.messagelist tr td.threads {
.messagelist tr > .threads {
    width: 26px;
}
.webkit .messagelist tr td.threads {
.webkit .messagelist tr > .threads {
    width: 30px;
}
.messagelist tr td.threads,
.messagelist tr td.threads + td {
.messagelist tr > .threads,
.messagelist tr > .threads + td {
    border-left: 0;
}
.messagelist tr td.size {
.messagelist tr > .size {
    width: 60px;
    text-align: right;
}
.messagelist thead tr td.size {
.messagelist thead tr th.size {
    text-align: left;
}
.messagelist tr td.fromto,
.messagelist tr td.from,
.messagelist tr td.to,
.messagelist tr td.cc,
.messagelist tr td.replyto {
.messagelist tr > .fromto,
.messagelist tr > .from,
.messagelist tr > .to,
.messagelist tr > .cc,
.messagelist tr > .replyto {
    width: 200px;
}
.messagelist tr td.date {
.messagelist tr > .date {
    width: 155px;
}
.messagelist tr td.folder {
.messagelist tr > .folder {
    width: 135px;
}
.messagelist tr td.hidden {
.messagelist tr > .hidden {
    display: none;
}
@@ -553,13 +553,14 @@
/*    background-color: #fff; */
}
.messagelist tr.flagged th,
.messagelist tr.flagged td,
.messagelist tr.flagged td a {
    color: #f30;
}
.messagelist thead tr td.sortedASC a,
.messagelist thead tr td.sortedDESC a {
.messagelist thead tr th.sortedASC a,
.messagelist thead tr th.sortedDESC a {
    color: #004458;
    text-decoration: underline;
    background-image: url(images/listicons.png);
@@ -567,7 +568,7 @@
    background-position: right -912px;
}
.messagelist thead tr td.sortedASC a {
.messagelist thead tr th.sortedASC a {
    background-position: right -944px;
}
@@ -589,39 +590,39 @@
    cursor: pointer;
}
.messagelist tr td.flag span,
.messagelist tr td.status span,
.messagelist tr td.attachment span,
.messagelist tr td.priority span {
.messagelist tr > .flag span,
.messagelist tr > .status span,
.messagelist tr > .attachment span,
.messagelist tr > .priority span {
    display: block;
    width: 20px;
}
.messagelist tr td div.collapsed,
.messagelist tr td div.expanded,
.messagelist tr td.threads .listmenu,
.messagelist tr td.attachment span.attachment,
.messagelist tr td.attachment span.report,
.messagelist tr td.priority span.priority,
.messagelist tr td.priority span.prio1,
.messagelist tr td.priority span.prio2,
.messagelist tr td.priority span.prio3,
.messagelist tr td.priority span.prio4,
.messagelist tr td.priority span.prio5,
.messagelist tr td.flag span.flagged,
.messagelist tr td.flag span.unflagged,
.messagelist tr td.flag span.unflagged:hover,
.messagelist tr td.status span.status,
.messagelist tr td.status span.msgicon,
.messagelist tr td.status span.deleted,
.messagelist tr td.status span.unread,
.messagelist tr td.status span.unreadchildren,
.messagelist tr td.subject span.msgicon,
.messagelist tr td.subject span.deleted,
.messagelist tr td.subject span.unread,
.messagelist tr td.subject span.replied,
.messagelist tr td.subject span.forwarded,
.messagelist tr td.subject span.unreadchildren {
.messagelist tr > .threads .listmenu,
.messagelist tr > .attachment span.attachment,
.messagelist tr > .attachment span.report,
.messagelist tr > .priority span.priority,
.messagelist tr > .priority span.prio1,
.messagelist tr > .priority span.prio2,
.messagelist tr > .priority span.prio3,
.messagelist tr > .priority span.prio4,
.messagelist tr > .priority span.prio5,
.messagelist tr > .flag span.flagged,
.messagelist tr > .flag span.unflagged,
.messagelist tr > .flag span.unflagged:hover,
.messagelist tr > .status span.status,
.messagelist tr > .status span.msgicon,
.messagelist tr > .status span.deleted,
.messagelist tr > .status span.unread,
.messagelist tr > .status span.unreadchildren,
.messagelist tr > .subject span.msgicon,
.messagelist tr > .subject span.deleted,
.messagelist tr > .subject span.unread,
.messagelist tr > .subject span.replied,
.messagelist tr > .subject span.forwarded,
.messagelist tr > .subject span.unreadchildren {
    display: inline-block;
    vertical-align: middle;
    height: 18px;
@@ -634,7 +635,7 @@
    background-position: 0 -996px;
}
.messagelist thead tr td.attachment span.attachment {
.messagelist thead tr th.attachment span.attachment {
    background-position: -24px -997px;
}
@@ -642,7 +643,7 @@
    background-position: -24px -1116px;
}
.messagelist thead tr td.priority span.priority {
.messagelist thead tr th.priority span.priority {
    background-position: -24px -1845px;
}
@@ -666,15 +667,15 @@
    background-position: 0 -1036px;
}
.messagelist thead tr td.flag span.flagged {
.messagelist thead tr th.flag span.flagged {
    background-position: -22px -1036px;
}
.messagelist tr td.status span.msgicon:hover {
    background-position: -23px -1056px;
.messagelist tr:hover td.status span.msgicon {
    background-position: -23px -1057px;
}
.messagelist tr td.flag span.unflagged:hover {
.messagelist tr:hover td.flag span.unflagged {
    background-position: -23px -1076px;
}
@@ -717,10 +718,10 @@
.messagelist tr td.status span.unread,
.messagelist tr td.subject span.unread,
.messagelist tr td.status span.unread:hover {
    background-position: 0 -1016px;
    background-position: 0 -1017px;
}
.messagelist thead tr td.status span.status {
.messagelist thead tr th.status span.status {
    background-position: -23px -1017px;
}
@@ -734,10 +735,10 @@
    cursor: pointer;
}
.messagelist tr td.threads .listmenu {
    background-position: 6px -972px;
.messagelist tr th.threads .listmenu {
    background-position: 4px -972px;
    cursor: pointer;
    width: 26px;
    width: 24px;
    height: 21px;
    overflow: hidden;
    text-indent: -5000px;
@@ -745,12 +746,12 @@
    padding: 3px  5px  2px  6px;
}
.messagelist tr td.threads .listmenu:focus {
.messagelist tr th.threads .listmenu:focus {
    background-color: rgba(73,180,210,0.7);
    outline: none;
}
.messagelist thead tr td.subject,
.messagelist thead tr th.subject,
.messagelist tbody tr td.subject {
    width: 99%;
    white-space: nowrap;
skins/larry/styles.css
@@ -1131,6 +1131,7 @@
}
.boxtitle,
.uibox .listing thead th,
.uibox .listing thead td {
    font-size: 12px;
    font-weight: bold;
@@ -1142,12 +1143,14 @@
    white-space: nowrap;
}
.uibox .listing thead th,
.uibox .listing thead td {
    padding-bottom: 8px;
    height: auto;
}
.uibox .boxtitle,
.uibox .listing thead th,
.uibox .listing thead td {
    background: #b0ccd7;
    color: #004458;
@@ -1212,11 +1215,7 @@
/* because of border-collapse, we make the left border twice what we want it to be - half will be hidden to the left */
.listing.focus tbody tr.focused > td:first-child {
    border-left: 2px solid #b0ccd7;
}
.listing.focus tbody tr.selected.focused > td:first-child {
    border-left-color: #9ec2d0;
    border-left: 2px solid #739da8;
}
.listbox .listitem.selected,
@@ -1511,6 +1510,7 @@
    border: 0;
}
.records-table thead th,
.records-table thead td {
    color: #69939e;
    font-size: 11px;
@@ -1525,13 +1525,17 @@
    padding: 8px 7px;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: left;
}
.records-table.sortheader thead th,
.records-table.sortheader thead td {
    padding: 0;
}
.records-table thead th a,
.records-table thead td a,
.records-table thead th span,
.records-table thead td span {
    display: block;
    padding: 7px 7px;
@@ -1541,6 +1545,7 @@
    text-overflow: ellipsis;
}
.records-table thead th a:focus,
.records-table thead td a:focus {
    color: #fff;
    background-color: rgba(73,180,210,0.7);
@@ -1561,6 +1566,7 @@
}
/* This padding-left minus the focused padding left should be half of the focused border-left */
.records-table thead tr th:first-child,
.records-table thead tr td:first-child,
.records-table tbody tr td:first-child {
    border-left: 2px solid transparent;
@@ -1569,11 +1575,7 @@
/* because of border-collapse, we make the left border twice what we want it to be - half will be hidden to the left */
.records-table.focus tbody tr.focused > td:first-child {
    border-left: 2px solid #b0ccd7;
}
.records-table.focus tbody tr.selected.focused > td:first-child {
    border-left-color: #49b3d2;
    border-left: 2px solid #49b3d2;
}
.records-table tr.selected td {