From 99cdca46b7bcc46fe6affd9e9f9f60a546b2e5b8 Mon Sep 17 00:00:00 2001
From: Thomas Bruederli <thomas@roundcube.net>
Date: Thu, 05 Jun 2014 03:18:07 -0400
Subject: [PATCH] Merge branch 'dev-accessibility'

---
 program/js/treelist.js                          |  114 ++
 program/js/common.js                            |   24 
 program/steps/mail/compose.inc                  |    5 
 skins/larry/templates/messagepart.html          |   14 
 program/steps/mail/show.inc                     |    7 
 skins/larry/templates/identities.html           |   12 
 program/lib/Roundcube/html.php                  |   21 
 skins/larry/addressbook.css                     |   51 
 skins/larry/templates/messagepreview.html       |   68 
 skins/classic/mail.css                          |  169 +-
 skins/classic/addressbook.css                   |    5 
 program/steps/settings/func.inc                 |    2 
 program/steps/addressbook/show.inc              |    1 
 skins/larry/includes/header.html                |   18 
 skins/larry/includes/settingstabs.html          |    8 
 skins/larry/templates/compose.html              |  126 +-
 program/include/rcmail_output_html.php          |   33 
 skins/larry/templates/message.html              |   46 
 skins/larry/templates/messageerror.html         |   14 
 skins/larry/templates/importcontacts.html       |    2 
 program/js/app.js                               |  597 +++++++++--
 skins/larry/mail.css                            |  208 ++-
 skins/larry/includes/mailtoolbar.html           |   58 
 skins/larry/includes/footer.html                |    1 
 skins/classic/common.css                        |    5 
 skins/larry/templates/folders.html              |   25 
 program/steps/mail/list_contacts.inc            |    2 
 skins/larry/templates/mail.html                 |  148 +-
 program/steps/settings/edit_folder.inc          |    3 
 plugins/zipdownload/zipdownload.php             |    8 
 program/steps/mail/func.inc                     |   31 
 program/js/list.js                              |  171 ++
 program/steps/addressbook/edit.inc              |    3 
 skins/larry/templates/addressbook.html          |  120 +-
 skins/larry/settings.css                        |  101 +-
 program/steps/addressbook/func.inc              |   14 
 skins/larry/templates/contactedit.html          |    5 
 skins/larry/templates/responses.html            |   12 
 skins/larry/templates/settings.html             |   11 
 plugins/zipdownload/zipdownload.js              |   25 
 program/steps/settings/responses.inc            |    2 
 program/include/rcmail.php                      |    9 
 program/localization/en_US/labels.inc           |   57 +
 plugins/managesieve/skins/larry/managesieve.css |    8 
 program/steps/mail/search_contacts.inc          |    2 
 skins/larry/styles.css                          |  206 +++-
 skins/larry/templates/login.html                |   10 
 plugins/legacy_browser/skins/larry/ie7hacks.css |    1 
 skins/larry/ui.js                               |  340 +++---
 49 files changed, 1,891 insertions(+), 1,032 deletions(-)

diff --git a/plugins/legacy_browser/skins/larry/ie7hacks.css b/plugins/legacy_browser/skins/larry/ie7hacks.css
index 2a17400..85ebaf2 100644
--- a/plugins/legacy_browser/skins/larry/ie7hacks.css
+++ b/plugins/legacy_browser/skins/larry/ie7hacks.css
@@ -37,6 +37,7 @@
 a.iconbutton,
 a.deletebutton,
 .boxpagenav a.icon,
+a.button span.icon,
 .pagenav a.button span.inner,
 .boxfooter .listbutton .inner,
 .attachmentslist li a.delete,
diff --git a/plugins/managesieve/skins/larry/managesieve.css b/plugins/managesieve/skins/larry/managesieve.css
index 1f954ca..2172c60 100644
--- a/plugins/managesieve/skins/larry/managesieve.css
+++ b/plugins/managesieve/skins/larry/managesieve.css
@@ -417,11 +417,13 @@
 
 
 /* vacation form */
-#settings-sections span.vacation a {
-  background: url(images/vacation_icons.png) no-repeat 7px 1px;
+#settings-sections .vacation a {
+  background-image: url(images/vacation_icons.png);
+	background-repeat: no-repeat;
+	background-position: 7px 1px;
 }
 
-#settings-sections span.vacation.selected a {
+#settings-sections .vacation.selected a {
   background-position: 7px -23px;
 }
 
diff --git a/plugins/zipdownload/zipdownload.js b/plugins/zipdownload/zipdownload.js
index 644c1e0..af9136c 100644
--- a/plugins/zipdownload/zipdownload.js
+++ b/plugins/zipdownload/zipdownload.js
@@ -43,21 +43,10 @@
             link.html('').append(span);
         }
 
-        span.addClass('folder-selector-link').text(rcmail.gettext('zipdownload.download'));
-
+        span.text(rcmail.gettext('zipdownload.download'));
         rcmail.env.download_link = link;
     });
-
-    // hide menu on click out of menu element
-    var fn = function(e) {
-        var menu = $('#zipdownload-menu');
-        if (e.target != menu.get(0))
-            menu.hide();
-    };
-    $(document.body).on('mouseup', fn);
-    $('iframe').contents().on('mouseup', fn)
-        .load(function(e) { try { $(this).contents().on('mouseup', fn); } catch(e) {}; });
-});
+  });
 
 
 function rcmail_zipdownload(mode)
@@ -100,14 +89,10 @@
 }
 
 // display download options menu
-function rcmail_zipdownload_menu()
+function rcmail_zipdownload_menu(e)
 {
-    // fix menu style and display menu
-    var z_index = rcmail.env.download_link.parents('.popupmenu').css('z-index'),
-        menu = $('#zipdownload-menu').css({'max-height': 'none', 'z-index': z_index + 1}).show();
-
-    // position menu on the screen
-    rcmail.element_position(menu, rcmail.env.download_link);
+    // show (sub)menu for download selection
+    rcmail.command('menu-open', 'zipdownload-menu', e && e.target ? e.target : rcmail.env.download_link, e);
 
     // abort default download action
     return false;
diff --git a/plugins/zipdownload/zipdownload.php b/plugins/zipdownload/zipdownload.php
index 90a3144..edb8188 100644
--- a/plugins/zipdownload/zipdownload.php
+++ b/plugins/zipdownload/zipdownload.php
@@ -96,7 +96,10 @@
 
         $rcmail  = rcmail::get_instance();
         $menu    = array();
-        $ul_attr = $rcmail->config->get('skin') == 'classic' ? null : array('class' => 'toolbarmenu');
+        $ul_attr = array('role' => 'menu', 'aria-labelledby' => 'aria-label-zipdownloadmenu');
+        if ($rcmail->config->get('skin') != 'classic') {
+            $ul_attr['class'] = 'toolbarmenu';
+        }
 
         foreach (array('eml', 'mbox', 'maildir') as $type) {
             $menu[] = html::tag('li', null, $rcmail->output->button(array(
@@ -106,7 +109,8 @@
             )));
         }
 
-        $rcmail->output->add_footer(html::div(array('id' => 'zipdownload-menu', 'class' => 'popupmenu'),
+        $rcmail->output->add_footer(html::div(array('id' => 'zipdownload-menu', 'class' => 'popupmenu', 'aria-hidden' => 'true'),
+            html::tag('h2', array('class' => 'voice', 'id' => 'aria-label-zipdownloadmenu'), "Message Download Options Menu") .
             html::tag('ul', $ul_attr, implode('', $menu))));
     }
 
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index a9e717b..1a22792 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -1077,14 +1077,17 @@
         }
         else {
             foreach ($table_data as $row_data) {
-                $class = !empty($row_data['class']) ? $row_data['class'] : '';
+                $class = !empty($row_data['class']) ? $row_data['class'] : null;
+                if (!empty($attrib['rowclass']))
+                    $class = trim($class . ' ' . $attrib['rowclass']);
                 $rowid = 'rcmrow' . rcube_utils::html_identifier($row_data[$id_col]);
 
                 $table->add_row(array('id' => $rowid, 'class' => $class));
 
                 // format each col
                 foreach ($a_show_cols as $col) {
-                    $table->add($col, $this->Q(is_array($row_data[$col]) ? $row_data[$col][0] : $row_data[$col]));
+                    $val = is_array($row_data[$col]) ? $row_data[$col][0] : $row_data[$col];
+                    $table->add($col, empty($attrib['ishtml']) ? $this->Q($val) : $val);
                 }
             }
         }
@@ -1490,7 +1493,7 @@
             $html_name = $this->Q($foldername) . ($unread ? html::span('unreadcount', sprintf($attrib['unreadwrap'], $unread)) : '');
             $link_attrib = $folder['virtual'] ? array() : array(
                 'href' => $this->url(array('_mbox' => $folder['id'])),
-                'onclick' => sprintf("return %s.command('list','%s',this)", rcmail_output::JS_OBJECT_NAME, $js_name),
+                'onclick' => sprintf("return %s.command('list','%s',this,event)", rcmail_output::JS_OBJECT_NAME, $js_name),
                 'rel' => $folder['id'],
                 'title' => $title,
             );
diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php
index 43d73a6..6594209 100644
--- a/program/include/rcmail_output_html.php
+++ b/program/include/rcmail_output_html.php
@@ -893,6 +893,14 @@
             return '';
         }
 
+        // localize title and summary attributes
+        if ($command != 'button' && !empty($attrib['title']) && $this->app->text_exists($attrib['title'])) {
+            $attrib['title'] = $this->app->gettext($attrib['title']);
+        }
+        if ($command != 'button' && !empty($attrib['summary']) && $this->app->text_exists($attrib['summary'])) {
+            $attrib['summary'] = $this->app->gettext($attrib['summary']);
+        }
+
         // execute command
         switch ($command) {
             // return a button
@@ -1165,6 +1173,17 @@
             $attrib['alt'] = html::quote($this->app->gettext($attrib['alt'], $attrib['domain']));
         }
 
+        // set accessibility attributes
+        if (!$attrib['role']) {
+            $attrib['role'] = 'button';
+        }
+        if (!empty($attrib['class']) && !empty($attrib['classact']) || !empty($attrib['imagepas']) && !empty($attrib['imageact'])) {
+            if (array_key_exists('tabindex', $attrib))
+                $attrib['data-tabindex'] = $attrib['tabindex'];
+            $attrib['tabindex'] = '-1';  // disable button by default
+            $attrib['aria-disabled'] = 'true';
+        }
+
         // set title to alt attribute for IE browsers
         if ($this->browser->ie && !$attrib['title'] && $attrib['alt']) {
             $attrib['title'] = $attrib['alt'];
@@ -1353,6 +1372,20 @@
             $is_empty = true;
         }
 
+        // set default page title
+        if (empty($this->pagetitle)) {
+            $this->pagetitle = 'Roundcube Mail';
+        }
+
+        // declare page language
+        if (!empty($_SESSION['language'])) {
+            $lang = substr($_SESSION['language'], 0, 2);
+            $output = preg_replace('/<html/', '<html lang="' . html::quote($lang) . '"', $output, 1);
+            if (!headers_sent()) {
+                header('Content-Language: ' . $lang);
+            }
+        }
+
         // replace specialchars in content
         $page_title  = html::quote($this->pagetitle);
         $page_header = '';
diff --git a/program/js/app.js b/program/js/app.js
index 11204ff..d12dd81 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -46,6 +46,7 @@
   this.messages = {};
   this.group2expand = {};
   this.http_request_jobs = {};
+  this.menu_stack = new Array();
 
   // webmail client settings
   this.dblclick_time = 500;
@@ -197,7 +198,10 @@
 
     // enable general commands
     this.enable_command('close', 'logout', 'mail', 'addressbook', 'settings', 'save-pref',
-      'compose', 'undo', 'about', 'switch-task', 'menu-open', 'menu-save', true);
+      'compose', 'undo', 'about', 'switch-task', 'menu-open', 'menu-close', 'menu-save', true);
+
+    // set active task button
+    this.set_button(this.task, 'sel');
 
     if (this.env.permaurl)
       this.enable_command('permaurl', 'extwin', true);
@@ -232,8 +236,7 @@
             return ref.command('sort', $(this).attr('rel'), this);
           });
 
-          document.onmouseup = function(e){ return ref.doc_mouse_up(e); };
-          this.gui_objects.messagelist.parentNode.onmousedown = function(e){ return ref.click_on_list(e); };
+          this.gui_objects.messagelist.parentNode.onclick = function(e){ return ref.click_on_list(e || window.event); };
 
           this.enable_command('toggle_status', 'toggle_flag', 'sort', true);
           this.enable_command('set-listmode', this.env.threads && !this.is_multifolder_listing());
@@ -276,7 +279,7 @@
           this.env.address_group_stack = [];
           this.env.compose_commands = ['send-attachment', 'remove-attachment', 'send', 'cancel',
             'toggle-editor', 'list-adresses', 'pushgroup', 'search', 'reset-search', 'extwin',
-            'insert-response', 'save-response'];
+            'insert-response', 'save-response', 'menu-open', 'menu-close'];
 
           if (this.env.drafts_mailbox)
             this.env.compose_commands.push('savedraft')
@@ -302,10 +305,12 @@
             $('a.insertresponse', this.gui_objects.responseslist)
               .attr('unselectable', 'on')
               .mousedown(function(e){ return rcube_event.cancel(e); })
-              .mouseup(function(e){
-                ref.command('insert-response', $(this).attr('rel'));
-                $(document.body).trigger('mouseup');  // hides the menu
-                return rcube_event.cancel(e);
+              .bind('mouseup keypress', function(e){
+                if (e.type == 'mouseup' || rcube_event.get_keycode(e) == 13) {
+                  ref.command('insert-response', $(this).attr('rel'));
+                  $(document.body).trigger('mouseup');  // hides the menu
+                  return rcube_event.cancel(e);
+                }
               });
 
             // avoid textarea loosing focus when hitting the save-response button/link
@@ -313,8 +318,6 @@
               $('#'+this.buttons['save-response'][i].id).mousedown(function(e){ return rcube_event.cancel(e); })
             }
           }
-
-          document.onmouseup = function(e){ return ref.doc_mouse_up(e); };
 
           // init message compose form
           this.init_messageform();
@@ -339,11 +342,21 @@
         // init address book widget
         if (this.gui_objects.contactslist) {
           this.contact_list = new rcube_list_widget(this.gui_objects.contactslist,
-            { multiselect:true, draggable:false, keyboard:false });
+            { multiselect:true, draggable:false, keyboard:true });
           this.contact_list
             .addEventListener('initrow', function(o) { ref.triggerEvent('insertrow', { cid:o.uid, row:o }); })
             .addEventListener('select', function(o) { ref.compose_recipient_select(o); })
             .addEventListener('dblclick', function(o) { ref.compose_add_recipient('to'); })
+            .addEventListener('keypress', function(o) {
+              if (o.key_pressed == o.ENTER_KEY) {
+                if (!ref.compose_add_recipient('to')) {
+                  // execute link action on <enter> if not a recipient entry
+                  if (o.last_selected && String(o.last_selected).charAt(0) == 'G') {
+                    $(o.rows[o.last_selected].obj).find('a').first().click();
+                  }
+                }
+              }
+            })
             .init();
         }
 
@@ -394,7 +407,6 @@
             this.contact_list.highlight_row(this.env.cid);
 
           this.gui_objects.contactslist.parentNode.onmousedown = function(e){ return ref.click_on_list(e); };
-          document.onmouseup = function(e){ return ref.doc_mouse_up(e); };
 
           $(this.gui_objects.qsearchbox).focusin(function() { ref.contact_list.blur(); });
 
@@ -449,9 +461,14 @@
 
         if (this.gui_objects.identitieslist) {
           this.identity_list = new rcube_list_widget(this.gui_objects.identitieslist,
-            {multiselect:false, draggable:false, keyboard:false});
+            {multiselect:false, draggable:false, keyboard:true});
           this.identity_list
             .addEventListener('select', function(o) { ref.identity_select(o); })
+            .addEventListener('keypress', function(o) {
+              if (o.key_pressed == o.ENTER_KEY) {
+                ref.identity_select(o);
+              }
+            })
             .init()
             .focus();
 
@@ -459,9 +476,10 @@
             this.identity_list.highlight_row(this.env.iid);
         }
         else if (this.gui_objects.sectionslist) {
-          this.sections_list = new rcube_list_widget(this.gui_objects.sectionslist, {multiselect:false, draggable:false, keyboard:false});
+          this.sections_list = new rcube_list_widget(this.gui_objects.sectionslist, {multiselect:false, draggable:false, keyboard:true});
           this.sections_list
             .addEventListener('select', function(o) { ref.section_select(o); })
+            .addEventListener('keypress', function(o) { if (o.key_pressed == o.ENTER_KEY) ref.section_select(o); })
             .init()
             .focus();
         }
@@ -469,7 +487,7 @@
           this.init_subscription_list();
         }
         else if (this.gui_objects.responseslist) {
-          this.responses_list = new rcube_list_widget(this.gui_objects.responseslist, {multiselect:false, draggable:false, keyboard:false});
+          this.responses_list = new rcube_list_widget(this.gui_objects.responseslist, {multiselect:false, draggable:false, keyboard:true});
           this.responses_list
             .addEventListener('select', function(list) {
               var win, id = list.get_single_selection();
@@ -559,6 +577,18 @@
         .get(0).addEventListener('drop', function(e){ return ref.file_dropped(e); }, false);
     }
 
+    // catch document (and iframe) mouse clicks
+    var body_mouseup = function(e){ return ref.doc_mouse_up(e); };
+    $(document.body)
+      .bind('mouseup', body_mouseup)
+      .bind('keydown', function(e){ return ref.doc_keypress(e); });
+
+    $('iframe').load(function(e) {
+        try { $(this.contentDocument || this.contentWindow).on('mouseup', body_mouseup);  }
+        catch (e) {/* catch possible "Permission denied" error in IE */ }
+      })
+      .contents().on('mouseup', body_mouseup);
+
     // trigger init event hook
     this.triggerEvent('init', { task:this.task, action:this.env.action });
 
@@ -591,7 +621,7 @@
   {
     var ret, uid, cid, url, flag, aborted = false;
 
-    if (obj && obj.blur)
+    if (obj && obj.blur && !(event || rcube_event.is_keyboard(event)))
       obj.blur();
 
     // do nothing if interface is locked by other command (with exception for searching reset)
@@ -634,8 +664,8 @@
     }
 
     // trigger plugin hooks
-    this.triggerEvent('actionbefore', {props:props, action:command});
-    ret = this.triggerEvent('before'+command, props);
+    this.triggerEvent('actionbefore', {props:props, action:command, originalEvent:event});
+    ret = this.triggerEvent('before'+command, props || event);
     if (ret !== undefined) {
       // abort if one of the handlers returned false
       if (ret === false)
@@ -707,9 +737,15 @@
           var mimetype = this.env.attachments[props.id];
           this.enable_command('open-attachment', mimetype && this.env.mimetypes && $.inArray(mimetype, this.env.mimetypes) >= 0);
         }
+        this.show_menu(props, props.show || undefined, event);
+        break;
+
+      case 'menu-close':
+        this.hide_menu(props, event);
+        break;
 
       case 'menu-save':
-        this.triggerEvent(command, {props:props});
+        this.triggerEvent(command, {props:props, originalEvent:event});
         return false;
 
       case 'open':
@@ -887,14 +923,14 @@
       case 'move':
       case 'moveto': // deprecated
         if (this.task == 'mail')
-          this.move_messages(props, obj);
+          this.move_messages(props, event);
         else if (this.task == 'addressbook')
           this.move_contacts(props);
         break;
 
       case 'copy':
         if (this.task == 'mail')
-          this.copy_messages(props, obj);
+          this.copy_messages(props, event);
         else if (this.task == 'addressbook')
           this.copy_contacts(props);
         break;
@@ -1450,7 +1486,8 @@
     if (menu && modkey == SHIFT_KEY && this.commands['copy']) {
       var pos = rcube_event.get_mouse_pos(e);
       this.env.drag_target = target;
-      $(menu).css({top: (pos.y-10)+'px', left: (pos.x-10)+'px'}).show();
+      this.show_menu(this.gui_objects.dragmenu.id, true, e);
+      $(menu).css({top: (pos.y-10)+'px', left: (pos.x-10)+'px'});
       return true;
     }
 
@@ -1566,17 +1603,22 @@
     }
   };
 
+  // global mouse-click handler to cleanup some UI elements
   this.doc_mouse_up = function(e)
   {
-    var list, id;
+    var list, id, target = rcube_event.get_target(e);
 
     // ignore event if jquery UI dialog is open
-    if ($(rcube_event.get_target(e)).closest('.ui-dialog, .ui-widget-overlay').length)
+    if ($(target).closest('.ui-dialog, .ui-widget-overlay').length)
       return;
 
-    list = this.message_list || this.contact_list;
-    if (list && !rcube_mouse_is_over(e, list.list.parentNode))
-      list.blur();
+    // remove focus from list widgets
+    if (window.rcube_list_widget && rcube_list_widget._instances.length) {
+      $.each(rcube_list_widget._instances, function(i,list){
+        if (list && !rcube_mouse_is_over(e, list.list.parentNode))
+          list.blur();
+      });
+    }
 
     // reset 'pressed' buttons
     if (this.buttons_sel) {
@@ -1585,7 +1627,77 @@
           this.button_out(this.buttons_sel[id], id);
       this.buttons_sel = {};
     }
+
+    // reset popup menus; delayed to have updated menu_stack data
+    window.setTimeout(function(e){
+      var obj, skip, config, id, i, parents = $(target).parents();
+      for (i = ref.menu_stack.length - 1; i >= 0; i--) {
+        id = ref.menu_stack[i];
+        obj = $('#' + id);
+
+        if (obj.is(':visible')
+          && target != obj.data('opener')
+          && target != obj.get(0)  // check if scroll bar was clicked (#1489832)
+          && !parents.is(obj.data('opener'))
+          && id != skip
+          && (obj.attr('data-editable') != 'true' || !$(target).parents('#' + id).length)
+          && (obj.attr('data-sticky') != 'true' || !rcube_mouse_is_over(e, obj.get(0)))
+        ) {
+          ref.hide_menu(id, e);
+        }
+        skip = obj.data('parent');
+      }
+    }, 10);
   };
+
+  // global keypress event handler
+  this.doc_keypress = function(e)
+  {
+    // Helper method to move focus to the next/prev active menu item
+    var focus_menu_item = function(dir) {
+      var obj, item, mod = dir < 0 ? 'prevAll' : 'nextAll', limit = dir < 0 ? 'last' : 'first';
+      if (ref.focused_menu && (obj = $('#'+ref.focused_menu))) {
+        item = obj.find(':focus').closest('li')[mod](':has(:not([aria-disabled=true]))').find('a,input')[limit]();
+        if (!item.length)
+          item = obj.find(':focus').closest('ul')[mod](':has(:not([aria-disabled=true]))').find('a,input')[limit]();
+        return item.focus().length;
+      }
+
+      return 0;
+    };
+
+    var target = e.target || {},
+      keyCode = rcube_event.get_keycode(e);
+
+    if (e.keyCode != 27 && (!this.menu_keyboard_active || target.nodeName == 'TEXTAREA' || target.nodeName == 'SELECT')) {
+      return true;
+    }
+
+    switch (keyCode) {
+      case 38:
+      case 40:
+      case 63232: // "up", in safari keypress
+      case 63233: // "down", in safari keypress
+        focus_menu_item(mod = keyCode == 38 || keyCode == 63232 ? -1 : 1);
+        break;
+
+      case 9:   // tab
+        if (this.focused_menu) {
+          var mod = rcube_event.get_modifier(e);
+          if (!focus_menu_item(mod == SHIFT_KEY ? -1 : 1)) {
+            this.hide_menu(this.focused_menu, e);
+          }
+        }
+        return rcube_event.cancel(e);
+
+      case 27:  // esc
+        if (this.menu_stack.length)
+          this.hide_menu(this.menu_stack[this.menu_stack.length-1], e);
+        break;
+    }
+
+    return true;
+  }
 
   this.click_on_list = function(e)
   {
@@ -1593,9 +1705,9 @@
       this.gui_objects.qsearchbox.blur();
 
     if (this.message_list)
-      this.message_list.focus();
+      this.message_list.focus(e);
     else if (this.contact_list)
-      this.contact_list.focus();
+      this.contact_list.focus(e);
 
     return true;
   };
@@ -1880,7 +1992,7 @@
       flags: flags.extra_flags
     });
 
-    var c, n, col, html, css_class,
+    var c, n, col, html, css_class, label, status_class = '', status_label = '',
       tree = '', expando = '',
       list = this.message_list,
       rows = list.rows,
@@ -1897,17 +2009,26 @@
     css_class = 'msgicon';
     if (this.env.status_col === null) {
       css_class += ' status';
-      if (flags.deleted)
-        css_class += ' deleted';
-      else if (!flags.seen)
-        css_class += ' unread';
-      else if (flags.unread_children > 0)
-        css_class += ' unreadchildren';
+      if (flags.deleted) {
+        status_class += ' deleted';
+        status_label += this.get_label('deleted') + ' ';
+      }
+      else if (!flags.seen) {
+        status_class += ' unread';
+        status_label += this.get_label('unread') + ' ';
+      }
+      else if (flags.unread_children > 0) {
+        status_class += ' unreadchildren';
+      }
     }
-    if (flags.answered)
-      css_class += ' replied';
-    if (flags.forwarded)
-      css_class += ' forwarded';
+    if (flags.answered) {
+      status_class += ' replied';
+      status_label += this.get_label('replied') + ' ';
+    }
+    if (flags.forwarded) {
+      status_class += ' forwarded';
+      status_label += this.get_label('replied') + ' ';
+    }
 
     // update selection
     if (message.selected && !list.in_selection(uid))
@@ -1944,15 +2065,17 @@
         row_class += ' unroot';
     }
 
-    tree += '<span id="msgicn'+row.id+'" class="'+css_class+'">&nbsp;</span>';
+    tree += '<span id="msgicn'+row.id+'" class="'+css_class+status_class+'" title="'+status_label+'"></span>';
     row.className = row_class;
 
     // build subject link
     if (cols.subject) {
-      var action = flags.mbox == this.env.drafts_mailbox ? 'compose' : 'show';
-      var uid_param = flags.mbox == this.env.drafts_mailbox ? '_draft_uid' : '_uid';
-      cols.subject = '<a href="./?_task=mail&_action='+action+'&_mbox='+urlencode(flags.mbox)+'&'+uid_param+'='+urlencode(uid)+'"'+
-        ' onclick="return rcube_event.cancel(event)" onmouseover="rcube_webmail.long_subject_title(this,'+(message.depth+1)+')"><span>'+cols.subject+'</span></a>';
+      var action  = flags.mbox == this.env.drafts_mailbox ? 'compose' : 'show',
+        uid_param = flags.mbox == this.env.drafts_mailbox ? '_draft_uid' : '_uid',
+        query = { _mbox: flags.mbox };
+      query[uid_param] = uid;
+      cols.subject = '<a href="' + this.url(action, query) + '" onclick="return rcube_event.keyboard_only(event)"' +
+        ' onmouseover="rcube_webmail.long_subject_title(this,'+(message.depth+1)+')" tabindex="-1"><span>'+cols.subject+'</span></a>';
     }
 
     // add each submitted col
@@ -1966,28 +2089,36 @@
 
       if (c == 'flag') {
         css_class = (flags.flagged ? 'flagged' : 'unflagged');
-        html = '<span id="flagicn'+row.id+'" class="'+css_class+'">&nbsp;</span>';
+        label = this.get_label(css_class);
+        html = '<span id="flagicn'+row.id+'" class="'+css_class+'" title="'+label+'"></span>';
       }
       else if (c == 'attachment') {
+        label = this.get_label('withattachment');
         if (flags.attachmentClass)
-          html = '<span class="'+flags.attachmentClass+'">&nbsp;</span>';
+          html = '<span class="'+flags.attachmentClass+'" title="'+label+'"></span>';
         else if (/application\/|multipart\/(m|signed)/.test(flags.ctype))
-          html = '<span class="attachment">&nbsp;</span>';
+          html = '<span class="attachment" title="'+label+'"></span>';
         else if (/multipart\/report/.test(flags.ctype))
-          html = '<span class="report">&nbsp;</span>';
-        else
+          html = '<span class="report"></span>';
+          else
           html = '&nbsp;';
       }
       else if (c == 'status') {
-        if (flags.deleted)
+        label = '';
+        if (flags.deleted) {
           css_class = 'deleted';
-        else if (!flags.seen)
+          label = this.get_label('deleted');
+        }
+        else if (!flags.seen) {
           css_class = 'unread';
-        else if (flags.unread_children > 0)
+          label = this.get_label('unread');
+        }
+        else if (flags.unread_children > 0) {
           css_class = 'unreadchildren';
+        }
         else
           css_class = 'msgicon';
-        html = '<span id="statusicn'+row.id+'" class="'+css_class+'">&nbsp;</span>';
+        html = '<span id="statusicn'+row.id+'" class="'+css_class+status_class+'" title="'+label+'"></span>';
       }
       else if (c == 'threads')
         html = expando;
@@ -1997,8 +2128,10 @@
         html = tree + cols[c];
       }
       else if (c == 'priority') {
-        if (flags.prio > 0 && flags.prio < 6)
-          html = '<span class="prio'+flags.prio+'">&nbsp;</span>';
+        if (flags.prio > 0 && flags.prio < 6) {
+          label = this.get_label('priority') + ' ' + flags.prio;
+          html = '<span class="prio'+flags.prio+'" title="'+label+'"></span>';
+        }
         else
           html = '&nbsp;';
       }
@@ -2308,7 +2441,6 @@
   this.clear_message_list = function()
   {
     this.env.messages = {};
-    this.last_selected = 0;
 
     this.show_contentframe(false);
     if (this.message_list)
@@ -2327,6 +2459,7 @@
       url._page = page;
 
     this.http_request('list', url, lock);
+    this.update_state({ _mbox: mbox, _page: (page && page > 1 ? page : null) });
   };
 
   // removes messages that doesn't exists from list selection array
@@ -2600,7 +2733,7 @@
   // set message icon
   this.set_message_icon = function(uid)
   {
-    var css_class,
+    var css_class, label = '',
       row = this.message_list.rows[uid];
 
     if (!row)
@@ -2608,38 +2741,55 @@
 
     if (row.icon) {
       css_class = 'msgicon';
-      if (row.deleted)
+      if (row.deleted) {
         css_class += ' deleted';
-      else if (row.unread)
+        label += this.get_label('deleted') + ' ';
+      }
+      else if (row.unread) {
         css_class += ' unread';
+        label += this.get_label('unread') + ' ';
+      }
       else if (row.unread_children)
         css_class += ' unreadchildren';
       if (row.msgicon == row.icon) {
-        if (row.replied)
+        if (row.replied) {
           css_class += ' replied';
-        if (row.forwarded)
+          label += this.get_label('replied') + ' ';
+        }
+        if (row.forwarded) {
           css_class += ' forwarded';
+          label += this.get_label('forwarded') + ' ';
+        }
         css_class += ' status';
       }
 
-      row.icon.className = css_class;
+      $(row.icon).attr('class', css_class).attr('title', label);
     }
 
     if (row.msgicon && row.msgicon != row.icon) {
+      label = '';
       css_class = 'msgicon';
-      if (!row.unread && row.unread_children)
+      if (!row.unread && row.unread_children) {
         css_class += ' unreadchildren';
-      if (row.replied)
+      }
+      if (row.replied) {
         css_class += ' replied';
-      if (row.forwarded)
+        label += this.get_label('replied') + ' ';
+      }
+      if (row.forwarded) {
         css_class += ' forwarded';
+        label += this.get_label('forwarded') + ' ';
+      }
 
-      row.msgicon.className = css_class;
+      $(row.msgicon).attr('class', css_class).attr('title', label);
     }
 
     if (row.flagicon) {
       css_class = (row.flagged ? 'flagged' : 'unflagged');
-      row.flagicon.className = css_class;
+      label = this.get_label(css_class);
+      $(row.flagicon).attr('class', css_class)
+        .attr('aria-label', label)
+        .attr('title', label);
     }
   };
 
@@ -2693,12 +2843,12 @@
   };
 
   // copy selected messages to the specified mailbox
-  this.copy_messages = function(mbox, obj)
+  this.copy_messages = function(mbox, event)
   {
     if (mbox && typeof mbox === 'object')
       mbox = mbox.id;
     else if (!mbox)
-      return this.folder_selector(obj, function(folder) { ref.command('copy', folder); });
+      return this.folder_selector(event, function(folder) { ref.command('copy', folder); });
 
     // exit if current or no mailbox specified
     if (!mbox || mbox == this.env.mailbox)
@@ -2715,12 +2865,12 @@
   };
 
   // move selected messages to the specified mailbox
-  this.move_messages = function(mbox, obj)
+  this.move_messages = function(mbox, event)
   {
     if (mbox && typeof mbox === 'object')
       mbox = mbox.id;
     else if (!mbox)
-      return this.folder_selector(obj, function(folder) { ref.command('move', folder); });
+      return this.folder_selector(event, function(folder) { ref.command('move', folder); });
 
     // exit if current or no mailbox specified
     if (!mbox || (mbox == this.env.mailbox && !this.is_multifolder_listing()))
@@ -3286,7 +3436,7 @@
     this.env.recipients_delimiter = this.env.recipients_separator + ' ';
 
     obj.keydown(function(e) { return ref.ksearch_keydown(e, this, props); })
-      .attr('autocomplete', 'off');
+      .attr({ 'autocomplete': 'off', 'aria-autocomplete': 'list', 'aria-expanded': 'false', 'role': 'combobox' });
   };
 
   this.submit_messageform = function(draft)
@@ -3357,6 +3507,8 @@
       input.val(oldval + recipients.join(delim + ' ') + delim + ' ');
       this.triggerEvent('add-recipient', { field:field, recipients:recipients });
     }
+
+    return recipients.length;
   };
 
   // checks the input fields before sending a message
@@ -3507,15 +3659,18 @@
       $('<a>').addClass('insertresponse active')
         .attr('href', '#')
         .attr('rel', key)
+        .attr('tabindex', '0')
         .html(this.quote_html(response.name))
         .appendTo(li)
         .mousedown(function(e){
           return rcube_event.cancel(e);
         })
-        .mouseup(function(e){
-          ref.command('insert-response', key);
-          $(document.body).trigger('mouseup');  // hides the menu
-          return rcube_event.cancel(e);
+        .bind('mouseup keypress', function(e){
+          if (e.type == 'mouseup' || rcube_event.get_keycode(e) == 13) {
+            ref.command('insert-response', $(this).attr('rel'));
+            $(document.body).trigger('mouseup');  // hides the menu
+            return rcube_event.cancel(e);
+          }
         });
     }
   };
@@ -3787,7 +3942,7 @@
 
       // cleanup
       rx = new RegExp(rx_delim + '\\s*' + rx_delim, 'g');
-      input_val = input_val.replace(rx, delim);
+      input_val = String(input_val).replace(rx, delim);
       rx = new RegExp('^[\\s' + rx_delim + ']+');
       input_val = input_val.replace(rx, '');
 
@@ -4155,7 +4310,7 @@
           return;
 
         var dir = key == 38 ? 1 : 0,
-          highlight = document.getElementById('rcmksearchSelected');
+          highlight = document.getElementById('rcmkSearchItem' + this.ksearch_selected);
 
         if (!highlight)
           highlight = this.ksearch_pane.__ul.firstChild;
@@ -4204,14 +4359,14 @@
 
   this.ksearch_select = function(node)
   {
-    var current = $('#rcmksearchSelected');
-    if (current[0] && node) {
-      current.removeAttr('id').removeClass('selected');
+    if (this.ksearch_pane && node) {
+      this.ksearch_pane.find('li.selected').removeClass('selected').removeAttr('aria-selected');
     }
 
     if (node) {
-      $(node).attr('id', 'rcmksearchSelected').addClass('selected');
+      $(node).addClass('selected').attr('aria-selected', 'true');
       this.ksearch_selected = node._rcm_id;
+      $(this.ksearch_input).attr('aria-activedescendant', 'rcmkSearchItem' + this.ksearch_selected);
     }
   };
 
@@ -4340,16 +4495,20 @@
       return;
 
     // display search results
-    var i, len, ul, li, text, type, init,
+    var i, id, len, ul, text, type, init,
       value = this.ksearch_value,
       maxlen = this.env.autocomplete_max ? this.env.autocomplete_max : 15;
 
     // create results pane if not present
     if (!this.ksearch_pane) {
       ul = $('<ul>');
-      this.ksearch_pane = $('<div>').attr('id', 'rcmKSearchpane')
+      this.ksearch_pane = $('<div>').attr('id', 'rcmKSearchpane').attr('role', 'listbox')
         .css({ position:'absolute', 'z-index':30000 }).append(ul).appendTo(document.body);
       this.ksearch_pane.__ul = ul[0];
+
+      // register (delegate) event handlers
+      ul.on('mouseover', 'li', function(e){ ref.ksearch_select(e.target); })
+        .on('onmouseup', 'li', function(e){ ref.ksearch_click(e.target); })
     }
 
     ul = this.ksearch_pane.__ul;
@@ -4374,23 +4533,29 @@
       for (i=0; i < len && maxlen > 0; i++) {
         text = typeof results[i] === 'object' ? results[i].name : results[i];
         type = typeof results[i] === 'object' ? results[i].type : '';
-        li = document.createElement('LI');
-        li.innerHTML = text.replace(new RegExp('('+RegExp.escape(value)+')', 'ig'), '##$1%%').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/##([^%]+)%%/g, '<b>$1</b>');
-        li.onmouseover = function(){ ref.ksearch_select(this); };
-        li.onmouseup = function(){ ref.ksearch_click(this) };
-        li._rcm_id = this.env.contacts.length + i;
-        if (type) li.className = type;
-        ul.appendChild(li);
+        id = i + this.env.contacts.length;
+        $('<li>').attr('id', 'rcmkSearchItem' + id)
+          .attr('role', 'option')
+          .html(this.quote_html(text.replace(new RegExp('('+RegExp.escape(value)+')', 'ig'), '##$1%%')).replace(/##([^%]+)%%/g, '<b>$1</b>'))
+          .addClass(type || '')
+          .appendTo(ul)
+          .get(0)._rcm_id = id;
         maxlen -= 1;
       }
     }
 
     if (ul.childNodes.length) {
+      // set the right aria-* attributes to the input field
+      $(this.ksearch_input)
+        .attr('aria-haspopup', 'true')
+        .attr('aria-expanded', 'true')
+        .attr('aria-owns', 'rcmKSearchpane');
+
       this.ksearch_pane.show();
+
       // select the first
       if (!this.env.contacts.length) {
-        $('li:first', ul).attr('id', 'rcmksearchSelected').addClass('selected');
-        this.ksearch_selected = 0;
+        this.ksearch_select($('li:first', ul).get(0));
       }
     }
 
@@ -4426,6 +4591,12 @@
 
     if (this.ksearch_pane)
       this.ksearch_pane.hide();
+
+    $(this.ksearch_input)
+      .attr('aria-haspopup', 'false')
+      .attr('aria-expanded', 'false')
+      .removeAttr('aria-activedescendant')
+      .removeAttr('aria-owns');
 
     this.ksearch_destroy();
   };
@@ -4631,6 +4802,7 @@
       // add link to pop back to parent group
       if (this.env.address_group_stack.length > 1) {
         $('<a href="#list">...</a>')
+          .attr('title', this.gettext('uponelevel'))
           .addClass('poplink')
           .appendTo(boxtitle)
           .click(function(e){ return ref.command('popgroup','',this); });
@@ -5174,6 +5346,7 @@
 
       if (appendcontainer.length && appendcontainer.get(0).nodeName == 'FIELDSET') {
         var input, colprop = this.env.coltypes[col],
+          input_id = 'ff_' + col + (colprop.count || 0),
           row = $('<div>').addClass('row'),
           cell = $('<div>').addClass('contactfieldcontent data'),
           label = $('<div>').addClass('contactfieldlabel label');
@@ -5181,14 +5354,14 @@
         if (colprop.subtypes_select)
           label.html(colprop.subtypes_select);
         else
-          label.html(colprop.label);
+          label.html('<label for="' + input_id + '">' + colprop.label + '</label>');
 
         var name_suffix = colprop.limit != 1 ? '[]' : '';
 
         if (colprop.type == 'text' || colprop.type == 'date') {
           input = $('<input>')
             .addClass('ff_'+col)
-            .attr({type: 'text', name: '_'+col+name_suffix, size: colprop.size})
+            .attr({type: 'text', name: '_'+col+name_suffix, size: colprop.size, id: input_id})
             .appendTo(cell);
 
           this.init_edit_field(col, input);
@@ -5199,7 +5372,7 @@
         else if (colprop.type == 'textarea') {
           input = $('<textarea>')
             .addClass('ff_'+col)
-            .attr({ name: '_'+col+name_suffix, cols:colprop.size, rows:colprop.rows })
+            .attr({ name: '_'+col+name_suffix, cols:colprop.size, rows:colprop.rows, id: input_id })
             .appendTo(cell);
 
           this.init_edit_field(col, input);
@@ -5235,7 +5408,7 @@
         else if (colprop.type == 'select') {
           input = $('<select>')
             .addClass('ff_'+col)
-            .attr('name', '_'+col+name_suffix)
+            .attr({ 'name': '_'+col+name_suffix, id: input_id })
             .appendTo(cell);
 
           var options = input.attr('options');
@@ -5542,7 +5715,7 @@
     this.last_sub_rx = RegExp('['+delim+']?[^'+delim+']+$');
 
     this.subscription_list = new rcube_list_widget(this.gui_objects.subscriptionlist,
-      {multiselect:false, draggable:true, keyboard:false, toggleselect:true});
+      {multiselect:false, draggable:true, keyboard:true, toggleselect:true});
     this.subscription_list
       .addEventListener('select', function(o){ ref.subscription_select(o); })
       .addEventListener('dragstart', function(o){ ref.drag_active = true; })
@@ -5551,7 +5724,8 @@
         row.obj.onmouseover = function() { ref.focus_subscription(row.id); };
         row.obj.onmouseout = function() { ref.unfocus_subscription(row.id); };
       })
-      .init();
+      .init()
+      .focus();
 
     $('#mailboxroot')
       .mouseover(function(){ ref.focus_subscription(this.id); })
@@ -5976,15 +6150,12 @@
         init_button(cmd, this.buttons[cmd][i]);
       }
     }
-
-    // set active task button
-    this.set_button(this.task, 'sel');
   };
 
   // set button to a specific state
   this.set_button = function(command, state)
   {
-    var n, button, obj, a_buttons = this.buttons[command],
+    var n, button, obj, $obj, a_buttons = this.buttons[command],
       len = a_buttons ? a_buttons.length : 0;
 
     for (n=0; n<len; n++) {
@@ -6019,7 +6190,14 @@
         obj.disabled = state == 'pas';
       }
       else if (button.type == 'uibutton') {
+        button.status = state;
         $(obj).button('option', 'disabled', state == 'pas');
+      }
+      else {
+        $obj = $(obj);
+        $obj
+          .attr('tabindex', state == 'pas' || state == 'sel' ? '-1' : ($obj.attr('data-tabindex') || '0'))
+          .attr('aria-disabled', state == 'pas' || state == 'sel' ? 'true' : 'false');
       }
     }
   };
@@ -6144,7 +6322,8 @@
       this.messages[key].labels = [{'id': id, 'msg': msg}];
     }
     else {
-      obj.click(function() { return ref.hide_message(obj); });
+      obj.click(function() { return ref.hide_message(obj); })
+        .attr('role', 'alert');
     }
 
     this.triggerEvent('message', { message:msg, type:type, timeout:timeout, object:obj });
@@ -6274,10 +6453,8 @@
       this.treelist.select(name);
     }
     else if (this.gui_objects.folderlist) {
-      $('li.selected', this.gui_objects.folderlist)
-        .removeClass('selected').addClass('unfocused');
-      $(this.get_folder_li(name, prefix, encode))
-        .removeClass('unfocused').addClass('selected');
+      $('li.selected', this.gui_objects.folderlist).removeClass('selected');
+      $(this.get_folder_li(name, prefix, encode)).addClass('selected');
 
       // trigger event hook
       this.triggerEvent('selectfolder', { folder:name, prefix:prefix });
@@ -6327,7 +6504,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;
@@ -6511,17 +6688,15 @@
   };
 
   // create folder selector popup, position and display it
-  this.folder_selector = function(obj, callback)
+  this.folder_selector = function(event, callback)
   {
     var container = this.folder_selector_element;
 
     if (!container) {
       var rows = [],
         delim = this.env.delimiter,
-        ul = $('<ul class="toolbarmenu iconized">'),
-        li = document.createElement('li'),
-        link = document.createElement('a'),
-        span = document.createElement('span');
+        ul = $('<ul class="toolbarmenu">'),
+        link = document.createElement('a');
 
       container = $('<div id="folder-selector" class="popupmenu"></div>');
       link.href = '#';
@@ -6529,33 +6704,30 @@
 
       // loop over sorted folders list
       $.each(this.env.mailboxes_list, function() {
-        var tmp, n = 0, s = 0,
+        var n = 0, s = 0,
           folder = ref.env.mailboxes[this],
           id = folder.id,
-          a = link.cloneNode(false), row = li.cloneNode(false);
+          a = $(link.cloneNode(false)),
+          row = $('<li>');
 
         if (folder.virtual)
-          a.className += ' virtual';
-        else {
-          a.className += ' active';
-          a.onclick = function() { container.hide().data('callback')(folder.id); };
-        }
+          a.addClass('virtual').attr('aria-disabled', 'true').attr('tabindex', '-1');
+        else
+          a.addClass('active').data('id', folder.id);
 
         if (folder['class'])
-          a.className += ' ' + folder['class'];
+          a.addClass(folder['class']);
 
         // calculate/set indentation level
         while ((s = id.indexOf(delim, s)) >= 0) {
           n++; s++;
         }
-        a.style.paddingLeft =  n ? (n * 16) + 'px' : 0;
+        a.css('padding-left', n ? (n * 16) + 'px' : 0);
 
         // add folder name element
-        tmp = span.cloneNode(false);
-        $(tmp).text(folder.name);
-        a.appendChild(tmp);
+        a.append($('<span>').text(folder.name));
 
-        row.appendChild(a);
+        row.append(a);
         rows.push(row);
       });
 
@@ -6567,22 +6739,156 @@
 
       // set max-height if the list is long
       if (rows.length > 10)
-        container.css('max-height', $('li', container)[0].offsetHeight * 10 + 9)
+        container.css('max-height', $('li', container)[0].offsetHeight * 10 + 9);
 
-      // hide selector on click out of selector element
-      var fn = function(e) { if (e.target != container.get(0)) container.hide(); };
-      $(document.body).on('mouseup', fn);
-      $('iframe').contents().on('mouseup', fn)
-        .load(function(e) { try { $(this).contents().on('mouseup', fn); } catch(e) {}; });
+      // register delegate event handler for folder item clicks
+      container.on('click', 'a.active', function(e){
+        container.data('callback')($(this).data('id'));
+        return false;
+      });
 
       this.folder_selector_element = container;
     }
 
-    // position menu on the screen
-    this.element_position(container, obj);
+    container.data('callback', callback);
 
-    container.show().data('callback', callback);
+    // position menu on the screen
+    this.show_menu('folder-selector', true, event);
   };
+
+
+  /***********************************************/
+  /*********    popup menu functions     *********/
+  /***********************************************/
+
+  // Show/hide a specific popup menu
+  this.show_menu = function(prop, show, event)
+  {
+    var name = typeof prop == 'object' ? prop.menu : prop,
+      obj = $('#'+name),
+      ref = event && event.target ? $(event.target) : $(obj.attr('rel') || '#'+name+'link'),
+      keyboard = rcube_event.is_keyboard(event),
+      align = obj.attr('data-align') || '',
+      stack = false;
+
+    // find "real" button element
+    if (ref.get(0).tagName != 'A' && ref.closest('a').length)
+      ref = ref.closest('a');
+
+    if (typeof prop == 'string')
+      prop = { menu:name };
+
+    // let plugins or skins provide the menu element
+    if (!obj.length) {
+      obj = this.triggerEvent('menu-get', { name:name, props:prop, originalEvent:event });
+    }
+
+    if (!obj || !obj.length) {
+      // just delegate the action to subscribers
+      return this.triggerEvent(show === false ? 'menu-close' : 'menu-open', { name:name, props:prop, originalEvent:event });
+    }
+
+    // move element to top for proper absolute positioning
+    obj.appendTo(document.body);
+
+    if (typeof show == 'undefined')
+      show = obj.is(':visible') ? false : true;
+
+    if (show && ref.length) {
+      var win = $(window),
+        pos = ref.offset(),
+        above = align.indexOf('bottom') >= 0;
+
+      stack = ref.attr('role') == 'menuitem' || ref.closest('[role=menuitem]').length > 0;
+
+      ref.offsetWidth = ref.outerWidth();
+      ref.offsetHeight = ref.outerHeight();
+      if (!above && pos.top + ref.offsetHeight + obj.height() > win.height()) {
+        above = true;
+      }
+      if (align.indexOf('right') >= 0) {
+        pos.left = pos.left + ref.outerWidth() - obj.width();
+      }
+      else if (stack) {
+        pos.left = pos.left + ref.offsetWidth - 5;
+        pos.top -= ref.offsetHeight;
+      }
+      if (pos.left + obj.width() > win.width()) {
+        pos.left = win.width() - obj.width() - 12;
+      }
+      pos.top = Math.max(0, pos.top + (above ? -obj.height() : ref.offsetHeight));
+      obj.css({ left:pos.left+'px', top:pos.top+'px' });
+    }
+
+    // add menu to stack
+    if (show) {
+      // truncate stack down to the one containing the ref link
+      for (var i = this.menu_stack.length - 1; stack && i >= 0; i--) {
+        if (!$(ref).parents('#'+this.menu_stack[i]).length)
+          this.hide_menu(this.menu_stack[i]);
+      }
+      if (stack && this.menu_stack.length) {
+        obj.data('parent', $.last(this.menu_stack));
+        obj.css('z-index', ($('#'+$.last(this.menu_stack)).css('z-index') || 0) + 1);
+      }
+      else if (!stack && this.menu_stack.length) {
+        this.hide_menu(this.menu_stack[0], event);
+      }
+
+      obj.show().attr('aria-hidden', 'false').data('opener', ref.attr('aria-expanded', 'true').get(0));
+      this.triggerEvent('menu-open', { name:name, obj:obj, props:prop, originalEvent:event });
+      this.menu_stack.push(name);
+
+      this.menu_keyboard_active = show && keyboard;
+      if (this.menu_keyboard_active) {
+        this.focused_menu = name;
+        obj.find('a,input:not(:disabled)').not('[aria-disabled=true]').first().focus();
+      }
+    }
+    else {  // close menu
+      this.hide_menu(name, event);
+    }
+
+    return show;
+  };
+
+  // hide the given popup menu (and it's childs)
+  this.hide_menu = function(name, event)
+  {
+    if (!this.menu_stack.length) {
+      // delegate to subscribers
+      this.triggerEvent('menu-close', { name:name, props:{ menu:name }, originalEvent:event });
+      return;
+    }
+
+    var obj, keyboard = rcube_event.is_keyboard(event);
+    for (var j=this.menu_stack.length-1; j >= 0; j--) {
+      obj = $('#' + this.menu_stack[j]).hide().attr('aria-hidden', 'true').data('parent', false);
+      this.triggerEvent('menu-close', { name:this.menu_stack[j], obj:obj, props:{ menu:this.menu_stack[j] }, originalEvent:event });
+      if (this.menu_stack[j] == name) {
+        j = -1;  // stop loop
+        if (obj.data('opener')) {
+          $(obj.data('opener')).attr('aria-expanded', 'false');
+          if (keyboard)
+            obj.data('opener').focus();
+        }
+      }
+      this.menu_stack.pop();
+    }
+
+    // focus previous menu in stack
+    if (this.menu_stack.length && keyboard) {
+      this.menu_keyboard_active = true;
+      this.focused_menu = $.last(this.menu_stack);
+      if (!obj || !obj.data('opener'))
+        $('#'+this.focused_menu).find('a,input:not(:disabled)').not('[aria-disabled=true]').first().focus();
+    }
+    else {
+      this.focused_menu = null;
+      this.menu_keyboard_active = false;
+    }
+  }
+
 
   // position a menu element on the screen in relation to other object
   this.element_position = function(element, obj)
@@ -6751,6 +7057,13 @@
 
     // reset keep-alive interval
     this.start_keepalive();
+  };
+
+  // update browser location to remember current view
+  this.update_state = function(query)
+  {
+    if (window.history.replaceState)
+      window.history.replaceState({}, document.title, rcmail.url('', query));
   };
 
   // send a http request to the server
@@ -6938,6 +7251,8 @@
 
           if ((response.action == 'list' || response.action == 'search') && this.message_list) {
             this.enable_command('set-listmode', this.env.threads && !is_multifolder);
+            if (this.message_list.rowcount > 0)
+              this.message_list.focus();
             this.msglist_select(this.message_list);
             this.triggerEvent('listupdate', { folder:this.env.mailbox, rowcount:this.message_list.rowcount });
           }
@@ -6949,10 +7264,18 @@
             this.enable_command('search-create', this.env.source == '');
             this.enable_command('search-delete', this.env.search_id);
             this.update_group_commands();
+            if (this.contact_list.rowcount > 0)
+              this.contact_list.focus();
             this.triggerEvent('listupdate', { folder:this.env.source, rowcount:this.contact_list.rowcount });
           }
         }
         break;
+
+      case 'list-contacts':
+      case 'search-contacts':
+        if (this.contact_list && this.contact_list.rowcount > 0)
+          this.contact_list.focus();
+        break;
     }
 
     if (response.unlock)
diff --git a/program/js/common.js b/program/js/common.js
index 48e8555..5ac4feb 100644
--- a/program/js/common.js
+++ b/program/js/common.js
@@ -281,6 +281,25 @@
   return false;
 },
 
+/**
+ * Determine whether the given event was trigered from keyboard
+ */
+is_keyboard: function(e)
+{
+  return e && (
+      (e.mozInputSource && e.mozInputSource == e.MOZ_SOURCE_KEYBOARD) ||
+      (!e.pageX && (e.pageY || 0) <= 0 && !e.clientX && (e.clientY || 0) <= 0)
+    );
+},
+
+/**
+ * Accept event if triggered from keyboard action (e.g. <Enter>)
+ */
+keyboard_only: function(e)
+{
+  return rcube_event.is_keyboard(e) ? true : rcube_event.cancel(e);
+},
+
 touchevent: function(e)
 {
   return { pageX:e.pageX, pageY:e.pageY, offsetX:e.pageX - e.target.offsetLeft, offsetY:e.pageY - e.target.offsetTop, target:e.target, istouch:true };
@@ -593,6 +612,11 @@
   };
 }
 
+// array utility function
+jQuery.last = function(arr) {
+  return arr && arr.length ? arr[arr.length-1] : undefined;
+}
+
 // jQuery plugin to emulate HTML5 placeholder attributes on input elements
 jQuery.fn.placeholder = function(text) {
   return this.each(function() {
diff --git a/program/js/list.js b/program/js/list.js
index 04aec1c..5492c0a 100644
--- a/program/js/list.js
+++ b/program/js/list.js
@@ -51,7 +51,7 @@
   this.rowcount = 0;
   this.colcount = 0;
 
-  this.subject_col = -1;
+  this.subject_col = 0;
   this.modkey = 0;
   this.multiselect = false;
   this.multiexpand = false;
@@ -60,6 +60,7 @@
   this.column_movable = false;
   this.keyboard = false;
   this.toggleselect = false;
+  this.aria_listbox = false;
 
   this.drag_active = false;
   this.col_drag_active = false;
@@ -75,6 +76,9 @@
   if (p && typeof p === 'object')
     for (var n in p)
       this[n] = p[n];
+
+  // register this instance
+  rcube_list_widget._instances.push(this);
 };
 
 
@@ -94,11 +98,17 @@
     this.tbody = this.list;
   }
 
+  if ($(this.list).attr('role') == 'listbox') {
+    this.aria_listbox = true;
+    if (this.multiselect)
+      $(this.list).attr('aria-multiselectable', 'true');
+  }
+
   if (this.tbody) {
     this.rows = {};
     this.rowcount = 0;
 
-    var r, len, rows = this.tbody.childNodes;
+    var r, len, rows = this.tbody.childNodes, me = this;
 
     for (r=0, len=rows.length; r<len; r++) {
       this.rowcount += this.init_row(rows[r]) ? 1 : 0;
@@ -108,8 +118,13 @@
     this.frame = this.list.parentNode;
 
     // set body events
-    if (this.keyboard)
+    if (this.keyboard) {
       rcube_event.add_listener({event:'keydown', object:this, method:'key_press'});
+
+      // allow the table element to receive focus.
+      $(this.list).attr('tabindex', '0')
+        .on('focus', function(e){ me.focus(e); });
+    }
   }
 
   return this;
@@ -154,6 +169,15 @@
       }, false);
     }
 
+    // label the list row with the subject col as descriptive label
+    if (this.aria_listbox) {
+      var lbl_id = 'l:' + row.id;
+      $(row)
+        .attr('role', 'option')
+        .attr('aria-labelledby', lbl_id)
+        .find(this.col_tagname()).eq(this.subject_col).attr('id', lbl_id);
+    }
+
     if (document.all)
       row.onselectstart = function() { return false; };
 
@@ -175,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', '');  // 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();
@@ -202,6 +226,7 @@
   if (!this.fixed_header) {
     this.fixed_header = $('<table>')
       .attr('class', this.list.className + ' fixedcopy')
+      .attr('role', 'presentation')
       .css({ position:'fixed' })
       .append(clone)
       .append('<tbody></tbody>');
@@ -219,6 +244,12 @@
   else {
     $(this.fixed_header).find('thead').replaceWith(clone);
   }
+
+  // avoid scrolling header links being focused
+  $(this.list.tHead).find('a.sortcol').attr('tabindex', '-1');
+
+  // set tabindex to fixed header sort links
+  clone.find('a.sortcol').attr('tabindex', '0');
 
   this.thead = clone.get(0);
   this.resize();
@@ -262,6 +293,7 @@
 
   this.rows = {};
   this.rowcount = 0;
+  this.last_selected = 0;
 
   if (sel)
     this.clear_selection();
@@ -370,39 +402,66 @@
  */
 focus: function(e)
 {
-  var n, id;
+  if (this.focused)
+    return;
+
   this.focused = true;
 
-  for (n in this.selection) {
-    id = this.selection[n];
-    if (this.rows[id] && this.rows[id].obj) {
-      $(this.rows[id].obj).addClass('selected').removeClass('unfocused');
-    }
+  if (e)
+    rcube_event.cancel(e);
+
+  var focus_elem = null;
+
+  if (this.last_selected && this.rows[this.last_selected]) {
+    focus_elem = $(this.rows[this.last_selected].obj).find(this.col_tagname()).eq(this.subject_col).attr('tabindex', '0');
   }
 
   // Un-focus already focused elements (#1487123, #1487316, #1488600, #1488620)
-  // It looks that window.focus() does the job for all browsers, but not Firefox (#1489058)
-  $('iframe,:focus:not(body)').blur();
-  window.focus();
+  if (focus_elem && focus_elem.length) {
+    // We now fix this by explicitly assigning focus to a dedicated link element
+    this.focus_noscroll(focus_elem);
+  }
+  else {
+    // It looks that window.focus() does the job for all browsers, but not Firefox (#1489058)
+    $('iframe,:focus:not(body)').blur();
+    window.focus();
+  }
 
-  if (e || (e = window.event))
-    rcube_event.cancel(e);
+  $(this.list).addClass('focus').removeAttr('tabindex');
+
+  // set internal focus pointer to first row
+  if (!this.last_selected)
+    this.select_first(CONTROL_KEY);
 },
 
 
 /**
  * remove focus from the list
  */
-blur: function()
+blur: function(e)
 {
-  var n, id;
   this.focused = false;
-  for (n in this.selection) {
-    id = this.selection[n];
-    if (this.rows[id] && this.rows[id].obj) {
-      $(this.rows[id].obj).removeClass('selected focused').addClass('unfocused');
-    }
+
+  // avoid the table getting focus right again
+  var me = this;
+  setTimeout(function(){
+    $(me.list).removeClass('focus').attr('tabindex', '0');
+  }, 20);
+
+  if (this.last_selected && this.rows[this.last_selected]) {
+    $(this.rows[this.last_selected].obj)
+      .find(this.col_tagname()).eq(this.subject_col).removeAttr('tabindex');
   }
+},
+
+/**
+ * Focus the given element without scrolling the list container
+ */
+focus_noscroll: function(elem)
+{
+  var y = this.frame.scrollTop || this.frame.scrollY;
+  elem.focus();
+  this.frame.scrollTop = y;
 },
 
 
@@ -522,6 +581,8 @@
   }
 
   this.rows[id].clicked = now;
+  this.focus();
+
   return false;
 },
 
@@ -794,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;
   }
@@ -839,9 +900,10 @@
  */
 select_row: function(id, mod_key, with_mouse)
 {
-  var select_before = this.selection.join(',');
+  var select_before = this.selection.join(','),
+    in_selection_before = this.in_selection(id);
 
-  if (!this.multiselect)
+  if (!this.multiselect && with_mouse)
     mod_key = 0;
 
   if (!this.shift_start)
@@ -877,20 +939,26 @@
     this.multi_selecting = true;
   }
 
-  // trigger event if selection changed
-  if (this.selection.join(',') != select_before)
-    this.triggerEvent('select');
-
-  if (this.last_selected != 0 && this.rows[this.last_selected])
-    $(this.rows[this.last_selected].obj).removeClass('focused');
+  if (this.last_selected != 0 && this.rows[this.last_selected]) {
+    $(this.rows[this.last_selected].obj).removeClass('focused')
+      .find(this.col_tagname()).eq(this.subject_col).removeAttr('tabindex');
+  }
 
   // unselect if toggleselect is active and the same row was clicked again
-  if (this.toggleselect && this.last_selected == id) {
+  if (this.toggleselect && in_selection_before) {
     this.clear_selection();
-    id = null;
   }
-  else
+  // trigger event if selection changed
+  else if (this.selection.join(',') != select_before) {
+    this.triggerEvent('select');
+  }
+
+  if (this.rows[id]) {
     $(this.rows[id].obj).addClass('focused');
+    // set cursor focus to link inside selected row
+    if (this.focused)
+      this.focus_noscroll($(this.rows[id].obj).find(this.col_tagname()).eq(this.subject_col).attr('tabindex', '0'));
+  }
 
   if (!this.selection.length)
     this.shift_start = null;
@@ -1038,7 +1106,7 @@
       this.highlight_row(n, true, true);
     }
     else {
-      $(this.rows[n].obj).removeClass('selected').removeClass('unfocused');
+      $(this.rows[n].obj).removeClass('selected').removeAttr('aria-selected');
     }
   }
 
@@ -1095,14 +1163,16 @@
   else {
     for (n in this.selection)
       if (this.rows[this.selection[n]]) {
-        $(this.rows[this.selection[n]].obj).removeClass('selected').removeClass('unfocused');
+        $(this.rows[this.selection[n]].obj).removeClass('selected').removeAttr('aria-selected');
       }
 
     this.selection = [];
   }
 
-  if (num_select && !this.selection.length && !no_event)
+  if (num_select && !this.selection.length && !no_event) {
     this.triggerEvent('select');
+    this.last_selected = 0;
+  }
 },
 
 
@@ -1156,13 +1226,13 @@
     if (this.selection.length > 1 || !this.in_selection(id)) {
       this.clear_selection(null, true);
       this.selection[0] = id;
-      $(this.rows[id].obj).addClass('selected');
+      $(this.rows[id].obj).addClass('selected').attr('aria-selected', 'true');
     }
   }
   else {
     if (!this.in_selection(id)) { // select row
       this.selection.push(id);
-      $(this.rows[id].obj).addClass('selected');
+      $(this.rows[id].obj).addClass('selected').attr('aria-selected', 'true');
       if (!norecur && !this.rows[id].expanded)
         this.highlight_children(id, true);
     }
@@ -1172,7 +1242,7 @@
         a_post = this.selection.slice(p+1, this.selection.length);
 
       this.selection = a_pre.concat(a_post);
-      $(this.rows[id].obj).removeClass('selected').removeClass('unfocused');
+      $(this.rows[id].obj).removeClass('selected').removeAttr('aria-selected');
       if (!norecur && !this.rows[id].expanded)
         this.highlight_children(id, false);
     }
@@ -1252,6 +1322,14 @@
 
       return rcube_event.cancel(e);
 
+    case 9: // Tab
+      this.blur();
+      break;
+
+    case 13: // Enter
+      if (!this.selection.length)
+        this.select_row(this.last_selected, mod_key, false);
+
     default:
       this.key_pressed = keyCode;
       this.modkey = mod_key;
@@ -1311,8 +1389,16 @@
   }
 
   if (new_row) {
+    // simulate ctr-key if no rows are selected
+    if (!mod_key && !this.selection.length)
+      mod_key = CONTROL_KEY;
+
     this.select_row(new_row.uid, mod_key, false);
     this.scrollto(new_row.uid);
+  }
+  else if (!new_row && !selected_row) {
+    // select the first row if none selected yet
+    this.select_first(CONTROL_KEY);
   }
 
   return false;
@@ -1708,3 +1794,6 @@
 rcube_list_widget.prototype.addEventListener = rcube_event_engine.prototype.addEventListener;
 rcube_list_widget.prototype.removeEventListener = rcube_event_engine.prototype.removeEventListener;
 rcube_list_widget.prototype.triggerEvent = rcube_event_engine.prototype.triggerEvent;
+
+// static
+rcube_list_widget._instances = [];
diff --git a/program/js/treelist.js b/program/js/treelist.js
index 90eeeea..65f5fd4 100644
--- a/program/js/treelist.js
+++ b/program/js/treelist.js
@@ -55,6 +55,7 @@
     drag_active = false,
     search_active = false,
     last_search = '',
+    has_focus = false,
     box_coords = {},
     item_coords = [],
     autoexpand_timer,
@@ -151,6 +152,19 @@
     })
   }
 
+  container.on('focusin', function(e){
+      // TODO: only accept focus on virtual nodes from keyboard events
+      has_focus = true;
+    })
+    .on('focusout', function(e){
+      has_focus = false;
+    });
+
+  container.attr('role', 'tree');
+
+  $(document.body)
+    .bind('keydown', keypress);
+
 
   /////// private methods
 
@@ -201,13 +215,13 @@
   function select(id)
   {
     if (selection) {
-      id2dom(selection).removeClass('selected');
+      id2dom(selection).removeClass('selected').removeAttr('aria-selected');
       selection = null;
     }
 
     var li = id2dom(id);
     if (li.length) {
-      li.addClass('selected');
+      li.addClass('selected').attr('aria-selected', 'true');
       selection = id;
       // TODO: expand all parent nodes if collapsed
       scroll_to_node(li);
@@ -262,6 +276,7 @@
 
     // insert as child of an existing node
     if (parent_node) {
+      node.level = parent_node.level + 1;
       if (!parent_node.children)
         parent_node.children = [];
 
@@ -296,6 +311,7 @@
     }
     // insert at top level
     else {
+      node.level = 0;
       data.push(node);
       li = render_node(node, container);
     }
@@ -392,7 +408,7 @@
    */
   function update_data()
   {
-    data = walk_list(container);
+    data = walk_list(container, 0);
   }
 
   /**
@@ -401,6 +417,7 @@
   function update_dom(node)
   {
     var li = id2dom(node.id);
+    li.attr('aria-expanded', node.collapsed ? 'false' : 'true');
     li.children('ul').first()[(node.collapsed ? 'hide' : 'show')]();
     li.children('div.treetoggle').removeClass('collapsed expanded').addClass(node.collapsed ? 'collapsed' : 'expanded');
     me.triggerEvent('toggle', node);
@@ -507,6 +524,7 @@
 
     // render child nodes
     for (var i=0; i < data.length; i++) {
+      data[i].level = 0;
       render_node(data[i], container);
     }
 
@@ -523,6 +541,7 @@
 
     var li = $('<li>')
       .attr('id', p.id_prefix + (p.id_encode ? p.id_encode(node.id) : node.id))
+      .attr('role', 'treeitem')
       .addClass((node.classes || []).join(' '))
       .data('id', node.id);
 
@@ -546,12 +565,14 @@
 
     // add child list and toggle icon
     if (node.children && node.children.length) {
+      li.attr('aria-expanded', node.collapsed ? 'false' : 'true');
       $('<div class="treetoggle '+(node.collapsed ? 'collapsed' : 'expanded') + '">&nbsp;</div>').appendTo(li);
-      var ul = $('<ul>').appendTo(li).attr('class', node.childlistclass);
+      var ul = $('<ul>').appendTo(li).attr('class', node.childlistclass).attr('role', 'group');
       if (node.collapsed)
         ul.hide();
 
       for (var i=0; i < node.children.length; i++) {
+        node.children[i].level = node.level + 1;
         render_node(node.children[i], ul);
       }
     }
@@ -563,7 +584,7 @@
    * Recursively walk the DOM tree and build an internal data structure
    * representing the skeleton of this tree list.
    */
-  function walk_list(ul)
+  function walk_list(ul, level)
   {
     var result = [];
     ul.children('li').each(function(i,e){
@@ -572,9 +593,10 @@
         id: dom2id(li),
         classes: String(li.attr('class')).split(' '),
         virtual: li.hasClass('virtual'),
+        level: level,
         html: li.children().first().get(0).outerHTML,
         text: li.children().first().text(),
-        children: walk_list(sublist)
+        children: walk_list(sublist, level+1)
       }
 
       if (sublist.length) {
@@ -592,15 +614,29 @@
 
         if (!li.children('div.treetoggle').length)
           $('<div class="treetoggle '+(node.collapsed ? 'collapsed' : 'expanded') + '">&nbsp;</div>').appendTo(li);
+
+        li.attr('aria-expanded', node.collapsed ? 'false' : 'true');
       }
       if (li.hasClass('selected')) {
+        li.attr('aria-selected', 'true');
         selection = node.id;
       }
 
       li.data('id', node.id);
+
+      // declare list item as treeitem
+      li.attr('role', 'treeitem').attr('aria-level', node.level+1);
+
+      // allow virtual nodes to receive focus
+      if (node.virtual) {
+        li.children('a:first').attr('tabindex', '0');
+      }
+
       result.push(node);
       indexbyid[node.id] = node;
-    })
+    });
+
+    ul.attr('role', level == 0 ? 'tree' : 'group');
 
     return result;
   }
@@ -683,6 +719,70 @@
     return undefined;
   }
 
+  /**
+   * Handler for keyboard events on treelist
+   */
+  function keypress(e)
+  {
+    var target = e.target || {},
+      keyCode = rcube_event.get_keycode(e);
+
+    if (!has_focus || target.nodeName == 'INPUT' || target.nodeName == 'TEXTAREA' || target.nodeName == 'SELECT')
+      return true;
+
+    switch (keyCode) {
+      case 38:
+      case 40:
+      case 63232: // 'up', in safari keypress
+      case 63233: // 'down', in safari keypress
+        var li = container.find(':focus').closest('li');
+        if (li.length) {
+          focus_next(li, (mod = keyCode == 38 || keyCode == 63232 ? -1 : 1));
+        }
+        break;
+
+      case 37: // Left arrow key
+      case 39: // Right arrow key
+        var id, node, li = container.find(':focus').closest('li');
+        if (li.length) {
+          id = dom2id(li);
+          node = indexbyid[id];
+          if (node && node.children.length)
+            toggle(id, rcube_event.get_modifier(e) == SHIFT_KEY);  // toggle subtree
+        }
+        return false;
+    }
+
+    return true;
+  }
+
+  function focus_next(li, dir, from_child)
+  {
+    var mod = dir < 0 ? 'prev' : 'next',
+      next = li[mod](), limit, parent;
+
+    if (dir > 0 && !from_child && li.children('ul[role=group]:visible').length) {
+      li.children('ul').children('li:first').children('a:first').focus();
+    }
+    else if (dir < 0 && !from_child && next.children('ul[role=group]:visible').length) {
+      next.children('ul').children('li:last').children('a:last').focus();
+    }
+    else if (next.length && next.children('a:first')) {
+      next.children('a:first').focus();
+    }
+    else {
+      parent = li.parent().closest('li[role=treeitem]');
+      if (parent.length)
+        if (dir < 0) {
+          parent.children('a:first').focus();
+        }
+        else {
+          focus_next(parent, dir, true);
+        }
+    }
+>>>>>>> dev-accessibility
+  }
+
 
   ///// drag & drop support
 
diff --git a/program/lib/Roundcube/html.php b/program/lib/Roundcube/html.php
index a88570d..0209d1b 100644
--- a/program/lib/Roundcube/html.php
+++ b/program/lib/Roundcube/html.php
@@ -32,7 +32,7 @@
 
     public static $doctype = 'xhtml';
     public static $lc_tags = true;
-    public static $common_attrib = array('id','class','style','title','align','unselectable');
+    public static $common_attrib = array('id','class','style','title','align','unselectable','tabindex','role');
     public static $containers = array('iframe','div','span','p','h1','h2','h3','ul','form','textarea','table','thead','tbody','tr','th','td','style','script');
 
 
@@ -285,7 +285,9 @@
 
             // ignore not allowed attributes, except data-*
             if (!empty($allowed)) {
-                if (!isset($allowed_f[$key]) && @substr_compare($key, 'data-', 0, 5) !== 0) {
+                $is_data_attr = @substr_compare($key, 'data-', 0, 5) === 0;
+                $is_aria_attr = @substr_compare($key, 'aria-', 0, 5) === 0;
+                if (!$is_aria_attr && !isset($allowed_f[$key]) && (!$is_data_attr || !isset($allowed_f['data-*']))) {
                     continue;
                 }
             }
@@ -835,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);
@@ -888,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['*'];
     }
 
     /**
@@ -897,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['*'];
     }
 
 }
diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc
index 2da9910..e7d385f 100644
--- a/program/localization/en_US/labels.inc
+++ b/program/localization/en_US/labels.inc
@@ -157,16 +157,24 @@
 $labels['back']             = 'Back';
 $labels['options']          = 'Options';
 
+$labels['first'] = 'First';
+$labels['last'] = 'Last';
+$labels['previous'] = 'Previous';
+$labels['next'] = 'Next';
 $labels['select'] = 'Select';
 $labels['all'] = 'All';
 $labels['none'] = 'None';
 $labels['currpage'] = 'Current page';
+$labels['isread'] = 'Read';
 $labels['unread'] = 'Unread';
 $labels['flagged'] = 'Flagged';
+$labels['unflagged'] = 'Not Flagged';
 $labels['unanswered'] = 'Unanswered';
 $labels['withattachment'] = 'With attachment';
 $labels['deleted'] = 'Deleted';
 $labels['undeleted'] = 'Not deleted';
+$labels['replied'] = 'Replied';
+$labels['forwarded'] = 'Forwarded';
 $labels['invert'] = 'Invert';
 $labels['filter'] = 'Filter';
 $labels['list'] = 'List';
@@ -259,6 +267,7 @@
 $labels['uploadprogress'] = '$percent ($current from $total)';
 $labels['close']  = 'Close';
 $labels['messageoptions']  = 'Message options...';
+$labels['togglecomposeoptions'] = 'Toggle composition options';
 
 $labels['low']     = 'Low';
 $labels['lowest']  = 'Lowest';
@@ -348,7 +357,9 @@
 $labels['editcontact'] = 'Edit contact';
 $labels['contacts'] = 'Contacts';
 $labels['contactproperties'] = 'Contact properties';
+$labels['contactnameandorg'] = 'Name and Organization';
 $labels['personalinfo'] = 'Personal information';
+$labels['contactphoto'] = 'Contact photo';
 
 $labels['edit']   = 'Edit';
 $labels['cancel'] = 'Cancel';
@@ -372,6 +383,7 @@
 $labels['grouprename']    = 'Rename group';
 $labels['groupdelete']    = 'Delete group';
 $labels['groupremoveselected'] = 'Remove selected contacts from group';
+$labels['uponelevel']     = 'Up one level';
 
 $labels['previouspage']   = 'Show previous page';
 $labels['firstpage']      = 'Show first page';
@@ -468,6 +480,7 @@
 $labels['2047folding'] = 'Full RFC 2047 (other)';
 $labels['force7bit'] = 'Use MIME encoding for 8-bit characters';
 $labels['advancedoptions'] = 'Advanced options';
+$labels['toggleadvancedoptions'] = 'Toggle advanced options';
 $labels['focusonnewmessage'] = 'Focus browser window on new message';
 $labels['checkallfolders'] = 'Check all folders for new messages';
 $labels['displaynext'] = 'After message delete/move display the next message';
@@ -569,4 +582,48 @@
 $labels['korean'] = 'Korean';
 $labels['chinese'] = 'Chinese';
 
+// accessibility (voice-only) headings and descriptions
+$labels['arialabeltopnav'] = 'Window control';
+$labels['arialabeltasknav'] = 'Application tasks';
+$labels['arialabeltoolbar'] = 'Application toolbar';
+$labels['arialabelmessagessearchfilter'] = 'Email listing filter';
+$labels['arialabelmailsearchform'] = 'Email message search form';
+$labels['arialabelcontactsearchform'] = 'Contacts search form';
+$labels['arialabelmailquicksearchbox'] = 'Email search input';
+$labels['arialabelquicksearchbox'] = 'Search input';
+$labels['arialabelfolderlist'] = 'Email folder selection';
+$labels['arialabelmessagelist'] = 'Email Messages Listing';
+$labels['arialabelmailpreviewframe'] = 'Message preview';
+$labels['arialabelmailboxmenu'] = 'Folder actions menu';
+$labels['arialabellistselectmenu'] = 'List selection menu';
+$labels['arialabelthreadselectmenu'] = 'Threads listing menu';
+$labels['arialabelmessagelistoptions'] = 'Message list display and sorting options';
+$labels['arialabelmailimportdialog'] = 'Message import dialog';
+$labels['arialabelmessagenav'] = 'Message navigation';
+$labels['arialabelmessagebody'] = 'Message Body';
+$labels['arialabelmessageactions'] = 'Message actions';
+$labels['arialabelcontactquicksearch'] = 'Contacts search form';
+$labels['arialabelcontactsearchbox'] = 'Contact search input';
+$labels['arialabelmessageheaders'] = 'Message headers';
+$labels['arialabelcomposeoptions'] = 'Composition options';
+$labels['arialabelresponsesmenu'] = 'Canned responses menu';
+$labels['arialabelattachmentuploadform'] = 'Attachment upload form';
+$labels['arialabelattachmentpreview'] = 'Attachment preview';
+$labels['ariasummarycomposecontacts'] = 'List of contacts and groups to select as recipients';
+$labels['arialabelcontactexportoptions'] = 'Contact export options';
+$labels['arialabelabookgroupoptions'] = 'Addressbook/group options';
+$labels['arialabelpreferencesform'] = 'Preferences form';
+$labels['arialabelidentityeditfrom'] = 'Identity edit form';
+$labels['arialabelresonseeditfrom'] = 'Response edit form';
+
+$labels['helplistnavigation'] = 'List keyboard navigation';
+$labels['helplistkeyboardnavigation'] = "Arrows up/down: Move row focus/selection.
+Space: Select focused row.
+Shift + up/down: Select additional row above/below.
+Ctrl + Space: Add focused row to selection/remove from selection.";
+$labels['helplistkeyboardnavmessages'] = "Arrows right/left: expand/collapse message thread (in threads mode only).
+Enter: Open the selected/focused message.
+Delete: Move selected messages to Trash.";
+$labels['helplistkeyboardnavcontacts'] = "Enter: Open the selected/focused contact.";
+
 ?>
diff --git a/program/steps/addressbook/edit.inc b/program/steps/addressbook/edit.inc
index 3bbbfcc..0fc7530 100644
--- a/program/steps/addressbook/edit.inc
+++ b/program/steps/addressbook/edit.inc
@@ -98,12 +98,15 @@
 
 function rcmail_contact_edithead($attrib)
 {
+    global $RCMAIL;
+
     // check if we have a valid result
     $record = rcmail_get_edit_record();
     $i_size = !empty($attrib['size']) ? $attrib['size'] : 20;
 
     $form = array(
         'head' => array(
+            'name' => $RCMAIL->gettext('contactnameandorg'),
             'content' => array(
                 'prefix' => array('size' => $i_size),
                 'firstname' => array('size' => $i_size, 'visible' => true),
diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc
index be0dd2a..8955488 100644
--- a/program/steps/addressbook/func.inc
+++ b/program/steps/addressbook/func.inc
@@ -395,7 +395,7 @@
                         ), '&raquo;');
                     }
                     else
-                        $val = '&nbsp;';
+                        $val = '';
                     break;
 
                 default:
@@ -422,7 +422,7 @@
     unset($attrib['name']);
 
     $OUTPUT->add_gui_object('addresslist_title', $attrib['id']);
-    $OUTPUT->add_label('contacts');
+    $OUTPUT->add_label('contacts','uponelevel');
 
     return html::tag($attrib['tag'], $attrib, $RCMAIL->gettext($attrib['label']), html::$common_attrib);
 }
@@ -518,7 +518,7 @@
     foreach ($coltypes as $col => $prop) {
         if ($prop['subtypes']) {
             $subtype_names = array_map('rcmail_get_type_label', $prop['subtypes']);
-            $select_subtype = new html_select(array('name' => '_subtype_'.$col.'[]', 'class' => 'contactselectsubtype'));
+            $select_subtype = new html_select(array('name' => '_subtype_'.$col.'[]', 'class' => 'contactselectsubtype', 'title' => $prop['label'] . ' ' . $RCMAIL->gettext('type')));
             $select_subtype->add($subtype_names, $prop['subtypes']);
             $coltypes[$col]['subtypes_select'] = $select_subtype->show();
         }
@@ -607,7 +607,7 @@
                 // prepare subtype selector in edit mode
                 if ($edit_mode && is_array($colprop['subtypes'])) {
                     $subtype_names = array_map('rcmail_get_type_label', $colprop['subtypes']);
-                    $select_subtype = new html_select(array('name' => '_subtype_'.$col.'[]', 'class' => 'contactselectsubtype'));
+                    $select_subtype = new html_select(array('name' => '_subtype_'.$col.'[]', 'class' => 'contactselectsubtype', 'title' => $colprop['label'] . ' ' . $RCMAIL->gettext('type')));
                     $select_subtype->add($subtype_names, $colprop['subtypes']);
                 }
                 else
@@ -648,6 +648,8 @@
                 foreach ($values as $i => $val) {
                     if ($subtypes[$i])
                         $subtype = $subtypes[$i];
+
+                    $colprop['id'] = 'ff_' . $col . intval($coltypes[$field]['count']);
 
                     // render composite field
                     if ($colprop['type'] == 'composite') {
@@ -714,7 +716,7 @@
                     // display row with label
                     if ($label) {
                         $rows .= html::div('row',
-                            html::div('contactfieldlabel label', $select_subtype ? $select_subtype->show($subtype) : rcube::Q($label)) .
+                            html::div('contactfieldlabel label', $select_subtype ? $select_subtype->show($subtype) : html::label($colprop['id'], rcube::Q($label))) .
                             html::div('contactfieldcontent '.$colprop['type'], $val));
                     }
                     else   // row without label
@@ -803,7 +805,7 @@
     else
         $ff_value = '-del-'; // will disable delete-photo action
 
-    $img = html::img(array('src' => $photo_img, 'border' => 1, 'alt' => ''));
+    $img = html::img(array('src' => $photo_img, 'border' => 1, 'alt' => $RCMAIL->gettext('contactphoto')));
     $content = html::div($attrib, $img);
 
     if ($CONTACT_COLTYPES['photo'] && ($RCMAIL->action == 'edit' || $RCMAIL->action == 'add')) {
diff --git a/program/steps/addressbook/show.inc b/program/steps/addressbook/show.inc
index f4224a3..4471ea6 100644
--- a/program/steps/addressbook/show.inc
+++ b/program/steps/addressbook/show.inc
@@ -60,6 +60,7 @@
 
     $form = array(
         'head' => array(  // section 'head' is magic!
+            'name' => $RCMAIL->gettext('contactnameandorg'),
             'content' => array(
                 'prefix' => array('type' => 'text'),
                 'firstname' => array('type' => 'text'),
diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc
index 6baf6e7..0257f31 100644
--- a/program/steps/mail/compose.inc
+++ b/program/steps/mail/compose.inc
@@ -969,7 +969,7 @@
         $OUTPUT->set_env('spellcheck_langs', join(',', $editor_lang_set));
     }
 
-    $out .= "\n".'<iframe name="savetarget" src="program/resources/blank.gif" style="width:0;height:0;border:none;visibility:hidden;"></iframe>';
+    $out .= "\n".'<iframe name="savetarget" src="program/resources/blank.gif" style="width:0;height:0;border:none;visibility:hidden;" aria-hidden="true"></iframe>';
 
     return $out;
 }
@@ -1859,9 +1859,10 @@
     foreach ($RCMAIL->get_compose_responses(true) as $response) {
         $key = $response['key'];
         $item = html::a(array(
-            'href '=> '#'.urlencode($response['name']),
+            'href' => '#'.urlencode($response['name']),
             'class' => rtrim('insertresponse ' . $attrib['itemclass']),
             'unselectable' => 'on',
+            'tabindex' => '0',
             'rel' => $key,
         ), rcube::Q($response['name']));
 
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index 50b1e82..0dba3c1 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -111,7 +111,9 @@
     if (!$OUTPUT->ajax_call) {
         $OUTPUT->add_label('checkingmail', 'deletemessage', 'movemessagetotrash',
             'movingmessage', 'copyingmessage', 'deletingmessage', 'markingmessage',
-            'copy', 'move', 'quota', 'replyall', 'replylist', 'stillsearching');
+            'copy', 'move', 'quota', 'replyall', 'replylist', 'stillsearching',
+            'flagged', 'unflagged', 'unread', 'deleted', 'replied', 'forwarded',
+            'priority', 'withattachment');
     }
 
     $pagetitle = $RCMAIL->localize_foldername($mbox_name, true);
@@ -531,14 +533,19 @@
         $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')";
-        if ($attrib['optionsmenuicon'] === true || $attrib['optionsmenuicon'] == 'true')
-            $list_menu = html::div(array('onclick' => $onclick, 'class' => 'listmenu',
-                'id' => 'listmenulink', 'title' => $RCMAIL->gettext('listoptions')));
-        else
-            $list_menu = html::a(array('href' => '#', 'onclick' => $onclick),
-                html::img(array('src' => $skin_path . $attrib['optionsmenuicon'],
-                    'id' => 'listmenulink', 'title' => $RCMAIL->gettext('listoptions'))));
+        $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')));
+        }
+        $list_menu = html::a(array(
+            'href' => '#list-options',
+            'onclick' => $onclick,
+            'class' => 'listmenu',
+            'id' => 'listmenulink',
+            'title' => $RCMAIL->gettext('listoptions'),
+            'tabindex' => '0',
+        ), $inner);
     }
     else {
         $list_menu = '';
@@ -558,12 +565,14 @@
         // get column name
         switch ($col) {
         case 'flag':
-            $col_name = html::span('flagged', '&nbsp;');
+            $col_name = html::span('flagged', $RCMAIL->gettext('flagged'));
             break;
         case 'attachment':
         case 'priority':
+            $col_name = html::span($col, $RCMAIL->gettext($col));
+            break;
         case 'status':
-            $col_name = html::span($col, '&nbsp;');
+            $col_name = html::span($col, $RCMAIL->gettext('readstatus'));
             break;
         case 'threads':
             $col_name = $list_menu;
diff --git a/program/steps/mail/list_contacts.inc b/program/steps/mail/list_contacts.inc
index 0ee8113..4f17bef 100644
--- a/program/steps/mail/list_contacts.inc
+++ b/program/steps/mail/list_contacts.inc
@@ -110,7 +110,7 @@
             $keyname = $row['_type'] == 'group' ? 'contactgroup' : 'contact';
 
             $OUTPUT->command('add_contact_row', $row_id, array(
-                $keyname => html::span(array('title' => $email), rcube::Q($name ? $name : $email) .
+                $keyname => html::a(array('title' => $email), rcube::Q($name ? $name : $email) .
                     ($name && count($emails) > 1 ? '&nbsp;' . html::span('email', rcube::Q($email)) : '')
                 )), $classname);
         }
diff --git a/program/steps/mail/search_contacts.inc b/program/steps/mail/search_contacts.inc
index d565816..ccef32d 100644
--- a/program/steps/mail/search_contacts.inc
+++ b/program/steps/mail/search_contacts.inc
@@ -87,7 +87,7 @@
             $row_id = $row['ID'].'-'.$i;
             $jsresult[$row_id] = format_email_recipient($email, $name);
             $OUTPUT->command('add_contact_row', $row_id, array(
-                'contact' => html::span(array('title' => $email), rcube::Q($name ? $name : $email) .
+                'contact' => html::a(array('title' => $email), rcube::Q($name ? $name : $email) .
                     ($name && count($emails) > 1 ? '&nbsp;' . html::span('email', rcube::Q($email)) : '')
                 )), 'person');
         }
diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc
index f1c10da..0ebdd62 100644
--- a/program/steps/mail/show.inc
+++ b/program/steps/mail/show.inc
@@ -199,6 +199,7 @@
     if (sizeof($MESSAGE->attachments)) {
         foreach ($MESSAGE->attachments as $attach_prop) {
             $filename = rcmail_attachment_name($attach_prop, true);
+            $size = '';
 
             if ($PRINT_MODE) {
                 $size = $RCMAIL->message_part_size($attach_prop);
@@ -213,6 +214,10 @@
                     $title = '';
                 }
 
+                if ($attach_prop->size) {
+                    $size = ' ' . html::span('attachment-size', '(' . $RCMAIL->show_bytes($attach_prop->size) . ')');
+                }
+
                 $mimetype = rcmail_fix_mimetype($attach_prop->mimetype);
                 $class    = rcube_utils::file2class($mimetype, $filename);
                 $id       = 'attach' . $attach_prop->mime_id;
@@ -222,7 +227,7 @@
                         rcmail_output::JS_OBJECT_NAME, $attach_prop->mime_id),
                     'onmouseover' => $title ? '' : 'rcube_webmail.long_subject_title_ex(this, 0)',
                     'title'       => rcube::Q($title),
-                    ), rcube::Q($filename));
+                    ), rcube::Q($filename) . $size);
 
                 $ol .= html::tag('li', array('class' => $class, 'id' => $id), $link);
 
diff --git a/program/steps/settings/edit_folder.inc b/program/steps/settings/edit_folder.inc
index 6b7bd08..c61ac6d 100644
--- a/program/steps/settings/edit_folder.inc
+++ b/program/steps/settings/edit_folder.inc
@@ -132,6 +132,7 @@
         }
 
         $select = $RCMAIL->folder_selector(array(
+            'id'          => '_parent',
             'name'        => '_parent',
             'noselection' => '---',
             'realnames'   => false,
@@ -155,7 +156,7 @@
 
     // Settings: threading
     if ($threading_supported && ($mbox_imap == 'INBOX' || (!$options['noselect'] && !$options['is_root']))) {
-        $select = new html_select(array('name' => '_viewmode', 'id' => '_listmode'));
+        $select = new html_select(array('name' => '_viewmode', 'id' => '_viewmode'));
         $select->add($RCMAIL->gettext('list'), 0);
         $select->add($RCMAIL->gettext('threads'), 1);
 
diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc
index 4b4575f..8f9cf09 100644
--- a/program/steps/settings/func.inc
+++ b/program/steps/settings/func.inc
@@ -343,7 +343,7 @@
                         if (is_array($meta) && $meta['name']) {
                             $skinname     = $meta['name'];
                             $author_link  = $meta['url'] ? html::a(array('href' => $meta['url'], 'target' => '_blank'), rcube::Q($meta['author'])) : rcube::Q($meta['author']);
-                            $license_link = $meta['license-url'] ? html::a(array('href' => $meta['license-url'], 'target' => '_blank'), rcube::Q($meta['license'])) : rcube::Q($meta['license']);
+                            $license_link = $meta['license-url'] ? html::a(array('href' => $meta['license-url'], 'target' => '_blank', 'tabindex' => '-1'), rcube::Q($meta['license'])) : rcube::Q($meta['license']);
                         }
 
                         $skinnames[] = mb_strtolower($skinname);
diff --git a/program/steps/settings/responses.inc b/program/steps/settings/responses.inc
index 06093b3..ddd1924 100644
--- a/program/steps/settings/responses.inc
+++ b/program/steps/settings/responses.inc
@@ -95,7 +95,7 @@
 {
     global $RCMAIL, $OUTPUT;
 
-    $attrib += array('id' => 'rcmresponseslist', 'tagname' => 'table', 'cols' => 1);
+    $attrib += array('id' => 'rcmresponseslist', 'tagname' => 'table');
 
     $plugin = $RCMAIL->plugins->exec_hook('responses_list', array(
         'list' => $RCMAIL->get_compose_responses(true),
diff --git a/skins/classic/addressbook.css b/skins/classic/addressbook.css
index ebf9ab9..3005258 100644
--- a/skins/classic/addressbook.css
+++ b/skins/classic/addressbook.css
@@ -279,6 +279,11 @@
 	padding: 0;
 }
 
+#contacthead > legend
+{
+	display: none;
+}
+
 #contacthead .names span.namefield,
 #contacthead .names input
 {
diff --git a/skins/classic/common.css b/skins/classic/common.css
index 813df9e..cc1f3e6 100644
--- a/skins/classic/common.css
+++ b/skins/classic/common.css
@@ -609,6 +609,7 @@
 
 /***** common table settings ******/
 
+table.records-table thead tr th,
 table.records-table thead tr td
 {
   height: 20px;
@@ -619,6 +620,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
@@ -629,7 +631,8 @@
   white-space: nowrap;
   border-bottom: 1px solid #EBEBEB;
   overflow: hidden;
-  text-align: left;  
+  text-align: left;
+	outline: none;
 }
 
 table.records-table tr
diff --git a/skins/classic/mail.css b/skins/classic/mail.css
index a0d1f17..4c4e5d9 100644
--- a/skins/classic/mail.css
+++ b/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;
@@ -780,6 +783,7 @@
   -o-text-overflow: ellipsis;
   border-bottom: 1px solid #EBEBEB;
   cursor: default;
+  outline: none;
 }
 
 .messagelist tbody tr td a
@@ -803,40 +807,42 @@
   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;
+  text-indent: -5000px;
+  overflow: hidden;
 }
 
 .messagelist tr td div.collapsed,
 .messagelist tr td div.expanded,
-.messagelist tr td.threads div.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;
@@ -845,99 +851,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;
 }
@@ -954,10 +960,12 @@
   cursor: pointer;
 }
 
-.messagelist tr td.threads div.listmenu
+.messagelist tr > .threads .listmenu
 {
   background-position: 0 -238px;
   cursor: pointer;
+  overflow: hidden;
+  text-indent: -5000px;
 }
 
 .messagelist tbody tr td.subject
@@ -977,45 +985,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;
 }
@@ -1038,6 +1046,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;
@@ -1058,21 +1067,15 @@
 .messagelist tr.selected td
 {
   color: #FFFFFF;
-  background-color: #CC3333;
-}
-
-.messagelist tr.unfocused td
-{
-  color: #FFFFFF;
   background-color: #929292;
 }
 
-.messagelist tr.selected td a
+.messagelist.focus tr.selected td
 {
-  color: #FFFFFF;
+  background-color: #CC3333;
 }
 
-.messagelist tr.unfocused td a
+.messagelist tr.selected td a
 {
   color: #FFFFFF;
 }
diff --git a/skins/larry/addressbook.css b/skins/larry/addressbook.css
index bfdd681..0583ce0 100644
--- a/skins/larry/addressbook.css
+++ b/skins/larry/addressbook.css
@@ -138,56 +138,25 @@
 	background: url(images/listicons.png) -2px -1180px no-repeat;
 }
 
-/* This padding-left should be equal to the focused border-left + the focused padding-left */
-#contacts-table thead tr td:first-child,
-#contacts-table tbody tr td:first-child {
-	border-left: 0;
-	padding-left: 36px;
-}
-
-/* because of border-collapse, we make the left border twice what we want it to be - half will be hidden to the left */
-#contacts-table tbody tr.focused > td:first-child {
-	border-left: 2px solid #b0ccd7;
-	padding-left: 34px;
-}
-
-#contacts-table tbody tr.selected.focused > td:first-child {
-	border-left-color: #9ec2d0;
-}
-
 #contacts-table .contact td.name {
-	background-position: 6px -1603px;
-}
-
-#contacts-table .contact.focused td.name {
 	background-position: 4px -1603px;
 }
 
-#contacts-table .contact.selected td.name,
-#contacts-table .contact.unfocused td.name {
-	background-position: 6px -1627px;
-	font-weight: bold;
-}
-
-#contacts-table .contact.selected.focused td.name {
+#contacts-table .contact.selected td.name {
 	background-position: 4px -1627px;
+	font-weight: bold;
 }
 
 #contacts-table .group td.name {
-	background-position: 6px -1555px;
-}
-
-#contacts-table .group.focused td.name {
 	background-position: 4px -1555px;
 }
 
-#contacts-table .group.selected td.name,
-#contacts-table .group.unfocused td.name {
-	background-position: 6px -1579px;
+#contacts-table .group.selected td.name {
+	background-position: 4px -1579px;
 	font-weight: bold;
 }
 
-#contacts-table .group.selected.focused td.name {
+#contacts-table.focus .group.selected.focused td.name {
 	background-position: 4px -1579px;
 }
 
@@ -232,6 +201,8 @@
 	float: left;
 	margin: 0 18px 20px 0;
 	width: 112px;
+	border: 0;
+	padding: 0;
 }
 
 #contactpic {
@@ -267,6 +238,10 @@
 	opacity: 0.05;
 }
 
+#contactphoto .formlinks a[aria-disabled='true'] {
+	visibility: hidden;
+}
+
 #contacthead {
 	border: 0;
 	margin: 0 16em 1em 0;
@@ -275,6 +250,10 @@
 	font-size: 12px;
 }
 
+#contacthead > legend {
+	display: none;
+}
+
 form #contacthead {
 	margin-right: 0;
 }
diff --git a/skins/larry/includes/footer.html b/skins/larry/includes/footer.html
index f421ec5..6cd3e62 100644
--- a/skins/larry/includes/footer.html
+++ b/skins/larry/includes/footer.html
@@ -6,6 +6,7 @@
 var UI = new rcube_mail_ui();
 $(document).ready(function(){
 	UI.set('errortitle', '<roundcube:label name="errortitle" quoting="javascript" />');
+	UI.set('toggleoptions', '<roundcube:label name="toggleadvancedoptions" quoting="javascript" />');
 	UI.init();
 });
 
diff --git a/skins/larry/includes/header.html b/skins/larry/includes/header.html
index 69e8b8a..179b860 100644
--- a/skins/larry/includes/header.html
+++ b/skins/larry/includes/header.html
@@ -1,5 +1,6 @@
 <div id="header">
-<div id="topline">
+<div id="topline" role="banner" aria-labelledby="aria-label-topnav">
+	<h2 id="aria-label-topnav" class="voice"><roundcube:label name="arialabeltopnav" /></h2>
 	<div class="topleft">
 		<roundcube:container name="topline-left" id="topline-left" />
 		<roundcube:button name="about" type="link" label="about" class="about-link" onclick="UI.show_about(this);return false" condition="!env:extwin" />
@@ -21,13 +22,14 @@
 
 <roundcube:if condition="!env:extwin &amp;&amp; !env:framed" />
 <div id="topnav">
-	<div id="taskbar" class="topright">
-	<roundcube:button command="mail" label="mail" class="button-mail" classSel="button-mail button-selected" innerClass="button-inner" />
-	<roundcube:button command="addressbook" label="addressbook" class="button-addressbook" classSel="button-addressbook button-selected" innerClass="button-inner" />
-	<roundcube:container name="taskbar" id="taskbar" />
-	<roundcube:button command="settings" label="settings" class="button-settings" classSel="button-settings button-selected" innerClass="button-inner" />
-	<roundcube:button command="logout" label="logout" class="button-logout" classSel="button-logout" innerClass="button-inner" />
-	<span class="minmodetoggle"></span>
+	<h2 id="aria-label-tasknav" class="voice"><roundcube:label name="arialabeltasknav" /></h2>
+	<div id="taskbar" class="topright" role="navigation" aria-labelledby="aria-label-tasknav">
+		<roundcube:button command="mail" label="mail" class="button-mail" classSel="button-mail button-selected" innerClass="button-inner" />
+		<roundcube:button command="addressbook" label="addressbook" class="button-addressbook" classSel="button-addressbook button-selected" innerClass="button-inner" />
+		<roundcube:container name="taskbar" id="taskbar" />
+		<roundcube:button command="settings" label="settings" class="button-settings" classSel="button-settings button-selected" innerClass="button-inner" />
+		<roundcube:button command="logout" label="logout" class="button-logout" classSel="button-logout" innerClass="button-inner" />
+		<span class="minmodetoggle" role="presentation"></span>
 	</div>
 	<roundcube:object name="logo" src="/images/roundcube_logo.png" id="toplogo" alt="Logo" onclick="if(window.rcmail)rcmail.command('switch-task','mail')" />
 </div>
diff --git a/skins/larry/includes/mailtoolbar.html b/skins/larry/includes/mailtoolbar.html
index ac08a32..691345b 100644
--- a/skins/larry/includes/mailtoolbar.html
+++ b/skins/larry/includes/mailtoolbar.html
@@ -3,11 +3,11 @@
 <roundcube:button command="reply" type="link" class="button reply disabled" classAct="button reply" classSel="button reply pressed" label="reply" title="replytomessage" />
 <span class="dropbutton">
 	<roundcube:button command="reply-all" type="link" class="button reply-all disabled" classAct="button reply-all" classSel="button reply-all pressed" label="replyall" title="replytoallmessage" />
-	<span class="dropbuttontip" id="replyallmenulink" onclick="UI.show_popup('replyallmenu');return false"></span>
+	<a href="#reply-all" class="dropbuttontip" id="replyallmenulink" onclick="UI.toggle_popup('replyallmenu',event);return false" aria-haspopup="true" aria-expanded="false" aria-owns="replyallmenu-menu" tabindex="0">Reply-all options</a>
 </span>
 <span class="dropbutton">
 	<roundcube:button command="forward" type="link" class="button forward disabled" classAct="button forward" classSel="button forward pressed" label="forward" title="forwardmessage" />
-	<span class="dropbuttontip" id="forwardmenulink" onclick="UI.show_popup('forwardmenu');return false"></span>
+	<a href="#forward" class="dropbuttontip" id="forwardmenulink" onclick="UI.toggle_popup('forwardmenu',event);return false" aria-haspopup="true" aria-expanded="false" aria-owns="forwardmenu-menu" tabindex="0">Forwarding options</a>
 </span>
 <roundcube:button command="delete" type="link" class="button delete disabled" classAct="button delete" classSel="button delete pressed" label="delete" title="deletemessage" />
 <roundcube:if condition="template:name == 'message'" />
@@ -15,44 +15,48 @@
 <roundcube:button command="print" type="link" class="button print disabled" classAct="button print" classSel="button print pressed" label="print" title="printmessage" />
 <roundcube:endif />
 <roundcube:container name="toolbar" id="mailtoolbar" />
-<roundcube:button name="markmenulink" id="markmessagemenulink" type="link" class="button markmessage" label="mark" title="markmessages" onclick="UI.show_popup('markmessagemenu');return false" />
-<roundcube:button name="messagemenulink" id="messagemenulink" type="link" class="button more" label="more" title="moreactions" onclick="UI.show_popup('messagemenu');return false" />
+<roundcube:button name="markmenulink" id="markmessagemenulink" type="link" class="button markmessage" label="mark" title="markmessages" onclick="UI.toggle_popup('markmessagemenu',event);return false" aria-haspopup="true" aria-expanded="false" aria-owns="markmessagemenu-menu" />
+<roundcube:button name="messagemenulink" id="messagemenulink" type="link" class="button more" label="more" title="moreactions" onclick="UI.toggle_popup('messagemenu',event);return false" aria-haspopup="true" aria-expanded="false" aria-owns="messagemenu-menu" />
 
-<div id="forwardmenu" class="popupmenu">
-	<ul class="toolbarmenu">
-		<li><roundcube:button command="forward-inline" label="forwardinline" prop="sub" classAct="forwardlink active" class="forwardlink" /></li>
-		<li><roundcube:button command="forward-attachment" label="forwardattachment" prop="sub" classAct="forwardattachmentlink active" class="forwardattachmentlink" /></li>
+<div id="forwardmenu" class="popupmenu" aria-hidden="true">
+	<h3 id="aria-label-forwardmenu" class="voice">Forwarding options</h3>
+	<ul id="forwardmenu-menu" class="toolbarmenu" role="menu" aria-labelledby="aria-label-forwardmenu">
+		<li role="menuitem"><roundcube:button command="forward-inline" label="forwardinline" prop="sub" classAct="forwardlink active" class="forwardlink" /></li>
+		<li role="menuitem"><roundcube:button command="forward-attachment" label="forwardattachment" prop="sub" classAct="forwardattachmentlink active" class="forwardattachmentlink" /></li>
 		<roundcube:container name="forwardmenu" id="forwardmenu" />
 	</ul>
 </div>
 
-<div id="replyallmenu" class="popupmenu">
-	<ul class="toolbarmenu">
-		<li><roundcube:button command="reply-all" label="replyall" prop="sub" class="replyalllink" classAct="replyalllink active" /></li>
-		<li><roundcube:button command="reply-list" label="replylist" prop="sub" class="replylistlink" classAct="replylistlink active" /></li>
+<div id="replyallmenu" class="popupmenu" aria-hidden="true">
+	<h3 id="aria-label-replyallmenu" class="voice">Reply-all options</h3>
+	<ul id="replyallmenu-menu" class="toolbarmenu" role="menu" aria-labelledby="aria-label-replyallmenu">
+		<li role="menuitem"><roundcube:button command="reply-all" label="replyall" prop="sub" class="replyalllink" classAct="replyalllink active" /></li>
+		<li role="menuitem"><roundcube:button command="reply-list" label="replylist" prop="sub" class="replylistlink" classAct="replylistlink active" /></li>
 		<roundcube:container name="replyallmenu" id="replyallmenu" />
 	</ul>
 </div>
 
-<div id="messagemenu" class="popupmenu">
-  <ul class="toolbarmenu iconized">
-	<li><roundcube:button command="print" label="printmessage" class="icon" classAct="icon active" innerclass="icon print" /></li>
-	<li><roundcube:button command="download" label="emlsave" class="icon" classAct="icon active" innerclass="icon download" /></li>
-	<li><roundcube:button command="edit" prop="new" label="editasnew" class="icon" classAct="icon active" innerclass="icon edit" /></li>
-	<li><roundcube:button command="viewsource" label="viewsource" class="icon" classAct="icon active" innerclass="icon viewsource" /></li>
-	<li><roundcube:button command="move" label="moveto" class="icon" classAct="icon active" innerclass="icon move folder-selector-link" /></li>
-	<li><roundcube:button command="copy" label="copyto" class="icon" classAct="icon active" innerclass="icon copy folder-selector-link" /></li>
-	<li><roundcube:button command="open" label="openinextwin" target="_blank" class="icon" classAct="icon active" innerclass="icon extwin" /></li>
+<div id="messagemenu" class="popupmenu" aria-hidden="true">
+  <h3 id="aria-label-messagemenu" class="voice">More message toolbar actions</h3>
+  <ul id="messagemenu-menu" class="toolbarmenu iconized" role="menu" aria-labelledby="aria-label-messagemenu">
+	<li role="menuitem"><roundcube:button command="print" label="printmessage" class="icon" classAct="icon active" innerclass="icon print" /></li>
+	<li role="menuitem"><roundcube:button command="download" label="emlsave" class="icon" classAct="icon active" innerclass="icon download" /></li>
+	<li role="menuitem"><roundcube:button command="edit" prop="new" label="editasnew" class="icon" classAct="icon active" innerclass="icon edit" /></li>
+	<li role="menuitem"><roundcube:button command="viewsource" label="viewsource" class="icon" classAct="icon active" innerclass="icon viewsource" /></li>
+	<li role="menuitem"><roundcube:button command="move" label="moveto" class="icon" classAct="icon active" innerclass="icon move folder-selector-link" /></li>
+	<li role="menuitem"><roundcube:button command="copy" label="copyto" class="icon" classAct="icon active" innerclass="icon copy folder-selector-link" /></li>
+	<li role="menuitem"><roundcube:button command="open" label="openinextwin" target="_blank" class="icon" classAct="icon active" innerclass="icon extwin" /></li>
 	<roundcube:container name="messagemenu" id="messagemenu" />
   </ul>
 </div>
 
-<div id="markmessagemenu" class="popupmenu">
-  <ul class="toolbarmenu iconized">
-	<li><roundcube:button command="mark" prop="read" label="markread" classAct="icon active" class="icon" innerclass="icon read" /></li>
-	<li><roundcube:button command="mark" prop="unread" label="markunread" classAct="icon active" class="icon" innerclass="icon unread" /></li>
-	<li><roundcube:button command="mark" prop="flagged" label="markflagged" classAct="icon active" class="icon" innerclass="icon flagged" /></li>
-	<li><roundcube:button command="mark" prop="unflagged" label="markunflagged" classAct="icon active" class="icon" innerclass="icon unflagged" /></li>
+<div id="markmessagemenu" class="popupmenu" aria-hidden="true">
+  <h3 id="aria-label-markmessagemenu" class="voice">Mark selected messages as...</h3>
+  <ul id="markmessagemenu-menu" class="toolbarmenu iconized" role="menu" aria-labelledby="aria-label-markmessagemenu">
+	<li role="menuitem"><roundcube:button command="mark" prop="read" label="markread" classAct="icon active" class="icon" innerclass="icon read" /></li>
+	<li role="menuitem"><roundcube:button command="mark" prop="unread" label="markunread" classAct="icon active" class="icon" innerclass="icon unread" /></li>
+	<li role="menuitem"><roundcube:button command="mark" prop="flagged" label="markflagged" classAct="icon active" class="icon" innerclass="icon flagged" /></li>
+	<li role="menuitem"><roundcube:button command="mark" prop="unflagged" label="markunflagged" classAct="icon active" class="icon" innerclass="icon unflagged" /></li>
 	<roundcube:container name="markmenu" id="markmessagemenu" />
   </ul>
 </div>
diff --git a/skins/larry/includes/settingstabs.html b/skins/larry/includes/settingstabs.html
index e626958..d43e8f0 100644
--- a/skins/larry/includes/settingstabs.html
+++ b/skins/larry/includes/settingstabs.html
@@ -1,7 +1,9 @@
-<div id="settings-sections" class="uibox listbox">
-<h2 class="boxtitle"><roundcube:label name="settings" /></h2>
+<div id="settings-sections" class="uibox listbox" role="navigation" aria-labelledby="aria-label-settingstabs">
+<h2 class="boxtitle" id="aria-label-settingstabs"><roundcube:label name="settings" /></h2>
 <div id="settings-tabs" class="scroller">
-	<roundcube:object name="settingstabs" class="listitem" />
+	<ul class="listing iconized">
+		<roundcube:object name="settingstabs" class="listitem" tagname="li" />
+	</ul>
 	<roundcube:container name="tabs" id="settings-tabs" />
 </div>
 </div>
diff --git a/skins/larry/mail.css b/skins/larry/mail.css
index e258cad..b82fb79 100644
--- a/skins/larry/mail.css
+++ b/skins/larry/mail.css
@@ -162,6 +162,7 @@
 	padding-right: 36px;
 }
 
+#mailboxlist li.mailbox > a:focus,
 #mailboxlist li.mailbox.selected > a {
 	background-position: 6px -21px;
 }
@@ -170,6 +171,7 @@
 	background-position: 6px -189px;
 }
 
+#mailboxlist li.mailbox.inbox > a:focus,
 #mailboxlist li.mailbox.inbox.selected > a {
 	background-position: 6px -213px;
 }
@@ -178,6 +180,7 @@
 	background-position: 6px -238px;
 }
 
+#mailboxlist li.mailbox.drafts > a:focus,
 #mailboxlist li.mailbox.drafts.selected > a {
 	background-position: 6px -262px;
 }
@@ -186,6 +189,7 @@
 	background-position: 6px -286px;
 }
 
+#mailboxlist li.mailbox.sent > a:focus,
 #mailboxlist li.mailbox.sent.selected > a {
 	background-position: 6px -310px;
 }
@@ -194,6 +198,7 @@
 	background-position: 6px -334px;
 }
 
+#mailboxlist li.mailbox.junk > a:focus,
 #mailboxlist li.mailbox.junk.selected > a {
 	background-position: 6px -358px;
 }
@@ -202,6 +207,7 @@
 	background-position: 6px -382px;
 }
 
+#mailboxlist li.mailbox.trash > a:focus,
 #mailboxlist li.mailbox.trash.selected > a {
 	background-position: 6px -406px;
 }
@@ -210,6 +216,7 @@
 	background-position: 6px -1924px;
 }
 
+#mailboxlist li.mailbox.trash.empty > a:focus,
 #mailboxlist li.mailbox.trash.empty.selected > a {
 	background-position: 6px -1948px;
 }
@@ -218,6 +225,7 @@
 	background-position: 6px -1699px;
 }
 
+#mailboxlist li.mailbox.archive > a:focus,
 #mailboxlist li.mailbox.archive.selected > a {
 	background-position: 6px -1723px;
 }
@@ -226,6 +234,7 @@
 	background-position: 23px -238px;
 }
 
+#mailboxlist li.mailbox ul li.drafts > a:focus,
 #mailboxlist li.mailbox ul li.drafts.selected > a {
 	background-position: 23px -262px;
 }
@@ -234,6 +243,7 @@
 	background-position: 23px -286px;
 }
 
+#mailboxlist li.mailbox ul li.sent > a:focus,
 #mailboxlist li.mailbox ul li.sent.selected > a {
 	background-position: 23px -310px;
 }
@@ -242,6 +252,7 @@
 	background-position: 23px -334px;
 }
 
+#mailboxlist li.mailbox ul li.junk > a:focus,
 #mailboxlist li.mailbox ul li.junk.selected > a {
 	background-position: 23px -358px;
 }
@@ -250,6 +261,7 @@
 	background-position: 23px -382px;
 }
 
+#mailboxlist li.mailbox ul li.trash > a:focus,
 #mailboxlist li.mailbox ul li.trash.selected > a {
 	background-position: 23px -406px;
 }
@@ -258,6 +270,7 @@
 	background-position: 23px -1924px;
 }
 
+#mailboxlist li.mailbox ul li.trash.empty > a:focus,
 #mailboxlist li.mailbox ul li.trash.empty.selected > a {
 	background-position: 23px -1948px;
 }
@@ -266,6 +279,7 @@
 	background-position: 23px -1699px;
 }
 
+#mailboxlist li.mailbox ul li.archive > a:focus,
 #mailboxlist li.mailbox ul li.archive.selected > a {
 	background-position: 23px -1723px;
 }
@@ -304,6 +318,7 @@
 	padding-left: 52px;  /* 36 + 1 x 16 */
 	background-position: 22px -95px;  /* 6 + 1 x 16 */
 }
+#mailboxlist li.mailbox ul li > a:focus,
 #mailboxlist li.mailbox ul li.selected > a {
 	background-position: 22px -119px;
 }
@@ -316,6 +331,7 @@
 	padding-left: 68px;  /* 2x */
 	background-position: 38px -95px;
 }
+#mailboxlist li.mailbox ul ul li > a:focus,
 #mailboxlist li.mailbox ul ul li.selected > a {
 	background-position: 38px -119px;
 }
@@ -327,6 +343,7 @@
 	padding-left: 84px;  /* 3x */
 	background-position: 54px -95px;
 }
+#mailboxlist li.mailbox ul ul ul li > a:focus,
 #mailboxlist li.mailbox ul ul ul li.selected > a {
 	background-position: 54px -119px;
 }
@@ -338,6 +355,7 @@
 	padding-left: 100px;  /* 4x */
 	background-position: 70px -95px;
 }
+#mailboxlist li.mailbox ul ul ul ul li > a:focus,
 #mailboxlist li.mailbox ul ul ul ul li.selected > a {
 	background-position: 70px -119px;
 }
@@ -464,66 +482,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;
 }
 
@@ -540,19 +558,22 @@
 /*	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: url(images/listicons.png) right -912px no-repeat;
+	background-image: url(images/listicons.png);
+	background-repeat: no-repeat;
+	background-position: right -912px;
 }
 
-.messagelist thead tr td.sortedASC a {
+.messagelist thead tr th.sortedASC a {
 	background-position: right -944px;
 }
 
@@ -574,39 +595,41 @@
 	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;
+	text-indent: -5000px;
+	overflow: hidden;
 }
 
 .messagelist tr td div.collapsed,
 .messagelist tr td div.expanded,
-.messagelist tr td.threads div.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;
@@ -619,7 +642,7 @@
 	background-position: 0 -996px;
 }
 
-.messagelist thead tr td.attachment span.attachment {
+.messagelist thead tr th.attachment span.attachment {
 	background-position: -24px -997px;
 }
 
@@ -627,7 +650,7 @@
 	background-position: -24px -1116px;
 }
 
-.messagelist thead tr td.priority span.priority {
+.messagelist thead tr th.priority span.priority {
 	background-position: -24px -1845px;
 }
 
@@ -651,15 +674,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;
 }
 
@@ -702,10 +725,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;
 }
 
@@ -719,13 +742,23 @@
 	cursor: pointer;
 }
 
-.messagelist tr td.threads div.listmenu {
-	background-position: 0 -976px;
+.messagelist tr th.threads .listmenu {
+	background-position: 4px -972px;
 	cursor: pointer;
-	width: 26px;
+	width: 24px;
+	height: 21px;
+	overflow: hidden;
+	text-indent: -5000px;
+	margin: -3px -5px -2px -6px;
+	padding: 3px  5px  2px  6px;
 }
 
-.messagelist thead tr td.subject,
+.messagelist tr th.threads .listmenu:focus {
+	background-color: rgba(73,180,210,0.7);
+	outline: none;
+}
+
+.messagelist thead tr th.subject,
 .messagelist tbody tr td.subject {
 	width: 99%;
 	white-space: nowrap;
@@ -907,6 +940,16 @@
 	background: linear-gradient(left, #fbfbfb 0, #e9e9e9 100%);
 	border-right: 1px solid #dfdfdf;
 	border-radius: 3px 0 0 0; /* for Opera */
+}
+
+.moreheaderstoggle:focus {
+	background: #f2f2f2;
+	background: -moz-linear-gradient(left, #66bcd9 0, #49b3d2 100%);
+	background: -webkit-gradient(linear, left top, right top, color-stop(0,#66bcd9), color-stop(100%,#49b3d2));
+	background: -o-linear-gradient(left, #66bcd9 0, #49b3d2 100%);
+	background: -ms-linear-gradient(left, #66bcd9 0, #49b3d2 100%);
+	background: linear-gradient(left, #66bcd9 0, #49b3d2 100%);
+	border-right-color: #149cc5;
 }
 
 .moreheaderstoggle .iconlink {
@@ -1303,11 +1346,17 @@
 	margin-left: 0.5em;
 }
 
-#compose-contacts li a, #contacts-table td {
-	background: url(images/listicons.png) -100px 0 no-repeat;
+#compose-contacts li a,
+#contacts-table td {
+	background-image: url(images/listicons.png);
+	background-position: -100px 0;
+	background-repeat: no-repeat;
 	overflow: hidden;
-	padding-left: 36px;
 	text-overflow: ellipsis;
+}
+
+#compose-contacts li a {
+	padding-left: 36px;
 }
 
 #contacts-table td.contactgroup a {
@@ -1331,6 +1380,7 @@
 	background-position: 6px -766px;
 }
 
+#compose-contacts li.addressbook a:focus,
 #compose-contacts li.addressbook.selected a {
 	background-position: 6px -791px;
 }
@@ -1339,20 +1389,36 @@
 	background-position: 6px -1555px;
 }
 
+#contacts-table.focus tr.focused td.contactgroup {
+	background-position: 4px -1555px;
+}
+
 #contacts-table tr.unfocused td.contactgroup,
 #contacts-table tr.selected td.contactgroup {
 	background-position: 6px -1579px;
+}
+
+#contacts-table.focus tr.selected.focused td.contactgroup {
+	background-position: 4px -1579px;
 }
 
 #contacts-table td.contact {
 	background-position: 6px -1603px;
 }
 
+#contacts-table.focus tr.focused td.contact {
+	background-position: 4px -1603px;
+}
+
 #contacts-table tr.unfocused td.contact,
 #contacts-table tr.selected td.contact {
 	background-position: 6px -1627px;
 }
 
+#contacts-table.focus tr.selected.focused td.contact {
+	background-position: 4px -1627px;
+}
+
 #compose-content {
 	position: absolute;
 	top: 0;
diff --git a/skins/larry/settings.css b/skins/larry/settings.css
index 6d4d13c..1ac62cd 100644
--- a/skins/larry/settings.css
+++ b/skins/larry/settings.css
@@ -70,7 +70,7 @@
 	width: 20px;
 	height: 18px;
 	background: url('images/listicons.png') 0 -1157px no-repeat;
-	text-indent: 1000px;
+	text-indent: -5000px;
 	overflow: hidden;
 }
 
@@ -78,9 +78,10 @@
 	background-position: -24px -1137px;
 }
 
-#sections-table tbody td.section,
-#settings-sections span.listitem a,
-#settings-sections span.tablink a {
+#sections-table tbody td,
+#sections-table .listitem span,
+#settings-sections .listitem a,
+#settings-sections .tablink a {
 	padding-left: 36px;
 	background-image: url(images/listicons.png);
 	background-position: -100px 0;
@@ -88,120 +89,120 @@
 }
 
 /* note: support span.tablink because this is used by plugins */
-#settings-sections span.listitem a,
-#settings-sections span.tablink a {
+#settings-sections .listitem a,
+#settings-sections .tablink a {
 	background-position: 6px -862px;
 }
 
-#settings-sections span.selected a,
-#settings-sections span.tablink.selected a {
+#settings-sections .selected a,
+#settings-sections .tablink.selected a {
 	background-position: 6px -887px;
 }
 
-#settings-sections span.preferences a {
+#settings-sections .preferences a {
 	background-position: 6px -431px;
 }
 
-#settings-sections span.preferences.selected a {
+#settings-sections .preferences.selected a {
 	background-position: 6px -455px;
 }
 
-#settings-sections span.folders a,
-#sections-table #rcmrowfolders td.section {
+#settings-sections .folders a,
+#sections-table #rcmrowfolders .section {
 	background-position: 6px 2px;
 }
 
-#settings-sections span.folders.selected a,
-#sections-table #rcmrowfolders.selected td.section {
+#settings-sections .folders.selected a,
+#sections-table #rcmrowfolders.selected .section {
 	background-position: 6px -22px;
 }
 
-#settings-sections span.identities a {
+#settings-sections .identities a {
 	background-position: 6px -478px;
 }
 
-#settings-sections span.identities.selected a {
+#settings-sections .identities.selected a {
 	background-position: 6px -502px;
 }
 
-#settings-sections span.filter a {
+#settings-sections .filter a {
 	background-position: 6px -1746px;
 }
 
-#settings-sections span.filter.selected a {
+#settings-sections .filter.selected a {
 	background-position: 6px -1770px;
 }
 
-#settings-sections span.password a {
+#settings-sections .password a {
 	background-position: 6px -1795px;
 }
 
-#settings-sections span.password.selected a {
+#settings-sections .password.selected a {
 	background-position: 6px -1819px;
 }
 
-#settings-sections span.responses a {
+#settings-sections .responses a {
 	background-position: 6px -1972px;
 }
 
-#settings-sections span.responses.selected a {
+#settings-sections .responses.selected a {
 	background-position: 6px -1996px;
 }
 
-#sections-table #rcmrowgeneral td.section {
-	background-position: 6px -573px;
+#sections-table #rcmrowgeneral .section {
+	background-position: 4px -573px;
 }
 
-#sections-table #rcmrowgeneral.selected td.section {
-	background-position: 6px -598px;
+#sections-table #rcmrowgeneral.selected .section {
+	background-position: 4px -598px;
 }
 
-#sections-table #rcmrowmailbox td.section {
-	background-position: 6px -621px;
+#sections-table #rcmrowmailbox .section {
+	background-position: 4px -621px;
 }
 
-#sections-table #rcmrowmailbox.selected td.section {
-	background-position: 6px -646px;
+#sections-table #rcmrowmailbox.selected .section {
+	background-position: 4px -646px;
 }
 
-#sections-table #rcmrowcompose td.section {
-	background-position: 6px -670px;
+#sections-table #rcmrowcompose .section {
+	background-position: 4px -670px;
 }
 
-#sections-table #rcmrowcompose.selected td.section {
-	background-position: 6px -695px;
+#sections-table #rcmrowcompose.selected .section {
+	background-position: 4px -695px;
 }
 
-#sections-table #rcmrowmailview td.section {
-	background-position: 6px -718px;
+#sections-table #rcmrowmailview .section {
+	background-position: 4px -718px;
 }
 
-#sections-table #rcmrowmailview.selected td.section {
-	background-position: 6px -742px;
+#sections-table #rcmrowmailview.selected .section {
+	background-position: 4px -742px;
 }
 
-#sections-table #rcmrowaddressbook td.section {
-	background-position: 6px -766px;
+#sections-table #rcmrowaddressbook .section {
+	background-position: 4px -766px;
 }
 
-#sections-table #rcmrowaddressbook.selected td.section {
-	background-position: 6px -791px;
+#sections-table #rcmrowaddressbook.selected .section {
+	background-position: 4px -791px;
 }
 
-#sections-table #rcmrowserver td.section {
-	background-position: 6px -814px;
+#sections-table #rcmrowserver .section {
+	background-position: 4px -814px;
 }
 
-#sections-table #rcmrowserver.selected td.section {
-	background-position: 6px -838px;
+#sections-table #rcmrowserver.selected .section {
+	background-position: 4px -838px;
 }
 
-#sections-table #rcmrowcalendar td.section {
-	background-position: 6px -526px;
+#sections-table #rcmrowcalendar .section {
+	background-position: 4px -526px;
 }
 
-#sections-table #rcmrowcalendar.selected td.section {
-	background-position: 6px -550px;
+#sections-table #rcmrowcalendar.selected .section {
+	background-position: 4px -550px;
 }
 
 #folderslist,
diff --git a/skins/larry/styles.css b/skins/larry/styles.css
index d1e4501..17543c8 100644
--- a/skins/larry/styles.css
+++ b/skins/larry/styles.css
@@ -35,6 +35,10 @@
   border: 0;
 }
 
+.voice {
+	display: none;
+}
+
 input[type="text"],
 input[type="password"],
 textarea {
@@ -310,6 +314,16 @@
 	box-shadow: inset 0 1px 2px 0 #555;
 	border-right-color: #555;
 	border-left-color: #555;
+}
+
+.buttongroup a.button:focus,
+.buttongroup a.button.selected:focus {
+	background: #f2f2f2;
+	background: -moz-linear-gradient(top, #49b3d2 0, #66bcd9 100%);
+	background: -webkit-gradient(linear, left top, left bottom, color-stop(0,#49b3d2), color-stop(100%,#66bcd9));
+	background: -o-linear-gradient(top, #49b3d2 0, #66bcd9 100%);
+	background: -ms-linear-gradient(top, #49b3d2 0, #66bcd9 100%);
+	background: linear-gradient(top, #49b3d2 0, #66bcd9 100%);
 }
 
 .pagenav a.button {
@@ -950,6 +964,13 @@
 	background: url(images/buttons.png) -1000px 0 no-repeat;
 }
 
+#taskbar a:focus {
+	color: #fff;
+	text-shadow: 0px 1px 1px #666;
+	background-color: rgba(73,180,210,0.7);
+	outline: none;
+}
+
 #taskbar a.button-selected {
 	color: #3cf;
 	background-color: #2c2c2c;
@@ -1115,6 +1136,7 @@
 }
 
 .boxtitle,
+.uibox .listing thead th,
 .uibox .listing thead td {
 	font-size: 12px;
 	font-weight: bold;
@@ -1126,7 +1148,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;
@@ -1145,6 +1174,7 @@
 }
 
 .listbox .listitem a,
+.listbox .listitem span,
 .listbox .tablink a,
 .listing tbody td,
 .listing li a {
@@ -1162,17 +1192,40 @@
 	display: table-cell;
 	height: auto;
 	min-height: 14px;
+	outline: none;
+}
+
+.listing tbody td a {
+	color: #376572;
+	text-shadow: 0px 1px 1px #fff;
+	text-decoration: none;
 }
 
 .webkit .listing tbody td {
 	height: 14px;
 }
 
+/* This padding-left minus the focused padding left should be half of the focused border-left */
+.listing thead tr td:first-child,
+.listing tbody tr td:first-child {
+	border-left: 2px solid transparent;
+	padding-left: 6px;
+}
+
+.listing.iconized thead tr td:first-child,
+.listing.iconized tbody tr td:first-child {
+	padding-left: 34px;
+}
+
+/* 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 #739da8;
+}
+
 .listbox .listitem.selected,
 .listbox .tablink.selected,
 .listbox .listitem.selected > a,
 .listbox .tablink.selected > a,
-.listing tbody tr.unfocused td,
 .listing tbody tr.selected td,
 .listing li.selected,
 .listing li.selected > a {
@@ -1237,6 +1290,16 @@
 	padding-left: 20px;
 	overflow: hidden;
 	text-overflow: ellipsis;
+}
+
+ul.treelist li a:focus,
+ul.listing .listitem a:focus,
+ul.listing .listitem span:focus,
+ul.listing.focus .listitem.focused span {
+	color: #fff !important;
+	background-color: rgba(73,180,210,0.6);
+	text-shadow: 0px 1px 1px #666;
+	outline: none;
 }
 
 ul.treelist ul li a {
@@ -1319,6 +1382,13 @@
 	margin-top: 1px;
 }
 
+.boxfooter a.listbutton:focus {
+	color: #fff;
+	background-color: rgba(73,180,210,0.6);
+	text-shadow: 0px 1px 1px #666;
+	outline: none;
+}
+
 .uibox .boxfooter .listbutton:first-child {
 	border-radius: 0 0 0 4px;
 }
@@ -1328,7 +1398,9 @@
 	width: 48px;
 	height: 35px;
 	text-indent: -5000px;
-	background: url(images/buttons.png) -1000px 0 no-repeat;
+	background-image: url(images/buttons.png);
+	background-position: -1000px 0;
+	background-repeat: no-repeat;
 }
 
 .boxfooter .listbutton.add .inner {
@@ -1442,6 +1514,7 @@
 	border: 0;
 }
 
+.records-table thead th,
 .records-table thead td {
 	color: #69939e;
 	font-size: 11px;
@@ -1456,13 +1529,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;
@@ -1470,6 +1547,14 @@
 	text-decoration: none;
 	overflow: hidden;
 	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);
+	text-shadow: 0px 1px 1px #666;
+	outline: none;
 }
 
 .records-table tbody td {
@@ -1481,27 +1566,28 @@
 	overflow: hidden;
 	text-overflow: ellipsis;
 	background-color: #fff;
+	outline: none;
 }
 
 /* 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: 0;
-	padding-left: 6px;
-}
-
-/* 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 tbody tr.focused > td:first-child {
-	border-left: 2px solid #b0ccd7;
+	border-left: 2px solid transparent;
 	padding-left: 4px;
 }
 
-.records-table tbody tr.selected.focused > td:first-child {
-	border-left-color: #49b3d2;
+/* 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 #49b3d2;
 }
 
 .records-table tr.selected td {
 	color: #fff !important;
+	background-color: #4db0d2 !important;
+}
+
+.records-table.focus tr.selected td {
 	background: #019bc6;
 	background: -moz-linear-gradient(top, #019bc6 0%, #017cb4 100%);
 	background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#019bc6), color-stop(100%,#017cb4));
@@ -1512,16 +1598,6 @@
 
 .records-table tr.selected td a,
 .records-table tr.selected td span {
-	color: #fff !important;
-}
-
-.records-table tr.unfocused td {
-	color: #fff !important;
-	background-color: #4db0d2 !important;
-}
-
-.records-table tr.unfocused td a,
-.records-table tr.unfocused td span {
 	color: #fff !important;
 }
 
@@ -1921,6 +1997,14 @@
 	border-radius: 0;
 }
 
+.dropbutton .dropbuttontip:focus,
+.toolbar a.button:focus {
+	color: #fff;
+	text-shadow: 0px 1px 1px #666;
+	background-color: rgba(30,150,192, 0.5);
+	border-radius: 3px;
+}
+
 .toolbar a.button.disabled {
 	opacity: 0.4;
 	filter: alpha(opacity=40);
@@ -1936,12 +2020,16 @@
 	position: absolute;
 	right: 0;
 	top: 0;
-	height: 42px;
+	height: 41px;
 	width: 18px;
+	overflow: hidden;
+	text-indent: -5000px;
 	background: url(images/buttons.png) 0 -1255px no-repeat;
 	cursor: pointer;
+	outline: none;
 }
 
+.dropbutton .dropbuttontip:focus,
 .dropbutton .dropbuttontip:hover {
 	background-position: -26px -1255px;
 }
@@ -2132,6 +2220,19 @@
 }
 
 
+a.menuselector:focus,
+a.menuselector.focus,
+a.iconbutton:focus,
+.pagenav a.button:focus {
+	border-color: #4fadd5;
+	-webkit-box-shadow: 0 0 4px 2px rgba(71,135,177, 0.8);
+	   -moz-box-shadow: 0 0 4px 2px rgba(71,135,177, 0.8);
+	     -o-box-shadow: 0 0 4px 2px rgba(71,135,177, 0.8);
+	        box-shadow: 0 0 4px 2px rgba(71,135,177, 0.8);
+	outline: none;
+}
+
+
 /*** quota indicator ***/
 
 #quotadisplay {
@@ -2224,6 +2325,7 @@
 
 .googie_list td.googie_list_onhover,
 ul.toolbarmenu li a.active:hover,
+ul.toolbarmenu li a.active:focus,
 #rcmKSearchpane ul li.selected,
 select.decorated option:hover,
 select.decorated option[selected='selected'] {
@@ -2233,6 +2335,7 @@
 	background: -o-linear-gradient(top, #00aad6 0%, #008fc9 100%);
 	background: -ms-linear-gradient(top, #00aad6 0%, #008fc9 100%);
 	background: linear-gradient(top, #00aad6 0%, #008fc9 100%);
+	outline: none;
 }
 
 ul.toolbarmenu.iconized li a,
@@ -2620,6 +2723,7 @@
 	overflow: hidden;
 	text-overflow: ellipsis;
 	line-height: 20px;
+	outline: none;
 }
 
 .attachmentslist li a.drop {
@@ -2631,6 +2735,15 @@
 	right: 0;
 	top: 0;
 	padding: 0;
+	overflow: hidden;
+	text-indent: -5000px;
+	outline: none;
+}
+
+.attachmentslist li a:focus,
+.attachmentslist li a.drop:focus {
+	background-color: rgba(30,150,192, 0.5);
+	border-radius: 2px;
 }
 
 #compose-attachments ul li {
@@ -2665,26 +2778,22 @@
 
 /*** fieldset tabs ***/
 
-.tabsbar {
-	margin-bottom: 12px;
-	padding-top: 15px;
-	height: 27px;
-	white-space: nowrap;
+.tabbed.ui-tabs {
+	padding: 0;
+	border: 0 !important;
+	background: none;
+}
+
+.boxcontent.tabbed.ui-tabs {
+	padding: 10px;
+}
+
+.ui-tabs .tabsbar.ui-tabs-nav {
+	margin-bottom: 10px;
 }
 
 .ui-dialog-content .tabsbar {
 	margin-bottom: 0;
-}
-
-.tabsbar .tablink {
-	padding: 15px 1px 15px 0;
-	background: #f8f8f8;
-	background: -moz-linear-gradient(top, #f8f8f8 0%, #d3d3d3 50%, #f8f8f8 100%);
-	background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(50%,#d3d3d3), color-stop(100%,#f8f8f8));
-	background: -webkit-linear-gradient(top, #f8f8f8 0%, #d3d3d3 50%, #f8f8f8 100%);
-	background: -o-linear-gradient(top, #f8f8f8 0%, #d3d3d3 50%, #f8f8f8 100%);
-	background: -ms-linear-gradient(top, #f8f8f8 0%, #d3d3d3 50%, #f8f8f8 100%);
-	background: linear-gradient(top, #f8f8f8 0%, #d3d3d3 50%, #f8f8f8 100%);
 }
 
 .tabsbar .tablink:last-child {
@@ -2695,30 +2804,15 @@
 	border-right: 0;
 }
 
-.tabsbar .tablink a {
-	padding: 15px;
-	color: #999;
-	font-size: 12px;
-	font-weight: bold;
-	text-decoration: none;
+.ui-tabs .ui-tabs-nav li.tablink a {
 	background: #fff;
-	border-right: 1px solid #fafafa;
 }
 
-.tabsbar .tablink.selected a {
-	color: #004458;
-	background: #f6f6f6;
-	background: -moz-linear-gradient(top, #fff 40%, #efefef 100%);
-	background: -webkit-gradient(linear, left top, left bottom, color-stop(40%,#fff), color-stop(100%,#efefef));
-	background: -o-linear-gradient(top, #fff 40%, #efefef 100%);
-	background: -ms-linear-gradient(top, #fff 40%, #efefef 100%);
-	background: linear-gradient(top, #fff 40%, #efefef 100%);
-}
-
-fieldset.tab {
+.ui-tabs fieldset.ui-tabs-panel {
 	border: 0;
 	padding: 0;
 	margin-left: 0;
+	background: none;
 }
 
 #image-selector-form.droptarget {
diff --git a/skins/larry/templates/addressbook.html b/skins/larry/templates/addressbook.html
index 97efdc6..e1101e6 100644
--- a/skins/larry/templates/addressbook.html
+++ b/skins/larry/templates/addressbook.html
@@ -10,25 +10,50 @@
 
 <div id="mainscreen">
 
+<h1 class="voice"><roundcube:label name="addressbook" /></h1>
+
 <!-- toolbar -->
-<div id="addressbooktoolbar" class="toolbar">
+<h2 id="aria-label-toolbar" class="voice"><roundcube:label name="arialabeltoolbar" /></h2>
+<div id="addressbooktoolbar" class="toolbar" role="toolbar" aria-labelledby="aria-label-toolbar">
 	<roundcube:button command="import" type="link" class="button import disabled" classAct="button import" classSel="button import pressed" label="import" title="importcontacts" />
 	<span class="dropbutton">
 		<roundcube:button command="export" type="link" class="button export disabled" classAct="button export" classSel="button export pressed" label="export" title="exportvcards" />
-		<span class="dropbuttontip" id="exportmenulink" onclick="UI.show_popup('exportmenu');return false"></span>
+		<a href="#export" class="dropbuttontip" id="exportmenulink" onclick="return UI.toggle_popup('exportmenu',event)" aria-haspopup="true" aria-expanded="false" aria-owns="exportmenu-menu" tabindex="0"><roundcube:label name="arialabelcontactexportoptions" /></a>
 	</span>
 
 	<span class="spacer"></span>
 	<roundcube:button command="compose" type="link" class="button compose disabled" classAct="button compose" classSel="button compose pressed" label="compose" title="writenewmessage" />
 	<roundcube:button command="advanced-search" type="link" class="button search disabled" classAct="button search" classSel="button search pressed" label="advanced" title="advsearch" />
 	<roundcube:container name="toolbar" id="addressbooktoolbar" />
+
+	<div id="exportmenu" class="popupmenu" aria-hidden="true">
+		<h3 id="aria-label-exportmenu" class="voice"><roundcube:label name="arialabelcontactexportoptions" /></h3>
+		<ul id="exportmenu-menu" class="toolbarmenu" role="menu" aria-labelledby="aria-label-exportmenu">
+			<li role="menuitem"><roundcube:button command="export" label="exportall" prop="sub" class="exportalllink" classAct="exportalllink active" /></li>
+			<li role="menuitem"><roundcube:button command="export-selected" label="exportsel" prop="sub" class="exportsellink" classAct="exportsellink active" /></li>
+		</ul>
+	</div>
+
 </div>
 
 <!-- search box -->
-<div id="quicksearchbar" class="searchbox">
+<div id="quicksearchbar" class="searchbox" role="search" aria-labelledby="aria-label-searchform">
+<h2 id="aria-label-searchform" class="voice"><roundcube:label name="arialabelcontactsearchform" /></h2>
+<label for="quicksearchbox" class="voice"><roundcube:label name="arialabelquicksearchbox" /></label>
 <roundcube:object name="searchform" id="quicksearchbox" />
-<roundcube:button name="searchmenulink" id="searchmenulink" class="iconbutton searchoptions" onclick="UI.show_popup('searchmenu');return false" title="searchmod" content=" " />
-<roundcube:button command="reset-search" id="searchreset" class="iconbutton reset" title="resetsearch" content=" " />
+<roundcube:button command="menu-open" prop="searchmenu" id="searchmenulink" class="iconbutton searchoptions" title="searchmod" label="options" aria-haspopup="true" aria-expanded="false" aria-owns="searchmenu-menu" />
+<roundcube:button command="reset-search" id="searchreset" class="iconbutton reset" title="resetsearch" label="resetsearch" />
+
+<div id="searchmenu" class="popupmenu" data-editable="true">
+	<h3 id="aria-label-searchmenu" class="voice"><roundcube:label name="searchmod" /></h3>
+	<ul class="toolbarmenu" id="searchmenu-menu" role="menu" aria-labelledby="aria-label-searchmenu">
+		<li role="menuitem"><label><input type="checkbox" name="s_mods[]" value="name" id="s_mod_name" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="name" /></span></label></li>
+		<li role="menuitem"><label><input type="checkbox" name="s_mods[]" value="firstname" id="s_mod_firstname" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="firstname" /></span></label></li>
+		<li role="menuitem"><label><input type="checkbox" name="s_mods[]" value="surname" id="s_mod_surname" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="surname" /></span></label></li>
+		<li role="menuitem"><label><input type="checkbox" name="s_mods[]" value="email" id="s_mod_email" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="email" /></span></label></li>
+		<li role="menuitem"><label><input type="checkbox" name="s_mods[]" value="*" id="s_mod_all" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="allfields" /></span></label></li>
+	</ul>
+</div>
 </div>
 
 <div id="mainscreencontent">
@@ -36,42 +61,64 @@
 <div id="addressview-left">
 
 <!-- sources/groups list -->
-<div id="directorylistbox" class="uibox listbox">
+<div id="directorylistbox" class="uibox listbox" role="navigation" aria-labelledby="directorylist-header">
 <h2 id="directorylist-header" class="boxtitle"><roundcube:label name="groups" /></h2>
 <div id="directorylist-content" class="scroller withfooter">
 	<roundcube:object name="directorylist" id="directorylist" class="treelist listing iconized" />
 </div>
 <div id="directorylist-footer" class="boxfooter">
-	<roundcube:button command="group-create" type="link" title="newcontactgroup" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="groupoptions" id="groupoptionslink" type="link" title="moreactions" class="listbutton groupactions" onclick="UI.show_popup('groupoptions');return false" innerClass="inner" content="&#9881;" />
+	<roundcube:button command="group-create" type="link" title="newcontactgroup" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" label="newcontactgroup" /><roundcube:button name="groupoptions" id="groupoptionslink" type="link" title="moreactions" class="listbutton groupactions" onclick="return UI.toggle_popup('groupoptions',event)" innerClass="inner" label="arialabelabookgroupoptions" aria-haspopup="true" aria-expanded="false" aria-owns="groupoptionsmenu" />
 </div>
+</div>
+
+<div id="groupoptions" class="popupmenu" aria-hidden="true">
+	<h3 id="aria-label-groupoptions" class="voice"><roundcube:label name="arialabelabookgroupoptions" /></h3>
+	<ul id="groupoptionsmenu" class="toolbarmenu" role="menu" aria-labelledby="aria-label-groupoptions">
+		<li role="menuitem"><roundcube:button command="group-rename" label="grouprename" classAct="active" /></li>
+		<li role="menuitem"><roundcube:button command="group-delete" label="groupdelete" classAct="active" /></li>
+		<li role="menuitem"><roundcube:button command="search-create" label="searchsave" classAct="active" /></li>
+		<li role="menuitem"><roundcube:button command="search-delete" label="searchdelete" classAct="active" /></li>
+		<roundcube:container name="groupoptions" id="groupoptionsmenu" />
+	</ul>
 </div>
 
 </div><!-- end addressview-left -->
 
-<div id="addressview-right">
+<div id="addressview-right" role="main" aria-labelledby="aria-label-contactslist">
 
 <!-- contacts list -->
 <div id="addresslist" class="uibox listbox">
-<roundcube:object name="addresslisttitle" label="contacts" tag="h2" class="boxtitle" />
+<roundcube:object name="addresslisttitle" label="contacts" tag="h2" class="boxtitle" id="aria-label-contactslist" />
 <div class="scroller withfooter">
-<roundcube:object name="addresslist" id="contacts-table" class="listing" noheader="true" />
-</div>
-<div class="boxfooter">
-	<roundcube:button command="add" type="link" title="newcontact" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button command="delete" type="link" title="deletecontact" class="listbutton delete disabled" classAct="listbutton delete" innerClass="inner" content="x" /><roundcube:button command="group-remove-selected" type="link" title="groupremoveselected" class="listbutton removegroup disabled" classAct="listbutton removegroup" innerClass="inner" content="-" />
-	<roundcube:object name="recordsCountDisplay" class="countdisplay" label="fromtoshort" />
+<roundcube:object name="addresslist" id="contacts-table" class="listing iconized" noheader="true" role="listbox" />
 </div>
 <div class="boxpagenav">
-	<roundcube:button command="firstpage" type="link" class="icon firstpage disabled" classAct="icon firstpage" title="firstpage" content="|&amp;lt;" />
-	<roundcube:button command="previouspage" type="link" class="icon prevpage disabled" classAct="icon prevpage" title="previouspage" content="&amp;lt;" />
-	<roundcube:button command="nextpage" type="link" class="icon nextpage disabled" classAct="icon nextpage" title="nextpage" content="&amp;gt;" />
-	<roundcube:button command="lastpage" type="link" class="icon lastpage disabled" classAct="icon lastpage" title="lastpage" content="&amp;gt;|" />
+	<roundcube:button command="firstpage" type="link" class="icon firstpage disabled" classAct="icon firstpage" title="firstpage" label="first" />
+	<roundcube:button command="previouspage" type="link" class="icon prevpage disabled" classAct="icon prevpage" title="previouspage" label="previous" />
+	<roundcube:button command="nextpage" type="link" class="icon nextpage disabled" classAct="icon nextpage" title="nextpage" label="next" />
+	<roundcube:button command="lastpage" type="link" class="icon lastpage disabled" classAct="icon lastpage" title="lastpage" label="last" />
 </div>
+<div class="boxfooter">
+	<roundcube:button command="add" type="link" title="newcontact" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" label="newcontact" /><roundcube:button command="delete" type="link" title="deletecontact" class="listbutton delete disabled" classAct="listbutton delete" innerClass="inner" label="deletecontact" /><roundcube:button command="group-remove-selected" type="link" title="groupremoveselected" class="listbutton removegroup disabled" classAct="listbutton removegroup" innerClass="inner" label="groupremoveselected" />
+	<span class="countdisplay" aria-live="polite" aria-relevant="text">
+		<span class="voice"><roundcube:label name="contacts" /></span>
+		<roundcube:object name="recordsCountDisplay" label="fromtoshort" />
+	</span>
+</div>
+</div>
+
+<div class="voice" role="note">
+<h3><roundcube:label name="helplistnavigation" /></h3>
+<pre>
+<roundcube:label name="helplistkeyboardnavigation" />
+<roundcube:label name="helplistkeyboardnavcontacts" />
+</pre>
 </div>
 
 
 <div id="contacts-box" class="uibox">
 	<div class="iframebox">
-		<roundcube:object name="addressframe" id="contact-frame" style="width:100%; height:100%" frameborder="0" src="/watermark.html" />
+		<roundcube:object name="addressframe" id="contact-frame" style="width:100%; height:100%" frameborder="0" src="/watermark.html" title="contactproperties" />
 	</div>
 </div>
 
@@ -81,37 +128,10 @@
 
 </div><!-- end mainscreen -->
 
-<div id="exportmenu" class="popupmenu">
-	<ul class="toolbarmenu">
-		<li><roundcube:button command="export" label="exportall" prop="sub" class="exportalllink" classAct="exportalllink active" /></li>
-		<li><roundcube:button command="export-selected" label="exportsel" prop="sub" class="exportsellink" classAct="exportsellink active" /></li>
-	</ul>
-</div>
-
-<div id="searchmenu" class="popupmenu">
-	<ul class="toolbarmenu">
-		<li><label><input type="checkbox" name="s_mods[]" value="name" id="s_mod_name" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="name" /></span></label></li>
-		<li><label><input type="checkbox" name="s_mods[]" value="firstname" id="s_mod_firstname" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="firstname" /></span></label></li>
-		<li><label><input type="checkbox" name="s_mods[]" value="surname" id="s_mod_surname" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="surname" /></span></label></li>
-		<li><label><input type="checkbox" name="s_mods[]" value="email" id="s_mod_email" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="email" /></span></label></li>
-		<li><label><input type="checkbox" name="s_mods[]" value="*" id="s_mod_all" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="allfields" /></span></label></li>
-	</ul>
-</div>
-
-<div id="groupoptions" class="popupmenu">
-	<ul id="groupoptionsmenu" class="toolbarmenu">
-		<li><roundcube:button command="group-rename" label="grouprename" classAct="active" /></li>
-		<li><roundcube:button command="group-delete" label="groupdelete" classAct="active" /></li>
-		<li><roundcube:button command="search-create" label="searchsave" classAct="active" /></li>
-		<li><roundcube:button command="search-delete" label="searchdelete" classAct="active" /></li>
-		<roundcube:container name="groupoptions" id="groupoptionsmenu" />
-	</ul>
-</div>
-
-<div id="dragcontactmenu" class="popupmenu">
-	<ul class="toolbarmenu">
-		<li><roundcube:button command="move" onclick="return rcmail.drag_menu_action('move')" label="move" classAct="active" /></li>
-		<li><roundcube:button command="copy" onclick="return rcmail.drag_menu_action('copy')" label="copy" classAct="active" /></li>
+<div id="dragcontactmenu" class="popupmenu" aria-hidden="true">
+	<ul class="toolbarmenu" role="menu">
+		<li role="menuitem"><roundcube:button command="move" onclick="return rcmail.drag_menu_action('move')" label="move" classAct="active" /></li>
+		<li role="menuitem"><roundcube:button command="copy" onclick="return rcmail.drag_menu_action('copy')" label="copy" classAct="active" /></li>
 	</ul>
 </div>
 
diff --git a/skins/larry/templates/compose.html b/skins/larry/templates/compose.html
index 90df4f3..2812d37 100644
--- a/skins/larry/templates/compose.html
+++ b/skins/larry/templates/compose.html
@@ -13,26 +13,27 @@
 
 <div id="mainscreen">
 
+<h1 class="voice"><roundcube:object name="pagetitle" /></h1>
+
 <!-- toolbar -->
-<div id="messagetoolbar" class="fullwidth">
-<div id="mailtoolbar" class="toolbar">
-	<roundcube:button command="list" type="link" class="button back disabled" classAct="button back" classSel="button back pressed" label="cancel" condition="!env:extwin" />
-	<roundcube:button command="close" type="link" class="button close disabled" classAct="button close" classSel="button close pressed" label="cancel" condition="env:extwin" />
+<h2 id="aria-label-toolbar" class="voice"><roundcube:label name="arialabeltoolbar" /></h2>
+<div id="messagetoolbar" class="toolbar fullwidth" role="toolbar" aria-labelledby="aria-label-toolbar">
+	<roundcube:button command="list" type="link" class="button back disabled" classAct="button back" label="cancel" condition="!env:extwin" tabindex="2" />
+	<roundcube:button command="close" type="link" class="button close disabled" classAct="button close" label="cancel" condition="env:extwin" tabindex="2" />
 	<span class="spacer"></span>
-	<roundcube:button command="send" type="link" class="button send" classAct="button send" classSel="button send pressed" label="send" title="sendmessage" />
-	<roundcube:button command="savedraft" type="link" class="button savedraft" classAct="button savedraft" classSel="button savedraft pressed" label="save" title="savemessage" />
+	<roundcube:button command="send" type="link" class="button send disabled" classAct="button send" label="send" title="sendmessage" tabindex="2" />
+	<roundcube:button command="savedraft" type="link" class="button savedraft disabled" classAct="button savedraft" label="save" title="savemessage" tabindex="2" />
 	<span class="spacer"></span>
 	<roundcube:if condition="config:enable_spellcheck" />
 	<span class="dropbutton">
-		<roundcube:button command="spellcheck" type="link" class="button spellcheck disabled" classAct="button spellcheck" classSel="button spellcheck pressed" label="spellcheck" title="checkspelling" />
-		<span class="dropbuttontip" id="spellmenulink" onclick="UI.show_popup('spellmenu');return false"></span>
+		<roundcube:button command="spellcheck" type="link" class="button spellcheck disabled" classAct="button spellcheck" classSel="button spellcheck pressed" label="spellcheck" title="checkspelling" tabindex="2" />
+		<a href="#languages" class="dropbuttontip" id="spellmenulink" onclick="UI.toggle_popup('spellmenu',event);return false" aria-haspopup="true" aria-expanded="false"tabindex="2">Select Spell Language</a>
 	</span>
 	<roundcube:endif />
-	<roundcube:button name="addattachment" type="link" class="button attach" classAct="button attach" classSel="button attach pressed" label="attach" title="addattachment" onclick="UI.show_uploadform();return false" />
-	<roundcube:button command="insert-sig" type="link" class="button insertsig disabled" classAct="button insertsig" classSel="button insertsig pressed" label="signature" title="insertsignature" />
-	<a href="#responses" class="button responses" label="responses" title="<roundcube:label name='insertresponse' />" id="responsesmenulink" unselectable="on" onmousedown="return false" onclick="UI.show_popup('responsesmenu');return false"><roundcube:label name="responses" /></a>
+	<roundcube:button name="addattachment" type="link" class="button attach" label="attach" title="addattachment" onclick="UI.show_uploadform(event);return false" aria-haspopup="true" aria-expanded="false"tabindex="2" />
+	<roundcube:button command="insert-sig" type="link" class="button insertsig disabled" classAct="button insertsig" label="signature" title="insertsignature" tabindex="2" />
+	<a href="#responses" class="button responses" label="responses" title="<roundcube:label name='insertresponse' />" id="responsesmenulink" unselectable="on" onmousedown="return false" onclick="UI.toggle_popup('responsesmenu',event);return false" tabindex="2" aria-haspopup="true" aria-expanded="false" aria-owns="textresponsesmenu"><roundcube:label name="responses" /></a>
 	<roundcube:container name="toolbar" id="compose-toolbar" />
-</div>
 </div>
 
 <div id="mainscreencontent">
@@ -40,39 +41,43 @@
 <div id="composeview-left">
 
 <!-- inline address book -->
-<div id="compose-contacts" class="uibox listbox">
-<h2 class="boxtitle"><roundcube:label name="contacts" /></h2>
-	<div class="listsearchbox">
+<div id="compose-contacts" class="uibox listbox" role="region" aria-labelledby="aria-label-composecontacts">
+<h2 id="aria-label-composecontacts" class="boxtitle"><roundcube:label name="contacts" /></h2>
+	<div class="listsearchbox" role="search" aria-labelledby="aria-label-composequicksearch">
+		<h3 id="aria-label-composequicksearch" class="voice"><roundcube:label name="arialabelcontactquicksearch" /></h3>
 		<div class="searchbox">
+			<label for="contactsearchbox" class="voice"><roundcube:label name="arialabelcontactsearchbox" /></label>
 			<roundcube:object name="searchform" id="contactsearchbox" />
 			<a id="searchmenulink" class="iconbutton searchicon"> </a>
 			<roundcube:button command="reset-search" class="iconbutton reset" title="resetsearch" content=" " />
 		</div>
 	</div>
-	<roundcube:object name="addressbooks" id="directorylist" class="listing" />
-	<div class="scroller withfooter">
-		<roundcube:object name="addresslist" id="contacts-table" class="listing" noheader="true" />
+	<roundcube:object name="addressbooks" id="directorylist" class="treelist listing" summary="ariasummarycomposecontacts" />
+	<div class="scroller withfooter" tabindex="-1">
+		<roundcube:object name="addresslist" id="contacts-table" class="listing iconized" noheader="true" role="listbox" />
 	</div>
 <div class="boxfooter">
 	<roundcube:button command="add-recipient" prop="to" type="link" title="to" class="listbutton addto disabled" classAct="listbutton addto" innerClass="inner" content="To+" /><roundcube:button command="add-recipient" prop="cc" type="link" title="cc" class="listbutton addcc disabled" classAct="listbutton addcc" innerClass="inner" content="Cc+" /><roundcube:button command="add-recipient" prop="bcc" type="link" title="bcc" class="listbutton addbcc disabled" classAct="listbutton addbcc" innerClass="inner" content="Bcc+" />
 </div>
 <div class="boxpagenav">
-	<roundcube:button command="firstpage" type="link" class="icon firstpage disabled" classAct="icon firstpage" title="firstpage" content="|&amp;lt;" />
-	<roundcube:button command="previouspage" type="link" class="icon prevpage disabled" classAct="icon prevpage" title="previouspage" content="&amp;lt;" />
-	<roundcube:button command="nextpage" type="link" class="icon nextpage disabled" classAct="icon nextpage" title="nextpage" content="&amp;gt;" />
-	<roundcube:button command="lastpage" type="link" class="icon lastpage disabled" classAct="icon lastpage" title="lastpage" content="&amp;gt;|" />
+	<roundcube:button command="firstpage" type="link" class="icon firstpage disabled" classAct="icon firstpage" title="firstpage" label="first" />
+	<roundcube:button command="previouspage" type="link" class="icon prevpage disabled" classAct="icon prevpage" title="previouspage" label="previous" />
+	<roundcube:button command="nextpage" type="link" class="icon nextpage disabled" classAct="icon nextpage" title="nextpage" label="next" />
+	<roundcube:button command="lastpage" type="link" class="icon lastpage disabled" classAct="icon lastpage" title="lastpage" label="last" />
 </div>
 </div>
 
 </div>
 
-<div id="composeview-right">
+<div id="composeview-right" role="main">
 
 <roundcube:form name="form" method="post" id="compose-content" class="uibox">
 
 <!-- message headers -->
-<div id="composeheaders">
-<a href="#options" id="composeoptionstoggle" class="moreheaderstoggle"><span class="iconlink" title="<roundcube:label name='options' />"></span></a>
+<div id="composeheaders" role="region" aria-labelledby="aria-label-composeheaders">
+<h2 id="aria-label-composeheaders" class="voice"><roundcube:label name="arialabelmessageheaders" /></h2>
+
+<a href="#options" id="composeoptionstoggle" class="moreheaderstoggle" title="<roundcube:label name='togglecomposeoptions' />" aria-exapnded="false"><span class="iconlink"></span></a>
 
 <table class="headers-table compose-headers">
 <tbody>
@@ -80,77 +85,78 @@
 		<td class="title"><label for="_from"><roundcube:label name="from" /></label></td>
 		<td class="editfield">
 			<roundcube:object name="composeHeaders" part="from" form="form" id="_from" tabindex="1" />
-			<a href="#identities" onclick="return rcmail.command('identities')" class="iconlink edit"><roundcube:label name="editidents" /></a>
+			<a href="#identities" onclick="return rcmail.command('identities')" class="iconlink edit" tabindex="0"><roundcube:label name="editidents" /></a>
 		</td>
 	</tr><tr>
 		<td class="title top"><label for="_to"><roundcube:label name="to" /></label></td>
-		<td class="editfield"><roundcube:object name="composeHeaders" part="to" form="form" id="_to" cols="70" rows="1" tabindex="2" /></td>
+		<td class="editfield"><roundcube:object name="composeHeaders" part="to" form="form" id="_to" cols="70" rows="1" tabindex="1" aria-required="true" /></td>
 	</tr><tr id="compose-cc">
 		<td class="title top">
 			<label for="_cc"><roundcube:label name="cc" /></label>
-			<a href="#cc" onclick="return UI.hide_header_row('cc');" class="iconbutton cancel" title="<roundcube:label name='delete' />">x</a>
+			<a href="#cc" onclick="return UI.hide_header_row('cc');" class="iconbutton cancel" title="<roundcube:label name='delete' />" tabindex="3"><roundcube:label name="delete" /> <roundcube:label name="cc" /></a>
 		</td>
-		<td class="editfield"><roundcube:object name="composeHeaders" part="cc" form="form" id="_cc" cols="70" rows="1" tabindex="3" /></td>
+		<td class="editfield"><roundcube:object name="composeHeaders" part="cc" form="form" id="_cc" cols="70" rows="1" tabindex="1" /></td>
 	</tr><tr id="compose-bcc">
 		<td class="title top">
 			<label for="_bcc"><roundcube:label name="bcc" /></label>
-			<a href="#bcc" onclick="return UI.hide_header_row('bcc');" class="iconbutton cancel" title="<roundcube:label name='delete' />">x</a>
+			<a href="#bcc" onclick="return UI.hide_header_row('bcc');" class="iconbutton cancel" title="<roundcube:label name='delete' />" tabindex="3"><roundcube:label name="delete" /> <roundcube:label name="bcc" /></a>
 		</td>
-		<td class="editfield"><roundcube:object name="composeHeaders" part="bcc" form="form" id="_bcc" cols="70" rows="1" tabindex="4" /></td>
+		<td class="editfield"><roundcube:object name="composeHeaders" part="bcc" form="form" id="_bcc" cols="70" rows="1" tabindex="1" /></td>
 	</tr><tr id="compose-replyto">
 		<td class="title top">
 			<label for="_replyto"><roundcube:label name="replyto" /></label>
-			<a href="#replyto" onclick="return UI.hide_header_row('replyto');" class="iconbutton cancel" title="<roundcube:label name='delete' />">x</a>
+			<a href="#replyto" onclick="return UI.hide_header_row('replyto');" class="iconbutton cancel" title="<roundcube:label name='delete' />" tabindex="3"><roundcube:label name="delete" /> <roundcube:label name="replyto" /></a>
 		</td>
-		<td class="editfield"><roundcube:object name="composeHeaders" part="replyto" form="form" id="_replyto" size="70" tabindex="5" /></td>
+		<td class="editfield"><roundcube:object name="composeHeaders" part="replyto" form="form" id="_replyto" size="70" tabindex="1" /></td>
 	</tr><tr id="compose-followupto">
 		<td class="title top">
 			<label for="_followupto"><roundcube:label name="followupto" /></label>
-			<a href="#followupto" onclick="return UI.hide_header_row('followupto');" class="iconbutton cancel" title="<roundcube:label name='delete' />">x</a>
+			<a href="#followupto" onclick="return UI.hide_header_row('followupto');" class="iconbutton cancel" title="<roundcube:label name='delete' />" tabindex="3"><roundcube:label name="delete" /> <roundcube:label name="followupto" /></a>
 		</td>
-		<td class="editfield"><roundcube:object name="composeHeaders" part="followupto" form="form" id="_followupto" size="70" tabindex="7" /></td>
+		<td class="editfield"><roundcube:object name="composeHeaders" part="followupto" form="form" id="_followupto" size="70" tabindex="1" /></td>
 	</tr><tr>
 		<td></td>
 		<td class="formlinks">
-			<a href="#cc" onclick="return UI.show_header_row('cc')" id="cc-link" class="iconlink add"><roundcube:label name="addcc" /></a>
-			<a href="#bcc" onclick="return UI.show_header_row('bcc')" id="bcc-link" class="iconlink add"><roundcube:label name="addbcc" /></a>
-			<a href="#reply-to" onclick="return UI.show_header_row('replyto')" id="replyto-link" class="iconlink add"><roundcube:label name="addreplyto" /></a>
-			<a href="#followup-to" onclick="return UI.show_header_row('followupto')" id="followupto-link" class="iconlink add"><roundcube:label name="addfollowupto" /></a>
+			<a href="#cc" onclick="return UI.show_header_row('cc')" id="cc-link" class="iconlink add" tabindex="3"><roundcube:label name="addcc" /></a>
+			<a href="#bcc" onclick="return UI.show_header_row('bcc')" id="bcc-link" class="iconlink add" tabindex="3"><roundcube:label name="addbcc" /></a>
+			<a href="#reply-to" onclick="return UI.show_header_row('replyto')" id="replyto-link" class="iconlink add" tabindex="3"><roundcube:label name="addreplyto" /></a>
+			<a href="#followup-to" onclick="return UI.show_header_row('followupto')" id="followupto-link" class="iconlink add" tabindex="3"><roundcube:label name="addfollowupto" /></a>
 		</td>
 	</tr><tr>
 		<td class="title"><label for="compose-subject"><roundcube:label name="subject" /></label></td>
-		<td class="editfield"><roundcube:object name="composeSubject" id="compose-subject" form="form" tabindex="8" /></td>
+		<td class="editfield"><roundcube:object name="composeSubject" id="compose-subject" form="form" tabindex="1" /></td>
 	</tr>
 </tbody>
 </table>
 
 <div id="composebuttons" class="formbuttons">
-	<roundcube:button command="extwin" type="link" class="button extwin" classSel="button extwin pressed" innerClass="icon" title="openinextwin" content="[]" condition="!env:extwin" />
+	<roundcube:button command="extwin" type="link" class="button extwin" classSel="button extwin pressed" innerClass="icon" title="openinextwin" label="openinextwin" condition="!env:extwin" />
 </div>
 
 <!-- (collapsable) message options -->
-<div id="composeoptions">
+<div id="composeoptions" role="region" aria-labelledby="aria-label-composeoptions">
+	<h2 id="aria-label-composeoptions" class="voice"><roundcube:label name="arialabelcomposeoptions" /></h2>
 	<roundcube:if condition="!in_array('htmleditor', (array)config:dont_override)" />
 	<span class="composeoption">
 		<label><roundcube:label name="editortype" />
-			<roundcube:object name="editorSelector" editorid="composebody" tabindex="14" /></label>
+			<roundcube:object name="editorSelector" editorid="composebody" tabindex="4" /></label>
 	</span>
 	<roundcube:endif />
 	<span class="composeoption">
 		<label for="rcmcomposepriority"><roundcube:label name="priority" />
-			<roundcube:object name="prioritySelector" form="form" id="rcmcomposepriority" /></label>
+			<roundcube:object name="prioritySelector" form="form" id="rcmcomposepriority" tabindex="4" /></label>
 	</span>
 	<span class="composeoption">
-		<label><roundcube:object name="receiptCheckBox" form="form" id="rcmcomposereceipt" /> <roundcube:label name="returnreceipt" /></label>
+		<label><roundcube:object name="receiptCheckBox" form="form" id="rcmcomposereceipt" tabindex="4" /> <roundcube:label name="returnreceipt" /></label>
 	</span>
 	<roundcube:if condition="config:smtp_server != ''" />
 	<span class="composeoption">
-		<label><roundcube:object name="dsnCheckBox" form="form" id="rcmcomposedsn" /> <roundcube:label name="dsn" /></label>
+		<label><roundcube:object name="dsnCheckBox" form="form" id="rcmcomposedsn" tabindex="4" /> <roundcube:label name="dsn" /></label>
 	</span>
 	<roundcube:endif />
 	<roundcube:if condition="!config:no_save_sent_messages" />
 	<span class="composeoption">
-		<label><roundcube:label name="savesentmessagein" /> <roundcube:object name="storetarget" maxlength="30" style="max-width:12em" /></label>
+		<label><roundcube:label name="savesentmessagein" /> <roundcube:object name="storetarget" maxlength="30" style="max-width:12em" tabindex="4" /></label>
 	</span>
 	<roundcube:endif />
 	<roundcube:container name="composeoptions" id="composeoptions" />
@@ -161,11 +167,13 @@
 <!-- message compose body -->
 <div id="composeview-bottom">
 	<div id="composebodycontainer">
-		<roundcube:object name="composeBody" id="composebody" form="form" cols="70" rows="20" tabindex="9" />
+		<label for="composebody" class="voice"><roundcube:label name="arialabelmessagebody" /></label>
+		<roundcube:object name="composeBody" id="composebody" form="form" cols="70" rows="20" tabindex="1" />
 	</div>
-	<div id="compose-attachments" class="rightcol">
+	<div id="compose-attachments" class="rightcol" role="region" aria-labelledby="aria-label-composeattachments">
+		<h2 id="aria-label-composeattachments" class="voice"><roundcube:label name="attachments" /></h2>
 		<div style="text-align:center; margin-bottom:20px">
-			<roundcube:button name="addattachment" type="input" class="button" classSel="button pressed" label="addattachment" onclick="UI.show_uploadform();return false" />
+			<roundcube:button name="addattachment" type="input" class="button" classSel="button pressed" label="addattachment" onclick="UI.show_uploadform(event);return false" tabindex="1" />
 		</div>
 		<roundcube:object name="composeAttachmentList" id="attachment-list" class="attachmentslist" />
 		<roundcube:object name="fileDropArea" id="compose-attachments" />
@@ -187,7 +195,8 @@
 
 </div><!-- end mainscreen -->
 
-<div id="upload-dialog" class="propform popupdialog" title="<roundcube:label name='addattachment' />">
+<div id="upload-dialog" class="propform popupdialog" title="<roundcube:label name='addattachment' />" aria-hidden="true">
+	<h2 id="aria-label-uploaddialog" class="voice"><roundcube:label name="arialabelattachmentuploadform" /></h2>
 	<roundcube:object name="composeAttachmentForm" id="uploadform" buttons="no" />
 	<div class="formbuttons">
 		<roundcube:button command="send-attachment" type="input" class="button mainaction" label="upload" />
@@ -195,15 +204,16 @@
 	</div>
 </div>
 
-<div id="spellmenu" class="popupmenu"></div>
+<div id="spellmenu" class="popupmenu" aria-hidden="true"></div>
 
-<div id="responsesmenu" class="popupmenu">
-    <ul class="toolbarmenu" id="textresponsesmenu">
-		<li class="separator" id=""><label><roundcube:label name="insertresponse" /></label></li>
+<div id="responsesmenu" class="popupmenu" aria-hidden="true">
+	<h3 id="aria-label-responsesmenu" class="voice"><roundcube:label name="arialabelresponsesmenu" /></h3>
+	<ul class="toolbarmenu" id="textresponsesmenu" role="menu" aria-labelledby="aria-label-responsesmenu">
+		<li role="separator" class="separator" id=""><label><roundcube:label name="insertresponse" /></label></li>
 		<roundcube:object name="responseslist" id="responseslist" tagname="ul" itemclass="active" />
-		<li class="separator"><label><roundcube:label name="manageresponses" /></label></li>
-		<li><roundcube:button command="save-response" type="link" label="savenewresponse" classAct="active" unselectable="on" /></li>
-		<li><roundcube:button command="responses" type="link" label="editresponses" classAct="active" /></li>
+		<li role="separator" class="separator"><label><roundcube:label name="manageresponses" /></label></li>
+		<li role="menuitem"><roundcube:button command="save-response" type="link" label="savenewresponse" classAct="active" unselectable="on" /></li>
+		<li role="menuitem"><roundcube:button command="responses" type="link" label="editresponses" classAct="active" /></li>
 	</ul>
 </div>
 
diff --git a/skins/larry/templates/contactedit.html b/skins/larry/templates/contactedit.html
index 3467ebe..b7aafed 100644
--- a/skins/larry/templates/contactedit.html
+++ b/skins/larry/templates/contactedit.html
@@ -16,7 +16,8 @@
 		<div id="sourcename"><roundcube:label name="addressbook" />: <roundcube:var name="env:sourcename" condition="env:action!='add'" /><roundcube:object name="sourceselector" id="sourceselect" condition="env:action=='add'" /></div>
 	<roundcube:endif />
 
-	<div id="contactphoto">
+	<fieldset id="contactphoto">
+		<legend class="voice"><roundcube:label name="contactphoto" /></legend>
 		<roundcube:object name="contactphoto" id="contactpic" placeholder="/images/contactpic.png" />
 		<roundcube:if condition="env:photocol" />
 		<roundcube:object name="fileDropArea" id="contactpic" />
@@ -25,7 +26,7 @@
 			<roundcube:button command="delete-photo" type="link" label="delete" class="iconlink delete disabled" classAct="iconlink delete active" condition="env:photocol" />
 		</div>
 		<roundcube:endif />
-	</div>
+	</fieldset>
 
 	<roundcube:object name="contactedithead" id="contacthead" size="16" form="editform" />
 	<br style="clear:both" />
diff --git a/skins/larry/templates/folders.html b/skins/larry/templates/folders.html
index 56396bf..ffb0a7e 100644
--- a/skins/larry/templates/folders.html
+++ b/skins/larry/templates/folders.html
@@ -10,9 +10,11 @@
 
 <div id="mainscreen" class="offset">
 
+<h1 class="voice"><roundcube:label name="settings" /> : <roundcube:label name="folders" /></h1>
+
 <roundcube:include file="/includes/settingstabs.html" />
 
-<div id="settings-right">
+<div id="settings-right" role="main">
 
 <div id="folderslist" class="uibox listbox">
 <h2 id="folderslist-header" class="boxtitle"><span style="float:right"><roundcube:label name="subscribed" /></span><roundcube:label name="folders" /></h2>
@@ -20,11 +22,22 @@
 <roundcube:object name="foldersubscription" form="subscriptionform" id="subscription-table" class="listing" noheader="true" />
 </div>
 <div id="folderslist-footer" class="boxfooter">
-	<roundcube:button command="create-folder" type="link" title="createfolder" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="mailboxmenulink" id="mailboxmenulink" type="link" title="folderactions" class="listbutton groupactions" onclick="UI.show_popup('mailboxmenu');return false" innerClass="inner" content="&#9881;" />
+	<roundcube:button command="create-folder" type="link" title="createfolder" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" label="createfolder" /><roundcube:button name="mailboxmenulink" id="mailboxmenulink" type="link" title="folderactions" class="listbutton groupactions" onclick="return UI.toggle_popup('mailboxmenu',event)" innerClass="inner" content="&#9881;" aria-haspopup="true" aria-expanded="false" aria-owns="mailboxoptionsmenu" />
 	<roundcube:if condition="env:quota" />
+		<span class="voice"><roundcube:label name="quota"></span>
 		<roundcube:object name="quotaDisplay" id="quotadisplay" class="countdisplay" display="text" />
 	<roundcube:endif />
 </div>
+
+<div id="mailboxmenu" class="popupmenu" aria-hidden="true">
+	<h3 id="aria-label-mailboxmenu" class="voice"><roundcube:label name="arialabelmailboxmenu" /></h3>
+	<ul class="toolbarmenu" id="mailboxoptionsmenu" role="menu" aria-labelledby="aria-label-mailboxmenu">
+		<li role="menuitem"><roundcube:button command="delete-folder" label="delete" classAct="active" /></li>
+		<li role="menuitem"><roundcube:button command="purge" type="link" label="empty" classAct="active" /></li>
+		<roundcube:container name="mailboxoptions" id="mailboxoptionsmenu" />
+	</ul>
+</div>
+
 </div>
 
 <div id="folder-details" class="uibox contentbox">
@@ -35,14 +48,6 @@
 
 </div>
 
-</div>
-
-<div id="mailboxmenu" class="popupmenu">
-	<ul class="toolbarmenu" id="mailboxoptionsmenu">
-		<li><roundcube:button command="delete-folder" label="delete" classAct="active" /></li>
-		<li><roundcube:button command="purge" type="link" label="empty" classAct="active" /></li>
-		<roundcube:container name="mailboxoptions" id="mailboxoptionsmenu" />
-	</ul>
 </div>
 
 <roundcube:include file="/includes/footer.html" />
diff --git a/skins/larry/templates/identities.html b/skins/larry/templates/identities.html
index e3d2cc8..91f7f8f 100644
--- a/skins/larry/templates/identities.html
+++ b/skins/larry/templates/identities.html
@@ -10,23 +10,25 @@
 
 <div id="mainscreen" class="offset">
 
+<h1 class="voice"><roundcube:label name="settings" /> : <roundcube:label name="identities" /></h1>
+
 <roundcube:include file="/includes/settingstabs.html" />
 
-<div id="settings-right">
+<div id="settings-right" role="main" aria-labelledby="aria-label-identitieslist">
 	
 <div id="identitieslist" class="uibox listbox">
-<h2 class="boxtitle"><roundcube:label name="identities" /></h2>
+<h2 class="boxtitle" id="aria-label-identitieslist"><roundcube:label name="identities" /></h2>
 <div class="scroller withfooter">
-<roundcube:object name="identitiesList" id="identities-table" class="listing" cellspacing="0" summary="Identities list" noheader="true" editIcon="" />
+<roundcube:object name="identitiesList" id="identities-table" class="listing" noheader="true" editIcon="" role="listbox" />
 </div>
 <div class="boxfooter">
-<roundcube:button command="add" type="link" title="newidentity" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" condition="config:identities_level:0<2" /><roundcube:button command="delete" type="link" title="delete" class="listbutton delete disabled" classAct="listbutton delete" innerClass="inner" content="-" condition="config:identities_level:0<2" />
+<roundcube:button command="add" type="link" title="newidentity" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" label="newidentity" condition="config:identities_level:0<2" /><roundcube:button command="delete" type="link" title="delete" class="listbutton delete disabled" classAct="listbutton delete" innerClass="inner" label="delete" condition="config:identities_level:0<2" />
 </div>
 </div>
 
 <div id="identity-details" class="uibox contentbox">
 	<div class="iframebox">
-		<roundcube:object name="identityframe" id="preferences-frame" style="width:100%; height:100%" frameborder="0" src="/watermark.html" />
+		<roundcube:object name="identityframe" id="preferences-frame" style="width:100%; height:100%" frameborder="0" src="/watermark.html" title="arialabelidentityeditfrom" />
 	</div>
 </div>
 
diff --git a/skins/larry/templates/importcontacts.html b/skins/larry/templates/importcontacts.html
index a670d03..2bc1d4a 100644
--- a/skins/larry/templates/importcontacts.html
+++ b/skins/larry/templates/importcontacts.html
@@ -15,7 +15,7 @@
 </div>
 
 <div id="pluginbody" class="offset uibox contentbox">
-<h2 class="boxtitle"><roundcube:label name="importcontacts" /></h2>
+<h1 class="boxtitle"><roundcube:label name="importcontacts" /></h1>
 
 <div id="import-box" class="boxcontent">
 <roundcube:object name="importstep" class="propform" />
diff --git a/skins/larry/templates/login.html b/skins/larry/templates/login.html
index 64ff6be..557b029 100644
--- a/skins/larry/templates/login.html
+++ b/skins/larry/templates/login.html
@@ -7,8 +7,10 @@
 </head>
 <body>
 
+<h1 class="voice"><roundcube:object name="productname" /> <roundcube:label name="login" /></h1>
+
 <div id="login-form">
-<div class="box-inner">
+<div class="box-inner" role="main">
 <roundcube:object name="logo" src="/images/roundcube_logo.png" id="logo" />
 
 <roundcube:form name="form" method="post">
@@ -17,15 +19,15 @@
 
 </div>
 
-<div class="box-bottom">
+<div class="box-bottom" role="complementary">
 	<roundcube:object name="message" id="message" />
 	<noscript>
 		<p class="noscriptwarning"><roundcube:label name="noscriptwarning" /></p>
 	</noscript>
 </div>
 
-<div id="bottomline">
-	<roundcube:var name="config:product_name"> <roundcube:object name="version" condition="config:display_version" />
+<div id="bottomline" role="contentinfo">
+	<roundcube:object name="productname" /> <roundcube:object name="version" condition="config:display_version" />
 	<roundcube:if condition="config:support_url" />
 		&nbsp;&#9679;&nbsp; <a href="<roundcube:var name='config:support_url' />" target="_blank" class="support-link"><roundcube:label name="support" /></a>
 	<roundcube:endif />
diff --git a/skins/larry/templates/mail.html b/skins/larry/templates/mail.html
index 1e4a3ce..1ca5fbb 100644
--- a/skins/larry/templates/mail.html
+++ b/skins/larry/templates/mail.html
@@ -17,8 +17,11 @@
 
 <div id="mainscreen">
 
+<h1 class="voice"><roundcube:label name="mail" /></h1>
+
 <!-- toolbar -->
-<div id="messagetoolbar" class="toolbar">
+<h2 id="aria-label-toolbar" class="voice"><roundcube:label name="arialabeltoolbar" /></h2>
+<div id="messagetoolbar" class="toolbar" role="toolbar" aria-labelledby="aria-label-toolbar">
 	<roundcube:button command="checkmail" type="link" class="button checkmail disabled" classAct="button checkmail" classSel="button checkmail pressed" label="refresh" title="checkmail" />
 	<roundcube:include file="/includes/mailtoolbar.html" />
 </div>
@@ -27,14 +30,35 @@
 
 <!-- search filter -->
 <div id="searchfilter">
-	<roundcube:object name="searchfilter" class="searchfilter decorated" />
+	<label for="messagessearchfilter" class="voice"><roundcube:label name="arialabelmessagessearchfilter" /></label>
+	<roundcube:object name="searchfilter" class="searchfilter decorated" id="messagessearchfilter" aria-controls="messagelist" />
 </div>
 
 <!-- search box -->
-<div id="quicksearchbar" class="searchbox">
+<div id="quicksearchbar" class="searchbox" role="search" aria-labelledby="aria-label-searchform">
+<h2 id="aria-label-searchform" class="voice"><roundcube:label name="arialabelmailsearchform" /></h2>
+<label for="quicksearchbox" class="voice"><roundcube:label name="arialabelmailquicksearchbox" /></label>
 <roundcube:object name="searchform" id="quicksearchbox" />
-<roundcube:button name="searchmenulink" id="searchmenulink" class="iconbutton searchoptions" onclick="UI.show_popup('searchmenu');return false" title="searchmod" content=" " />
-<roundcube:button command="reset-search" id="searchreset" class="iconbutton reset" title="resetsearch" content=" " />
+<roundcube:button command="menu-open" prop="searchmenu" id="searchmenulink" class="iconbutton searchoptions" title="searchmod" label="options" aria-haspopup="true" aria-expanded="false" aria-owns="searchmenu-menu" />
+<roundcube:button command="reset-search" id="searchreset" class="iconbutton reset" title="resetsearch" label="resetsearch" />
+
+<div id="searchmenu" class="popupmenu" data-editable="true">
+	<h3 id="aria-label-searchmenu" class="voice"><roundcube:label name="searchmod" /></h3>
+	<ul class="toolbarmenu" id="searchmenu-menu" role="menu" aria-labelledby="aria-label-searchmenu">
+		<li role="menuitem"><label><input type="checkbox" name="s_mods[]" value="subject" id="s_mod_subject" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="subject" /></span></label></li>
+		<li role="menuitem"><label><input type="checkbox" name="s_mods[]" value="from" id="s_mod_from" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="from" /></span></label></li>
+		<li role="menuitem"><label><input type="checkbox" name="s_mods[]" value="to" id="s_mod_to" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="to" /></span></label></li>
+		<li role="menuitem"><label><input type="checkbox" name="s_mods[]" value="cc" id="s_mod_cc" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="cc" /></span></label></li>
+		<li role="menuitem"><label><input type="checkbox" name="s_mods[]" value="bcc" id="s_mod_bcc" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="bcc" /></span></label></li>
+		<li role="menuitem"><label><input type="checkbox" name="s_mods[]" value="body" id="s_mod_body" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="body" /></span></label></li>
+		<li role="menuitem"><label><input type="checkbox" name="s_mods[]" value="text" id="s_mod_text" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="msgtext" /></span></label></li>
+		<li role="separator" class="separator"><label><roundcube:label name="searchscope" /></label></li>
+		<li role="menuitem"><label><input type="radio" name="s_scope" value="base" id="s_scope_base" onclick="UI.set_searchscope(this)" /> <span><roundcube:label name="currentfolder" /></span></label></li>
+		<li role="menuitem"><label><input type="radio" name="s_scope" value="sub" id="s_scope_sub" onclick="UI.set_searchscope(this)" /> <span><roundcube:label name="subfolders" /></span></label></li>
+		<li role="menuitem"><label><input type="radio" name="s_scope" value="all" id="s_scope_all" onclick="UI.set_searchscope(this)" /> <span><roundcube:label name="allfolders" /></span></label></li>
+	</ul>
+</div>
+
 </div>
 
 </div>
@@ -43,13 +67,15 @@
 <div id="mailview-left">
 
 <!-- folders list -->
-<div id="mailboxcontainer" class="uibox listbox">
+<div id="mailboxcontainer" class="uibox listbox" role="navigation" aria-labelledby="aria-label-folderlist">
+<h2 id="aria-label-folderlist" class="voice"><roundcube:label name="arialabelfolderlist" /></h2>
 <div id="folderlist-content" class="scroller withfooter">
 <roundcube:object name="mailboxlist" id="mailboxlist" class="treelist listing" folder_filter="mail" unreadwrap="%s" />
 </div>
 <div id="folderlist-footer" class="boxfooter">
-	<roundcube:button name="mailboxmenulink" id="mailboxmenulink" type="link" title="folderactions" class="listbutton groupactions" onclick="UI.show_popup('mailboxmenu');return false" innerClass="inner" content="&#9881;" />
+	<roundcube:button name="mailboxmenulink" id="mailboxmenulink" type="link" title="folderactions" class="listbutton groupactions" onclick="UI.toggle_popup('mailboxmenu',event);return false" innerClass="inner" content="&#9881;" aria-haspopup="true" aria-expanded="false" aria-owns="mailboxoptionsmenu" />
 	<roundcube:if condition="env:quota" />
+		<span class="voice"><roundcube:label name="quota"></span>
 		<roundcube:object name="quotaDisplay" id="quotadisplay" class="countdisplay" display="text" />
 	<roundcube:endif />
 </div>
@@ -57,7 +83,7 @@
 
 </div>
 
-<div id="mailview-right">
+<div id="mailview-right" role="main">
 
 <roundcube:if condition="config:preview_pane == true" />
 <div id="mailview-top" class="uibox">
@@ -67,10 +93,20 @@
 
 <!-- messagelist -->
 <div id="messagelistcontainer" class="boxlistcontent">
+<h2 id="aria-label-messagelist" class="voice"><roundcube:label name="arialabelmessagelist" /></h2>
 <roundcube:object name="messages"
 	id="messagelist"
 	class="records-table messagelist sortheader fixedheader"
-	optionsmenuIcon="true" />
+	optionsmenuIcon="true"
+	aria-labelledby="aria-label-messagelist" />
+</div>
+
+<div class="voice" role="note">
+<h3><roundcube:label name="helplistnavigation" /></h3>
+<pre>
+<roundcube:label name="helplistkeyboardnavigation" />
+<roundcube:label name="helplistkeyboardnavmessages" />
+</pre>
 </div>
 
 <!-- list footer -->
@@ -81,33 +117,34 @@
 	</div>
 	
 	<div id="listselectors">
-	<a href="#select" id="listselectmenulink" class="menuselector" onclick="UI.show_popup('listselectmenu');return false"><span class="handle"><roundcube:label name="select" /></span></a>
+	<a href="#select" id="listselectmenulink" class="menuselector" onclick="UI.toggle_popup('listselectmenu', event);return false" aria-haspopup="true" aria-expanded="false" aria-owns="listselectmenu-menu"><span class="handle"><roundcube:label name="select" /></span></a>
 	<roundcube:if condition="env:threads" />
-		&nbsp; <a href="#threads" id="threadselectmenulink" class="menuselector" onclick="UI.show_popup('threadselectmenu');return false"><span class="handle"><roundcube:label name="threads" /></span></a>
+		&nbsp; <a href="#threads" id="threadselectmenulink" class="menuselector" onclick="UI.toggle_popup('threadselectmenu', event);return false" aria-haspopup="true" aria-expanded="false" aria-owns="threadselectmenu-menu"><span class="handle"><roundcube:label name="threads" /></span></a>
 	<roundcube:endif />
 	</div>
 
 	<div id="countcontrols" class="pagenav dark">
-		<roundcube:object name="messageCountDisplay" class="countdisplay" />
+		<roundcube:object name="messageCountDisplay" class="countdisplay" aria-live="polite" aria-relevant="text" />
 		<span class="pagenavbuttons">
-		<roundcube:button command="firstpage" type="link" class="button firstpage disabled" classAct="button firstpage" classSel="button firstpage pressed" innerClass="inner" title="firstpage" content="|&amp;lt;" />
-		<roundcube:button command="previouspage" type="link" class="button prevpage disabled" classAct="button prevpage" classSel="button prevpage pressed" innerClass="inner" title="previouspage" content="&amp;lt;" />
-		<roundcube:button command="nextpage" type="link" class="button nextpage disabled" classAct="button nextpage" classSel="button nextpage pressed" innerClass="inner" title="nextpage" content="&amp;gt;" />
-		<roundcube:button command="lastpage" type="link" class="button lastpage disabled" classAct="button lastpage" classSel="button lastpage pressed" innerClass="inner" title="lastpage" content="&amp;gt;|" />
+		<roundcube:button command="firstpage" type="link" class="button firstpage disabled" classAct="button firstpage" classSel="button firstpage pressed" innerClass="inner" title="firstpage" label="first" />
+		<roundcube:button command="previouspage" type="link" class="button prevpage disabled" classAct="button prevpage" classSel="button prevpage pressed" innerClass="inner" title="previouspage" label="previous" />
+		<roundcube:button command="nextpage" type="link" class="button nextpage disabled" classAct="button nextpage" classSel="button nextpage pressed" innerClass="inner" title="nextpage" label="next" />
+		<roundcube:button command="lastpage" type="link" class="button lastpage disabled" classAct="button lastpage" classSel="button lastpage pressed" innerClass="inner" title="lastpage" label="last" />
 		</span>
 	</div>
 
 	<roundcube:container name="listcontrols" id="listcontrols" />
 
-	<a href="#preview" id="mailpreviewtoggle" title="<roundcube:label name='previewpane' />"></a>
+	<a href="#preview" id="mailpreviewtoggle" class="iconbutton" title="<roundcube:label name='previewpane' />" role="button" tabindex="0"><roundcube:label name="previewpane" /></a>
 </div>
 
 </div><!-- end mailview-top -->
 
 <div id="mailview-bottom" class="uibox">
 
-<div id="mailpreviewframe" class="iframebox">
-<roundcube:object name="messagecontentframe" id="messagecontframe" style="width:100%; height:100%" frameborder="0" src="/watermark.html" />
+<div id="mailpreviewframe" class="iframebox" role="complementary" aria-labelledby="aria-label-mailpreviewframe">
+<h2 id="aria-label-mailpreviewframe" class="voice"><roundcube:label name="arialabelmailpreviewframe" /></h2>
+<roundcube:object name="messagecontentframe" id="messagecontframe" style="width:100%; height:100%" frameborder="0" src="/watermark.html" title="arialabelmailpreviewframe" />
 </div>
 
 </div><!-- end mailview-bottom -->
@@ -118,59 +155,47 @@
 
 </div><!-- end mainscreen -->
 
-<div id="searchmenu" class="popupmenu">
-	<ul class="toolbarmenu">
-		<li><label><input type="checkbox" name="s_mods[]" value="subject" id="s_mod_subject" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="subject" /></span></label></li>
-		<li><label><input type="checkbox" name="s_mods[]" value="from" id="s_mod_from" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="from" /></span></label></li>
-		<li><label><input type="checkbox" name="s_mods[]" value="to" id="s_mod_to" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="to" /></span></label></li>
-		<li><label><input type="checkbox" name="s_mods[]" value="cc" id="s_mod_cc" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="cc" /></span></label></li>
-		<li><label><input type="checkbox" name="s_mods[]" value="bcc" id="s_mod_bcc" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="bcc" /></span></label></li>
-		<li><label><input type="checkbox" name="s_mods[]" value="body" id="s_mod_body" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="body" /></span></label></li>
-		<li><label><input type="checkbox" name="s_mods[]" value="text" id="s_mod_text" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="msgtext" /></span></label></li>
-		<li class="separator"><label><roundcube:label name="searchscope" /></label></li>
-		<li><label><input type="radio" name="s_scope" value="base" id="s_scope_base" onclick="UI.set_searchscope(this)" /> <span><roundcube:label name="currentfolder" /></span></label></li>
-		<li><label><input type="radio" name="s_scope" value="sub" id="s_scope_sub" onclick="UI.set_searchscope(this)" /> <span><roundcube:label name="subfolders" /></span></label></li>
-		<li><label><input type="radio" name="s_scope" value="all" id="s_scope_all" onclick="UI.set_searchscope(this)" /> <span><roundcube:label name="allfolders" /></span></label></li>
+<div id="dragmessagemenu" class="popupmenu" aria-hidden="true">
+	<ul class="toolbarmenu" role="menu">
+		<li role="menuitem"><roundcube:button command="move" onclick="return rcmail.drag_menu_action('move')" label="move" classAct="active" /></li>
+		<li role="menuitem"><roundcube:button command="copy" onclick="return rcmail.drag_menu_action('copy')" label="copy" classAct="active" /></li>
 	</ul>
 </div>
 
-<div id="dragmessagemenu" class="popupmenu">
-	<ul class="toolbarmenu">
-		<li><roundcube:button command="move" onclick="return rcmail.drag_menu_action('move')" label="move" classAct="active" /></li>
-		<li><roundcube:button command="copy" onclick="return rcmail.drag_menu_action('copy')" label="copy" classAct="active" /></li>
-	</ul>
-</div>
-
-<div id="mailboxmenu" class="popupmenu">
-	<ul class="toolbarmenu" id="mailboxoptionsmenu">
-		<li><roundcube:button command="expunge" type="link" label="compact" classAct="active" /></li>
-		<li><roundcube:button command="purge" type="link" label="empty" classAct="active" /></li>
-		<li><roundcube:button command="import-messages" name="messageimport" type="link" classAct="active" label="importmessages" onclick="if(rcmail.command_enabled('import-messages'))UI.show_uploadform();return false" /></li>
-		<li><roundcube:button command="folders" task="settings" type="link" label="managefolders" classAct="active" /></li>
+<div id="mailboxmenu" class="popupmenu" aria-hidden="true">
+	<h3 id="aria-label-mailboxmenu" class="voice"><roundcube:label name="arialabelmailboxmenu" /></h3>
+	<ul class="toolbarmenu" id="mailboxoptionsmenu" role="menu" aria-labelledby="aria-label-mailboxmenu">
+		<li role="menuitem"><roundcube:button command="expunge" type="link" label="compact" classAct="active" /></li>
+		<li role="menuitem"><roundcube:button command="purge" type="link" label="empty" classAct="active" /></li>
+		<li role="menuitem"><roundcube:button command="import-messages" name="messageimport" type="link" classAct="active" label="importmessages" onclick="if(rcmail.command_enabled('import-messages'))UI.show_uploadform();return false" /></li>
+		<li role="menuitem"><roundcube:button command="folders" task="settings" type="link" label="managefolders" classAct="active" /></li>
 		<roundcube:container name="mailboxoptions" id="mailboxoptionsmenu" />
 	</ul>
 </div>
 
-<div id="listselectmenu" class="popupmenu dropdown">
-	<ul class="toolbarmenu iconized">
-		<li><roundcube:button command="select-all" type="link" label="all" class="icon" classAct="icon active" innerclass="icon mail" /></li>
-		<li><roundcube:button command="select-all" type="link" prop="page" label="currpage" class="icon" classAct="icon active" innerclass="icon list" /></li>
-		<li><roundcube:button command="select-all" type="link" prop="unread" label="unread" class="icon" classAct="icon active" innerclass="icon unread" /></li>
-		<li><roundcube:button command="select-all" type="link" prop="flagged" label="flagged" class="icon" classAct="icon active" innerclass="icon flagged" /></li>
-		<li><roundcube:button command="select-all" type="link" prop="invert" label="invert" class="icon" classAct="icon active" innerclass="icon invert" /></li>
-		<li><roundcube:button command="select-none" type="link" label="none" class="icon" classAct="icon active" innerclass="icon cross" /></li>
+<div id="listselectmenu" class="popupmenu dropdown" aria-hidden="true">
+	<h3 id="aria-label-listselectmenu" class="voice"><roundcube:label name="arialabellistselectmenu" /></h3>
+	<ul id="listselectmenu-menu" class="toolbarmenu iconized" role="menu" aria-labelledby="aria-label-listselectmenu">
+		<li role="menuitem"><roundcube:button command="select-all" type="link" label="all" class="icon" classAct="icon active" innerclass="icon mail" /></li>
+		<li role="menuitem"><roundcube:button command="select-all" type="link" prop="page" label="currpage" class="icon" classAct="icon active" innerclass="icon list" /></li>
+		<li role="menuitem"><roundcube:button command="select-all" type="link" prop="unread" label="unread" class="icon" classAct="icon active" innerclass="icon unread" /></li>
+		<li role="menuitem"><roundcube:button command="select-all" type="link" prop="flagged" label="flagged" class="icon" classAct="icon active" innerclass="icon flagged" /></li>
+		<li role="menuitem"><roundcube:button command="select-all" type="link" prop="invert" label="invert" class="icon" classAct="icon active" innerclass="icon invert" /></li>
+		<li role="menuitem"><roundcube:button command="select-none" type="link" label="none" class="icon" classAct="icon active" innerclass="icon cross" /></li>
 	</ul>
 </div>
 
-<div id="threadselectmenu" class="popupmenu dropdown">
-	<ul class="toolbarmenu">
-		<li><roundcube:button command="expand-all" type="link" label="expand-all" class="icon" classAct="icon active" innerclass="icon conversation" /></li>
-		<li><roundcube:button command="expand-unread" type="link" label="expand-unread" class="icon" classAct="icon active" innerclass="icon conversation" /></li>
-		<li><roundcube:button command="collapse-all" type="link" label="collapse-all" class="icon" classAct="icon active" innerclass="icon conversation" /></li>
+<div id="threadselectmenu" class="popupmenu dropdown" aria-hidden="true">
+	<h3 id="aria-label-threadselectmenu" class="voice"><roundcube:label name="arialabelthreadselectmenu" /></h3>
+	<ul id="threadselectmenu-menu" class="toolbarmenu" role="menu" aria-labelledby="aria-label-threadselectmenu">
+		<li role="menuitem"><roundcube:button command="expand-all" type="link" label="expand-all" class="icon" classAct="icon active" innerclass="icon conversation" /></li>
+		<li role="menuitem"><roundcube:button command="expand-unread" type="link" label="expand-unread" class="icon" classAct="icon active" innerclass="icon conversation" /></li>
+		<li role="menuitem"><roundcube:button command="collapse-all" type="link" label="collapse-all" class="icon" classAct="icon active" innerclass="icon conversation" /></li>
 	</ul>
 </div>
 
-<div id="listoptions" class="propform popupdialog">
+<div id="listoptions" class="propform popupdialog" role="dialog" aria-labelledby="aria-label-listoptions" aria-hidden="true">
+<h2 id="aria-label-listoptions" class="voice"><roundcube:label name="arialabelmessagelistoptions" /></h2>
 <roundcube:if condition="!in_array('list_cols', (array)config:dont_override)" />
 	<fieldset class="floating">
 		<legend><roundcube:label name="listcolumns" /></legend>
@@ -219,11 +244,12 @@
 	<br style="clear:both" />
 	<div class="formbuttons">
 		<roundcube:button command="menu-save" id="listmenusave" type="input" class="button mainaction" label="save" />
-		<roundcube:button command="menu-open" id="listmenucancel" type="input" class="button" label="cancel" />
+		<roundcube:button command="menu-close" prop="messagelistmenu" id="listmenucancel" type="input" class="button" label="cancel" />
 	</div>
 </div>
 
-<div id="upload-dialog" class="propform popupdialog" title="<roundcube:label name='importmessages' />">
+<div id="upload-dialog" class="propform popupdialog" title="<roundcube:label name='importmessages' />" aria-hidden="true">
+	<h2 id="aria-label-uploaddialog" class="voice"><roundcube:label name="arialabelmailimportdialog" /></h2>
 	<roundcube:object name="messageimportform" id="uploadform" buttons="no" />
 	<div class="formbuttons">
 		<roundcube:button command="import-messages" type="input" class="button mainaction" label="upload" />
diff --git a/skins/larry/templates/message.html b/skins/larry/templates/message.html
index a661f57..30c3d3d 100644
--- a/skins/larry/templates/message.html
+++ b/skins/larry/templates/message.html
@@ -10,8 +10,11 @@
 
 <div id="mainscreen">
 
+<h1 class="voice"><roundcube:object name="messageHeaders" valueOf="subject" /></h1>
+
 <!-- toolbar -->
-<div id="messagetoolbar" class="toolbar fullwidth">
+<h2 id="aria-label-toolbar" class="voice"><roundcube:label name="arialabeltoolbar" /></h2>
+<div id="messagetoolbar" class="toolbar fullwidth" role="toolbar" aria-labelledby="aria-label-toolbar">
 <roundcube:if condition="!env:extwin" />
 	<roundcube:button command="list" type="link" class="button back disabled" classAct="button back" classSel="button back pressed" label="back" />
 <roundcube:endif />
@@ -25,7 +28,8 @@
 <div id="mailview-left">
 
 <!-- folders list -->
-<div id="mailboxcontainer" class="uibox listbox">
+<div id="mailboxcontainer" class="uibox listbox" role="navigation" aria-labelledby="aria-label-folderlist">
+<h2 id="aria-label-folderlist" class="voice"><roundcube:label name="arialabelfolderlist" /></h2>
 <div class="scroller">
 <roundcube:object name="mailboxlist" id="mailboxlist" class="treelist listing" folder_filter="mail" unreadwrap="%s" />
 </div>
@@ -33,27 +37,22 @@
 
 </div>
 
-<div id="mailview-right" class="uibox">
+<div id="mailview-right" class="uibox" role="main">
 <roundcube:else />
 <roundcube:object name="mailboxlist" folder_filter="mail" type="js" />
 
-<div id="mailview-right" class="offset fullwidth uibox">
+<div id="mailview-right" class="offset fullwidth uibox" role="main">
 <roundcube:endif />
 
 <div id="messageheader">
 <span class="moreheaderstoggle"></span>
 
-<h2 class="subject"><roundcube:object name="messageHeaders" valueOf="subject" /></h2>
-<div class="message-headers">
-<roundcube:object name="messageHeaders" class="headers-table" addicon="/images/addcontact.png" exclude="subject" max="20" />
-</div>
-<roundcube:object name="messageFullHeaders" id="full-headers" />
-
 <!-- record navigation -->
-<div id="countcontrols" class="pagenav">
+<div id="countcontrols" class="pagenav" role="navigation" aria-labelledby="aria-label-countcontrols">
+	<h2 id="aria-label-countcontrols" class="voice"><roundcube:label name="arialabelmessagenav" /></h2>
 	<roundcube:object name="messageCountDisplay" class="countdisplay" />
-	<roundcube:button command="previousmessage" type="link" class="button prevpage disabled" classAct="button prevpage" classSel="button prevpage pressed" innerClass="inner" title="previousmessage" content="&amp;lt;" />
-	<roundcube:button command="nextmessage" type="link" class="button nextpage disabled" classAct="button nextpage" classSel="button nextpage pressed" innerClass="inner" title="nextmessage" content="&amp;gt;" />
+	<roundcube:button command="previousmessage" type="link" class="button prevpage disabled" classAct="button prevpage" classSel="button prevpage pressed" innerClass="inner" title="previousmessage" label="previous" />
+	<roundcube:button command="nextmessage" type="link" class="button nextpage disabled" classAct="button nextpage" classSel="button nextpage pressed" innerClass="inner" title="nextmessage" label="next" />
 </div>
 
 <roundcube:if condition="env:optional_format=='text'" />
@@ -70,15 +69,22 @@
 </div>
 <roundcube:endif />
 
+<h2 class="subject"><span class="voice"><roundcube:label name="subject" />: </span><roundcube:object name="messageHeaders" valueOf="subject" /></h2>
+<div class="message-headers">
+<roundcube:object name="messageHeaders" class="headers-table" addicon="/images/addcontact.png" exclude="subject" max="20" />
+</div>
+<roundcube:object name="messageFullHeaders" id="full-headers" />
 
 <div id="contactphoto"><roundcube:object name="contactphoto" /></div>
 </div>
 
 <div id="messagecontent">
-<div class="rightcol">
+<div class="rightcol" role="region" aria-labelledby="aria-label-messageattachments">
+<h2 id="aria-label-messageattachments" class="voice"><roundcube:label name="attachments" /></h2>
 <roundcube:object name="messageAttachments" id="attachment-list" class="attachmentslist" />
 </div>
-<div class="leftcol">
+<div class="leftcol" role="region" aria-labelledby="aria-label-messagebody">
+<h2 id="aria-label-messagebody" class="voice"><roundcube:label name="arialabelmessagebody" /></h2>
 <roundcube:object name="messageObjects" id="message-objects" />
 <roundcube:object name="messageBody" id="messagebody" headertableclass="message-partheaders headers-table" />
 </div>
@@ -92,11 +98,11 @@
 
 </div><!-- end mainscreen -->
 
-<div id="attachmentmenu" class="popupmenu">
-	<ul class="toolbarmenu">
-		<li><roundcube:button command="open-attachment" id="attachmenuopen" type="link" label="open" class="icon" classAct="icon active" innerclass="icon extwin" /></li>
-		<li><roundcube:button command="download-attachment" id="attachmenudownload" type="link" label="download" class="icon" classAct="icon active" innerclass="icon download" /></li>
-		<roundcube:container name="attachmentmenu" id="attachmentmenu" />
+<div id="attachmentmenu" class="popupmenu" aria-hidden="true">
+	<ul class="toolbarmenu" id="attachmentoptionsmenu" role="menu">
+		<li role="menuitem"><roundcube:button command="open-attachment" id="attachmenuopen" type="link" label="open" class="icon" classAct="icon active" innerclass="icon extwin" /></li>
+		<li role="menuitem"><roundcube:button command="download-attachment" id="attachmenudownload" type="link" label="download" class="icon" classAct="icon active" innerclass="icon download" /></li>
+		<roundcube:container name="attachmentmenu" id="attachmentoptionsmenu" />
 	</ul>
 </div>
 
diff --git a/skins/larry/templates/messageerror.html b/skins/larry/templates/messageerror.html
index d509ce8..c5c9521 100644
--- a/skins/larry/templates/messageerror.html
+++ b/skins/larry/templates/messageerror.html
@@ -16,11 +16,12 @@
 
 <div id="mainscreen">
 
+<h1 class="voice"><roundcube:label name="messageopenerror" /></h1>
+
 <!-- toolbar -->
-<div id="messagetoolbar" class="fullwidth">
-	<div id="mailtoolbar" class="toolbar">
-		<roundcube:button command="list" type="link" class="button back disabled" classAct="button back" classSel="button back pressed" label="back" />
-	</div>
+<h2 id="aria-label-toolbar" class="voice"><roundcube:label name="arialabeltoolbar" /></h2>
+<div id="messagetoolbar" class="toolbar fullwidth" role="toolbar" aria-labelledby="aria-label-toolbar">
+	<roundcube:button command="list" type="link" class="button back disabled" classAct="button back" classSel="button back pressed" label="back" />
 </div>
 
 <div id="mainscreencontent">
@@ -28,7 +29,8 @@
 <div id="mailview-left">
 
 <!-- folders list -->
-<div id="mailboxcontainer" class="uibox listbox">
+<div id="mailboxcontainer" class="uibox listbox" role="navigation" aria-labelledby="aria-label-folderlist">
+<h2 id="aria-label-folderlist" class="voice"><roundcube:label name="arialabelfolderlist" /></h2>
 <div class="scroller">
 	<roundcube:object name="mailboxlist" id="mailboxlist" class="treelist listing" folder_filter="mail" unreadwrap="%s" />
 </div>
@@ -36,7 +38,7 @@
 
 </div>
 
-<div id="mailview-right" class="offset uibox">
+<div id="mailview-right" class="uibox">
 
 <div id="messagecontent" class="watermark"></div>
 
diff --git a/skins/larry/templates/messagepart.html b/skins/larry/templates/messagepart.html
index 3b878c9..edf275f 100644
--- a/skins/larry/templates/messagepart.html
+++ b/skins/larry/templates/messagepart.html
@@ -10,7 +10,10 @@
 
 <div id="mainscreen">
 
-<div id="messagetoolbar" class="toolbar fullwidth">
+<h1 class="voice"><roundcube:label name="attachment" />: <roundcube:var name="env:filename" /></h1>
+
+<h2 id="aria-label-toolbar" class="voice"><roundcube:label name="arialabeltoolbar" /></h2>
+<div id="messagetoolbar" class="toolbar fullwidth" role="toolbar" aria-labelledby="aria-label-toolbar">
 	<roundcube:button command="download" type="link" class="button download disabled" classAct="button download" classSel="button download pressed" label="download" />
 	<roundcube:button command="print" type="link" class="button print disabled" classAct="button print" classSel="button print pressed" label="print" />
 	<roundcube:container name="toolbar" id="messagetoolbar" />
@@ -18,16 +21,17 @@
 
 <div id="mainscreencontent">
 
-<div id="messagepartheader" class="uibox listbox">
-	<h2 class="boxtitle"><roundcube:label name="properties" /></h2>
+<div id="messagepartheader" class="uibox listbox" role="contentinfo" aria-labelledby="aria-label-contentinfo">
+	<h2 class="boxtitle" id="aria-label-contentinfo"><roundcube:label name="properties" /></h2>
 	<div class="scroller">
 		<roundcube:object name="messagePartControls" class="listing" />
 	</div>
 </div>
 
-<div id="messagepartcontainer" class="uibox">
+<div id="messagepartcontainer" class="uibox" role="main" aria-labelledby="aria-label-messagepart">
+	<h2 id="aria-label-messagepart" class="voice"><roundcube:label name="arialabelattachmentpreview" /></h2>
 	<div class="iframebox">
-	<roundcube:object name="messagePartFrame" id="messagepartframe" frameborder="0" />
+	<roundcube:object name="messagePartFrame" id="messagepartframe" frameborder="0" title="arialabelattachmentpreview" />
 	</div>
 </div>
 
diff --git a/skins/larry/templates/messagepreview.html b/skins/larry/templates/messagepreview.html
index 4a6d76e..03fc915 100644
--- a/skins/larry/templates/messagepreview.html
+++ b/skins/larry/templates/messagepreview.html
@@ -7,9 +7,33 @@
 <body class="iframe fullheight">
 
 <div id="messageheader" class="previewheader">
-<h3 class="subject"><roundcube:object name="messageHeaders" valueOf="subject" /></h3>
 
-<a href="#details" id="previewheaderstoggle" class="moreheaderstoggle"><span class="iconlink" title="<roundcube:label name='togglemoreheaders' />"></span></a>
+<!-- record navigation -->
+<div id="countcontrols" role="toolbar" aria-labelledby="aria-label-messagetoolbar">
+<h2 id="aria-label-messagetoolbar" class="voice"><roundcube:label name="arialabelmessageactions" /></h2>
+<roundcube:if condition="env:optional_format=='text'" />
+	<span class="buttongroup">
+		<roundcube:button command="change-format" prop="html" type="link" class="button first changeformat html selected" innerClass="icon" title="changeformathtml" content="HTML" /><roundcube:button command="change-format" prop="text" type="link" class="button last changeformat text" classSel="button changeformat text pressed" innerClass="icon" title="changeformattext" content="Text" />
+	</span>
+	&nbsp;
+<roundcube:elseif condition="env:optional_format=='html'" />
+	<span class="buttongroup">
+		<roundcube:button command="change-format" prop="html" type="link" class="button first changeformat html" classSel="button changeformat html pressed" innerClass="icon" title="changeformathtml" content="HTML" /><roundcube:button command="change-format" prop="text" type="link" class="button last changeformat text selected" innerClass="icon" title="changeformattext" content="Text" />
+	</span>
+	&nbsp;
+<roundcube:endif />
+<roundcube:if condition="env:mailbox != config:drafts_mbox">
+	<roundcube:button command="reply" type="link" class="button reply" classSel="button reply pressed" innerClass="icon" title="replytomessage" label="replytomessage" />
+	<roundcube:button command="reply-all" type="link" class="button replyall" classSel="button replyall pressed" innerClass="icon" title="replytoallmessage" label="replytoallmessage" />
+	<roundcube:button command="forward" type="link" class="button forward" classSel="button forward pressed" innerClass="icon" title="forwardmessage" label="forwardmessage" />
+	&nbsp;
+<roundcube:endif />
+	<roundcube:button command="extwin" type="link" class="button extwin" classSel="button extwin pressed" innerClass="icon" title="openinextwin" label="openinextwin" />
+</div>
+
+<h3 class="subject"><span class="voice"><roundcube:label name="subject" />: </span><roundcube:object name="messageHeaders" valueOf="subject" /></h3>
+
+<a href="#details" id="previewheaderstoggle" class="moreheaderstoggle" aria-expanded="false"><span class="iconlink" title="<roundcube:label name='togglemoreheaders' />"></span></a>
 <div id="contactphoto"><roundcube:object name="contactphoto" /></div>
 
 <table class="headers-table" id="preview-shortheaders"><tbody><tr>
@@ -28,45 +52,25 @@
 
 <roundcube:object name="messageFullHeaders" id="full-headers" />
 
-<!-- record navigation -->
-<div id="countcontrols">
-<roundcube:if condition="env:optional_format=='text'" />
-	<span class="buttongroup">
-		<roundcube:button command="change-format" prop="html" type="link" class="button first changeformat html selected" innerClass="icon" title="changeformathtml" content="HTML" /><roundcube:button command="change-format" prop="text" type="link" class="button last changeformat text" classSel="button changeformat text pressed" innerClass="icon" title="changeformattext" content="Text" />
-	</span>
-	&nbsp;
-<roundcube:elseif condition="env:optional_format=='html'" />
-	<span class="buttongroup">
-		<roundcube:button command="change-format" prop="html" type="link" class="button first changeformat html" classSel="button changeformat html pressed" innerClass="icon" title="changeformathtml" content="HTML" /><roundcube:button command="change-format" prop="text" type="link" class="button last changeformat text selected" innerClass="icon" title="changeformattext" content="Text" />
-	</span>
-	&nbsp;
-<roundcube:endif />
-<roundcube:if condition="env:mailbox != config:drafts_mbox">
-	<roundcube:button command="reply" type="link" class="button reply" classSel="button reply pressed" innerClass="icon" title="replytomessage" content="&lt;-" />
-	<roundcube:button command="reply-all" type="link" class="button replyall" classSel="button replyall pressed" innerClass="icon" title="replytoallmessage" content="&lt;&lt;-" />
-	<roundcube:button command="forward" type="link" class="button forward" classSel="button forward pressed" innerClass="icon" title="forwardmessage" content="-&gt;" />
-	&nbsp;
-<roundcube:endif />
-	<roundcube:button command="extwin" type="link" class="button extwin" classSel="button extwin pressed" innerClass="icon" title="openinextwin" content="[]" />
 </div>
 
-</div>
-
-<div id="messagepreview">
-<div class="rightcol">
+<div id="messagepreview" role="main">
+<div class="rightcol" role="region" aria-labelledby="aria-label-messageattachments">
+<h2 id="aria-label-messageattachments" class="voice"><roundcube:label name="attachments" /></h2>
 <roundcube:object name="messageAttachments" id="attachment-list" class="attachmentslist" />
 </div>
-<div class="leftcol">
+<div class="leftcol" role="region" aria-labelledby="aria-label-messagebody">
+<h2 id="aria-label-messagebody" class="voice"><roundcube:label name="arialabelmessagebody" /></h2>
 <roundcube:object name="messageObjects" id="message-objects" />
 <roundcube:object name="messageBody" id="messagebody" headertableclass="message-partheaders headers-table" />
 </div>
 </div>
 
-<div id="attachmentmenu" class="popupmenu">
-	<ul class="toolbarmenu">
-		<li><roundcube:button command="open-attachment" id="attachmenuopen" type="link" label="open" class="icon" classAct="icon active" innerclass="icon extwin" /></li>
-		<li><roundcube:button command="download-attachment" id="attachmenudownload" type="link" label="download" class="icon" classAct="icon active" innerclass="icon download" /></li>
-		<roundcube:container name="attachmentmenu" id="attachmentmenu" />
+<div id="attachmentmenu" class="popupmenu" aria-hidden="true">
+	<ul class="toolbarmenu" id="attachmentoptionsmenu" role="menu">
+		<li role="menuitem"><roundcube:button command="open-attachment" id="attachmenuopen" type="link" label="open" class="icon" classAct="icon active" innerclass="icon extwin" /></li>
+		<li role="menuitem"><roundcube:button command="download-attachment" id="attachmenudownload" type="link" label="download" class="icon" classAct="icon active" innerclass="icon download" /></li>
+		<roundcube:container name="attachmentmenu" id="attachmentoptionsmenu" />
 	</ul>
 </div>
 
diff --git a/skins/larry/templates/responses.html b/skins/larry/templates/responses.html
index 8e68845..503ed21 100644
--- a/skins/larry/templates/responses.html
+++ b/skins/larry/templates/responses.html
@@ -10,23 +10,25 @@
 
 <div id="mainscreen" class="offset">
 
+<h1 class="voice"><roundcube:label name="settings" /> : <roundcube:label name="identities" /></h1>
+
 <roundcube:include file="/includes/settingstabs.html" />
 
-<div id="settings-right">
+<div id="settings-right" role="main" aria-labelledby="aria-label-responseslist">
 
 <div id="identitieslist" class="uibox listbox">
-<h2 class="boxtitle"><roundcube:label name="responses" /></h2>
+<h2 class="boxtitle" id="aria-label-responseslist"><roundcube:label name="responses" /></h2>
 <div class="scroller withfooter">
-<roundcube:object name="responsesList" id="identities-table" class="listing" cellspacing="0" summary="Responses list" noheader="true" />
+<roundcube:object name="responsesList" id="identities-table" class="listing" cellspacing="0" noheader="true" role="listbox" />
 </div>
 <div class="boxfooter">
-<roundcube:button command="add" type="link" title="savenewresponse" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button command="delete" type="link" title="delete" class="listbutton delete disabled" classAct="listbutton delete" innerClass="inner" content="-" />
+<roundcube:button command="add" type="link" title="savenewresponse" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" label="savenewresponse" /><roundcube:button command="delete" type="link" title="delete" class="listbutton delete disabled" classAct="listbutton delete" innerClass="inner" label="delete" />
 </div>
 </div>
 
 <div id="identity-details" class="uibox contentbox">
 	<div class="iframebox">
-		<roundcube:object name="responseframe" id="preferences-frame" style="width:100%; height:100%" frameborder="0" src="/watermark.html" />
+		<roundcube:object name="responseframe" id="preferences-frame" style="width:100%; height:100%" frameborder="0" src="/watermark.html" title="arialabelresonseeditfrom" />
 	</div>
 </div>
 
diff --git a/skins/larry/templates/settings.html b/skins/larry/templates/settings.html
index 08df768..406b9c9 100644
--- a/skins/larry/templates/settings.html
+++ b/skins/larry/templates/settings.html
@@ -10,19 +10,22 @@
 
 <div id="mainscreen" class="offset">
 
+<h1 class="voice"><roundcube:label name="settings" /> : <roundcube:label name="preferences" /></h1>
+
 <roundcube:include file="/includes/settingstabs.html" />
 
 <div id="settings-right">
 
-<div id="sectionslist" class="uibox listbox">
+<div id="sectionslist" class="uibox listbox" role="navigation" aria-labelledby="aria-label-pefsection">
+<h2 id="aria-label-pefsection" class="boxtitle"><roundcube:label name="section" /></h2>
 <div class="scroller">
-	<roundcube:object name="sectionslist" id="sections-table" class="listing" />
+	<roundcube:object name="sectionslist" id="sections-table" class="listing iconized" noheader="true" />
 </div>
 </div>
 
-<div id="preferences-box" class="uibox contentbox">
+<div id="preferences-box" class="uibox contentbox" role="main">
 	<div class="iframebox">
-		<roundcube:object name="prefsframe" id="preferences-frame" style="width:100%; height:100%" frameborder="0" src="/watermark.html" />
+		<roundcube:object name="prefsframe" id="preferences-frame" style="width:100%; height:100%" frameborder="0" src="/watermark.html" title="arialabelpreferencesform" />
 	</div>
 </div>
 
diff --git a/skins/larry/ui.js b/skins/larry/ui.js
index 391e7ab..47183f7 100644
--- a/skins/larry/ui.js
+++ b/skins/larry/ui.js
@@ -20,13 +20,10 @@
     searchmenu:         { editable:1, callback:searchmenu },
     attachmentmenu:     { },
     listoptions:        { editable:1 },
-    dragmenu:           { sticky:1 },
     groupmenu:          { above:1 },
     mailboxmenu:        { above:1 },
     spellmenu:          { callback: spellmenu },
-    // toggle: #1486823, #1486930
-    'attachment-form':  { editable:1, above:1, toggle:!bw.ie&&!bw.linux },
-    'upload-form':      { editable:1, toggle:!bw.ie&&!bw.linux }
+    'folder-selector':  { iconized:1 }
   };
 
   var me = this;
@@ -40,6 +37,7 @@
   this.init_tabs = init_tabs;
   this.show_about = show_about;
   this.show_popup = show_popup;
+  this.toggle_popup = toggle_popup;
   this.add_popup = add_popup;
   this.set_searchmod = set_searchmod;
   this.set_searchscope = set_searchscope;
@@ -138,8 +136,9 @@
 
     /***  mail task  ***/
     if (rcmail.env.task == 'mail') {
-      rcmail.addEventListener('menu-open', menu_open)
-        .addEventListener('menu-save', menu_save)
+      rcmail.addEventListener('menu-open', menu_toggle)
+        .addEventListener('menu-close', menu_toggle)
+        .addEventListener('menu-save', save_listoptions)
         .addEventListener('responseafterlist', function(e){ switch_view_mode(rcmail.env.threading ? 'thread' : 'list', true) })
         .addEventListener('responseaftersearch', function(e){ switch_view_mode(rcmail.env.threading ? 'thread' : 'list', true) });
 
@@ -157,7 +156,14 @@
 
         // add menu link for each attachment
         $('#attachment-list > li').each(function() {
-          $(this).append($('<a class="drop"></a>').click(function() { attachmentmenu(this); }));
+          $(this).append($('<a class="drop" tabindex="0" aria-haspopup="true">Show options</a>')
+              .bind('click keypress', function(e) {
+                  if (e.type != 'keypress' || rcube_event.get_keycode(e) == 13) {
+                      attachmentmenu(this, e);
+                      return false;
+                  }
+              })
+          );
         });
 
         if (get_pref('previewheaders') == '1') {
@@ -184,11 +190,13 @@
           }
         }
 
-        $('#composeoptionstoggle').click(function(){
-          $('#composeoptionstoggle').toggleClass('remove');
-          $('#composeoptions').toggle();
+        $('#composeoptionstoggle').click(function(e){
+          var expanded = $('#composeoptions').toggle().is(':visible');
+          $('#composeoptionstoggle').toggleClass('remove').attr('aria-expanded', expanded ? 'true' : 'false');
           layout_composeview();
-          save_pref('composeoptions', $('#composeoptions').is(':visible') ? '1' : '0');
+          save_pref('composeoptions', expanded ? '1' : '0');
+          if (!rcube_event.is_keyboard(e))
+            this.blur();
           return false;
         }).css('cursor', 'pointer');
 
@@ -210,7 +218,7 @@
       }
       else if (rcmail.env.action == 'list' || !rcmail.env.action) {
         var previewframe = $('#mailpreviewframe').is(':visible');
-        $('#mailpreviewtoggle').addClass(previewframe ? 'enabled' : 'closed').click(function(e){ toggle_preview_pane(e); return false });
+        $('#mailpreviewtoggle').addClass(previewframe ? 'enabled' : 'closed').attr('aria-expanded', previewframe ? 'true' : 'false').click(function(e){ toggle_preview_pane(e); return false });
         $('#maillistmode').addClass(rcmail.env.threading ? '' : 'selected').click(function(e){ switch_view_mode('list'); return false });
         $('#mailthreadmode').addClass(rcmail.env.threading ? 'selected' : '').click(function(e){ switch_view_mode('thread'); return false });
 
@@ -265,7 +273,9 @@
           orientation:'v', relative:true, start:266, min:180, size:12 }).init();
       }
       else if (rcmail.env.action == 'edit-prefs') {
-        $('<a href="#toggle">&#9660;</a>')
+        $('<a href="#toggle"></a>')
+            .text(env.toggleoptions)
+            .attr('title', env.toggleoptions)
             .addClass('advanced-toggle')
             .appendTo('#preferences-details fieldset.advanced legend');
 
@@ -333,6 +343,10 @@
           var val = $('option:selected', this).text();
           $(this).next().children().text(val);
         });
+
+      select
+        .on('focus', function(e){ overlay.addClass('focus'); })
+        .on('blur', function(e){ overlay.removeClass('focus'); });
     });
 
     // set min-width to show all toolbar buttons
@@ -341,60 +355,9 @@
       screen.css('min-width', $('.toolbar').width() + $('#quicksearchbar').width() + $('#searchfilter').width() + 30);
     }
 
-    $(document.body)
-      .bind('mouseup', body_mouseup)
-      .bind('keyup', function(e){
-        if (e.keyCode == 27) {
-          for (var id in popups) {
-            if (popups[id].is(':visible'))
-              show_popup(id, false);
-          }
-        }
-      });
-
-    $('iframe').load(function(e){
-      // this = iframe
-      try {
-        var doc = this.contentDocument ? this.contentDocument : this.contentWindow ? this.contentWindow.document : null;
-        $(doc).mouseup(body_mouseup);
-      }
-      catch (e) {
-        // catch possible "Permission denied" error in IE
-      };
-    })
-    .contents().mouseup(body_mouseup);
-
     // don't use $(window).resize() due to some unwanted side-effects
     window.onresize = resize;
     resize();
-  }
-
-  /**
-   * Handler for mouse-up events on the document body.
-   * This will close all open popup menus
-   */
-  function body_mouseup(e)
-  {
-    var config, obj, target = e.target;
-
-    if (target.className == 'inner')
-        target = e.target.parentNode;
-
-    for (var id in popups) {
-      obj = popups[id];
-      config = popupconfig[id];
-      if (obj.is(':visible')
-        && target.id != id+'link'
-        && target != obj.get(0)  // check if scroll bar was clicked (#1489832)
-        && !config.toggle
-        && (!config.editable || !target_overlaps(target, obj.get(0)))
-        && (!config.sticky || !rcube_mouse_is_over(e, obj.get(0)))
-        && !$(target).is('.folder-selector-link')
-      ) {
-        var myid = id+'';
-        window.setTimeout(function() { show_popupmenu(myid, false); }, 10);
-      }
-    }
   }
 
   /**
@@ -475,6 +438,8 @@
           minHeight: 90
         }).show();
 
+      me.messagedialog.closest('div[role=dialog]').attr('role', 'alertdialog');
+
       me.message_timer = window.setTimeout(dialog_close, p.timeout);
     }
   }
@@ -489,7 +454,7 @@
     $('#message-objects div a').addClass('button');
 
     if (!$('#attachment-list li').length) {
-      $('div.rightcol').hide();
+      $('div.rightcol').hide().attr('aria-hidden', 'true');
       $('div.leftcol').css('margin-right', '0');
     }
   }
@@ -586,75 +551,35 @@
   /**
    * Trigger for popup menus
    */
+  function toggle_popup(popup, e, config)
+  {
+    // auto-register menu object
+    if (config || !popupconfig[popup])
+      add_popup(popup, config);
+
+    return rcmail.command('menu-open', popup, e.target, e);
+  }
+
+  /**
+   * (Deprecated) trigger for popup menus
+   */
   function show_popup(popup, show, config)
   {
     // auto-register menu object
     if (config || !popupconfig[popup])
       add_popup(popup, config);
 
-    var visible = show_popupmenu(popup, show),
-      config = popupconfig[popup];
-    if (typeof config.callback == 'function')
-      config.callback(visible);
-  }
-
-  /**
-   * Show/hide a specific popup menu
-   */
-  function show_popupmenu(popup, show)
-  {
-    var obj = popups[popup],
-      config = popupconfig[popup],
-      ref = $(config.link ? config.link : '#'+popup+'link'),
-      above = config.above;
-
-    if (!obj) {
-      obj = popups[popup] = $('#'+popup);
-      obj.appendTo(document.body);  // move them to top for proper absolute positioning
-    }
-
-    if (!obj || !obj.length)
-      return false;
-
-    if (typeof show == 'undefined')
-      show = obj.is(':visible') ? false : true;
-    else if (config.toggle && show && obj.is(':visible'))
-      show = false;
-
-    if (show && ref.length) {
-      var parent = ref.parent(),
-        win = $(window),
-        pos;
-
-      if (parent.hasClass('dropbutton'))
-        ref = parent;
-
+    config = popupconfig[popup] || {};
+    var ref = $(config.link ? config.link : '#'+popup+'link'),
       pos = ref.offset();
-      ref.offsetHeight = ref.outerHeight();
-      if (!above && pos.top + ref.offsetHeight + obj.height() > win.height())
-        above = true;
-      if (pos.left + obj.width() > win.width())
-        pos.left = win.width() - obj.width() - 12;
+    if (ref.has('.inner'))
+      ref = ref.children('.inner');
 
-      obj.css({ left:pos.left, top:(pos.top + (above ? -obj.height() : ref.offsetHeight)) });
-    }
-
-    obj[show?'show':'hide']();
-
-    return show;
-  }
-
-  /**
-   *
-   */
-  function target_overlaps(target, elem)
-  {
-    while (target.parentNode) {
-      if (target.parentNode == elem)
-        return true;
-      target = target.parentNode;
-    }
-    return false;
+    // fire command with simulated mouse click event
+    return rcmail.command('menu-open',
+      { menu:popup, show:show },
+      ref.get(0),
+      $.Event('click', { target:ref.get(0), pageX:pos.left, pageY:pos.top, clientX:pos.left, clientY:pos.top }));
   }
 
 
@@ -670,7 +595,7 @@
       topstyles, bottomstyles, uid;
 
     frame.toggle();
-    button.removeClass().addClass(visible ? 'enabled' : 'closed');
+    button.removeClass().toggleClass('enabled closed').attr('aria-expanded', visible ? 'true' : 'false');
 
     if (visible) {
       $('#mailview-top').removeClass('fullheight').css({ bottom:'auto' });
@@ -720,9 +645,9 @@
 
     // add toggle button to full headers table
     if (full.is(':visible'))
-      button.attr('href', '#hide').removeClass('add').addClass('remove')
+      button.attr('href', '#hide').removeClass('add').addClass('remove').attr('aria-expanded', 'true');
     else
-      button.attr('href', '#details').removeClass('remove').addClass('add')
+      button.attr('href', '#details').removeClass('remove').addClass('add').attr('aria-expanded', 'false');
 
     save_pref('previewheaders', full.is(':visible') ? '1' : '0');
   }
@@ -734,25 +659,57 @@
   function switch_view_mode(mode, force)
   {
     if (force || !$('#mail'+mode+'mode').hasClass('disabled')) {
-      $('#maillistmode, #mailthreadmode').removeClass('selected');
-      $('#mail'+mode+'mode').addClass('selected');
+      $('#maillistmode, #mailthreadmode').removeClass('selected').attr('tabindex', '0').attr('aria-disabled', 'false');
+      $('#mail'+mode+'mode').addClass('selected').attr('tabindex', '-1').attr('aria-disabled', 'true');
     }
   }
 
 
-  /**** popup callbacks ****/
+  /**** popup menu callbacks ****/
 
-  function menu_open(p)
+  /**
+   * Handler for menu-open and menu-close events
+   */
+  function menu_toggle(p)
   {
-    if (p && p.props && p.props.menu == 'attachmentmenu')
-      show_popupmenu('attachmentmenu');
-    else
-      show_listoptions();
-  }
+    if (p && p.name == 'messagelistmenu') {
+      show_listoptions(p);
+    }
+    else if (p) {
+      // adjust menu position according to config
+      var config = popupconfig[p.name] || {},
+        ref = $(config.link || '#'+p.name+'link'),
+        visible = p.obj && p.obj.is(':visible'),
+        above = config.above;
 
-  function menu_save(prop)
-  {
-    save_listoptions();
+      // fix position according to config
+      if (p.obj && visible && ref.length) {
+        var parent = ref.parent(),
+          win = $(window), pos;
+
+        if (parent.hasClass('dropbutton'))
+          ref = parent;
+
+        if (config.above || ref.hasClass('dropbutton')) {
+          pos = ref.offset();
+          p.obj.css({ left:pos.left+'px', top:(pos.top + (config.above ? -p.obj.height() : ref.outerHeight()))+'px' });
+        }
+      }
+
+      // add the right classes
+      if (p.obj && config.iconized) {
+        p.obj.children('ul').addClass('iconized');
+      }
+
+      // apply some data-attributes from menu config
+      if (p.obj && config.editable)
+        p.obj.attr('data-editable', 'true');
+
+      // trigger callback function
+      if (typeof config.callback == 'function') {
+        config.callback(visible, p);
+      }
+    }
   }
 
   function searchmenu(show)
@@ -789,7 +746,7 @@
     }
   }
 
-  function attachmentmenu(elem)
+  function attachmentmenu(elem, event)
   {
     var id = elem.parentNode.id.replace(/^attach/, '');
 
@@ -802,41 +759,44 @@
     });
 
     popupconfig.attachmentmenu.link = elem;
-    rcmail.command('menu-open', {menu: 'attachmentmenu', id: id});
+    rcmail.command('menu-open', {menu: 'attachmentmenu', id: id}, elem, event);
   }
 
-  function spellmenu(show)
+  function spellmenu(show, p)
   {
-    var link, li,
+    var k, link, li,
       lang = rcmail.spellcheck_lang(),
-      menu = popups.spellmenu,
-      ul = $('ul', menu);
+      ul = $('ul', p.obj);
 
     if (!ul.length) {
-      ul = $('<ul class="toolbarmenu selectable">');
+      ul = $('<ul class="toolbarmenu selectable" role="menu">');
 
-      for (i in rcmail.env.spell_langs) {
-        li = $('<li>');
-        link = $('<a href="#"></a>').text(rcmail.env.spell_langs[i])
-          .addClass('active').data('lang', i)
-          .click(function() {
-            rcmail.spellcheck_lang_set($(this).data('lang'));
+      for (k in rcmail.env.spell_langs) {
+        li = $('<li role="menuitem">');
+        link = $('<a href="#'+k+'" tabindex="0"></a>').text(rcmail.env.spell_langs[k])
+          .addClass('active').data('lang', k)
+          .bind('click keypress', function(e) {
+              if (e.type != 'keypress' || rcube_event.get_keycode(e) == 13) {
+                  rcmail.spellcheck_lang_set($(this).data('lang'));
+                  rcmail.hide_menu('spellmenu', e);
+                  return false;
+              }
           });
 
         link.appendTo(li);
         li.appendTo(ul);
       }
 
-      ul.appendTo(menu);
+      ul.appendTo(p.obj);
     }
 
     // select current language
     $('li', ul).each(function() {
       var el = $('a', this);
       if (el.data('lang') == lang)
-        el.addClass('selected');
+        el.addClass('selected').attr('aria-selected', 'true');
       else if (el.hasClass('selected'))
-        el.removeClass('selected');
+        el.removeClass('selected').removeAttr('aria-selected');
     });
   }
 
@@ -844,13 +804,13 @@
   /**
    *
    */
-  function show_listoptions()
+  function show_listoptions(p)
   {
     var $dialog = $('#listoptions');
 
     // close the dialog
     if ($dialog.is(':visible')) {
-      $dialog.dialog('close');
+      $dialog.dialog('close', p.originalEvent);
       return;
     }
 
@@ -869,8 +829,13 @@
       resizable: false,
       closeOnEscape: true,
       title: null,
-      close: function() {
+      open: function(e) {
+        setTimeout(function(){ $dialog.find('a, input:not(:disabled)').not('[aria-disabled=true]').first().focus(); }, 100);
+      },
+      close: function(e) {
         $dialog.dialog('destroy').hide();
+        if (e.originalEvent && rcube_event.is_keyboard(e.originalEvent))
+          $('#listmenulink').focus();
       },
       minWidth: 500,
       width: $dialog.width()+25
@@ -881,9 +846,12 @@
   /**
    *
    */
-  function save_listoptions()
+  function save_listoptions(p)
   {
     $('#listoptions').dialog('close');
+
+    if (rcube_event.is_keyboard(p.originalEvent))
+      $('#listmenulink').focus();
 
     var sort = $('input[name="sort_col"]:checked').val(),
       ord = $('input[name="sort_ord"]:checked').val(),
@@ -982,7 +950,7 @@
       });
   }
 
-  function show_uploadform()
+  function show_uploadform(e)
   {
     var $dialog = $('#upload-dialog');
 
@@ -1008,6 +976,10 @@
       resizable: false,
       closeOnEscape: true,
       title: $dialog.attr('title'),
+      open: function(e) {
+        if (!document.all)
+          $('input[type=file]', $dialog).first().click();
+      },
       close: function() {
         try { $('#upload-dialog form').get(0).reset(); }
         catch(e){ }  // ignore errors
@@ -1017,9 +989,6 @@
       },
       width: 480
     }).show();
-
-    if (!document.all)
-      $('input[type=file]', $dialog).first().click();
   }
 
   function add_uploadfile(e)
@@ -1086,47 +1055,35 @@
       content.attr('id', id);
     }
 
-    // first hide not selected tabs
-    current = current || 0;
-    fs.each(function(idx) { if (idx != current) $(this).hide(); });
-
     // create tabs container
-    var tabs = $('<div>').addClass('tabsbar').prependTo(content);
+    var tabs = $('<ul>').addClass('tabsbar').prependTo(content);
 
     // convert fildsets into tabs
     fs.each(function(idx) {
-      var tab, a, elm = $(this), legend = elm.children('legend');
+      var tab, a, elm = $(this),
+        legend = elm.children('legend'),
+        tid = id + '-t' + idx;
 
       // create a tab
-      a   = $('<a>').text(legend.text()).attr('href', '#');
-      tab = $('<span>').attr({'id': 'tab'+idx, 'class': 'tablink'})
-        .click(function() { show_tab(id, idx); return false })
+      a   = $('<a>').text(legend.text()).attr('href', '#' + tid);
+      tab = $('<li>').addClass('tablink');
 
       // remove legend
       legend.remove();
-      // style fieldset
-      elm.addClass('tab');
-      // style selected tab
-      if (idx == current)
-        tab.addClass('selected');
+
+      // link fieldset with tab item
+      elm.attr('id', tid);
 
       // add the tab to container
       tab.append(a).appendTo(tabs);
     });
-  }
 
-  function show_tab(id, index)
-  {
-    var fs = $('#'+id).children('fieldset');
-
-    fs.each(function(idx) {
-      // Show/hide fieldset (tab content)
-      $(this)[index==idx ? 'show' : 'hide']();
-      // Select/unselect tab
-      $('#tab'+idx).toggleClass('selected', idx==index);
+    // use jquery UI tabs widget to do the interaction and styling
+    content.tabs({
+      active: current || 0,
+      heightStyle: 'content',
+      activate: function(e, ui) {resize(); }
     });
-
-    resize();
   }
 
   /**
@@ -1237,6 +1194,7 @@
     this.handle = $('<div>')
       .attr('id', this.id)
       .attr('unselectable', 'on')
+      .attr('role', 'presentation')
       .addClass('splitter ' + (this.horizontal ? 'splitter-h' : 'splitter-v'))
       .appendTo(this.parent)
       .bind('mousedown', onDragStart);

--
Gitblit v1.9.1