diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,7 +7,7 @@ return array( 'names' => array( - 'core.pkg.css' => 'b7ba02ba', + 'core.pkg.css' => '4189f007', 'core.pkg.js' => '417722ff', 'darkconsole.pkg.js' => 'ca8671ce', 'differential.pkg.css' => '8a064eb7', @@ -105,7 +105,7 @@ 'rsrc/css/application/subscriptions/subscribers-list.css' => '5bb30c78', 'rsrc/css/application/tokens/tokens.css' => '5f7bca25', 'rsrc/css/application/uiexample/example.css' => '528b19de', - 'rsrc/css/core/core.css' => 'da26ddb2', + 'rsrc/css/core/core.css' => 'c0737fe7', 'rsrc/css/core/remarkup.css' => '98a7627b', 'rsrc/css/core/syntax.css' => '3c18c1cb', 'rsrc/css/core/z-index.css' => '0d89d53c', @@ -686,7 +686,7 @@ 'phabricator-busy' => '6453c869', 'phabricator-chatlog-css' => '852140ff', 'phabricator-content-source-view-css' => '4b8b05d4', - 'phabricator-core-css' => 'da26ddb2', + 'phabricator-core-css' => 'c0737fe7', 'phabricator-countdown-css' => '86b7b0a0', 'phabricator-crumbs-view-css' => '0222cbe0', 'phabricator-drag-and-drop-file-upload' => 'ae6abfba', diff --git a/src/applications/auth/application/PhabricatorApplicationAuth.php b/src/applications/auth/application/PhabricatorApplicationAuth.php --- a/src/applications/auth/application/PhabricatorApplicationAuth.php +++ b/src/applications/auth/application/PhabricatorApplicationAuth.php @@ -40,6 +40,7 @@ ->setWorkflow(true) ->setHref('/logout/') ->setSelected(($controller instanceof PhabricatorLogoutController)) + ->setAural(pht('Log Out')) ->setOrder(900); $items[] = $item; } else { @@ -53,6 +54,7 @@ // TODO: Login icon? ->setIcon('power') ->setHref('/auth/start/') + ->setAural(pht('Log In')) ->setOrder(900); $items[] = $item; } diff --git a/src/applications/help/application/PhabricatorApplicationHelp.php b/src/applications/help/application/PhabricatorApplicationHelp.php --- a/src/applications/help/application/PhabricatorApplicationHelp.php +++ b/src/applications/help/application/PhabricatorApplicationHelp.php @@ -31,10 +31,13 @@ } if ($application && $application->getHelpURI()) { + $help_name = pht('%s Help', $application->getName()); + $item = id(new PHUIListItemView()) - ->setName(pht('%s Help', $application->getName())) + ->setName($help_name) ->addClass('core-menu-item') ->setIcon('info-sm') + ->setAural($help_name) ->setOrder(200) ->setHref($application->getHelpURI()); $items[] = $item; diff --git a/src/applications/home/application/PhabricatorApplicationHome.php b/src/applications/home/application/PhabricatorApplicationHome.php --- a/src/applications/home/application/PhabricatorApplicationHome.php +++ b/src/applications/home/application/PhabricatorApplicationHome.php @@ -60,6 +60,7 @@ ->setHref('/home/create/') ->addSigil('quick-create-menu') ->setID($create_id) + ->setAural(pht('Quick Create')) ->setOrder(300); $items[] = $item; } diff --git a/src/applications/home/controller/PhabricatorHomeController.php b/src/applications/home/controller/PhabricatorHomeController.php --- a/src/applications/home/controller/PhabricatorHomeController.php +++ b/src/applications/home/controller/PhabricatorHomeController.php @@ -167,7 +167,7 @@ } } - $nav->addFilter( + $nav->addFilter( '', pht('Customize Applications...'), '/settings/panel/home/'); diff --git a/src/applications/meta/view/PhabricatorApplicationLaunchView.php b/src/applications/meta/view/PhabricatorApplicationLaunchView.php --- a/src/applications/meta/view/PhabricatorApplicationLaunchView.php +++ b/src/applications/meta/view/PhabricatorApplicationLaunchView.php @@ -39,9 +39,10 @@ $application->getName()); if ($application->isBeta()) { - $content[] = phutil_tag( + $content[] = javelin_tag( 'span', array( + 'aural' => false, 'class' => 'phabricator-application-beta', ), "\xCE\xB2"); diff --git a/src/applications/people/application/PhabricatorApplicationPeople.php b/src/applications/people/application/PhabricatorApplicationPeople.php --- a/src/applications/people/application/PhabricatorApplicationPeople.php +++ b/src/applications/people/application/PhabricatorApplicationPeople.php @@ -120,6 +120,7 @@ ->setName($user->getUsername()) ->setHref('/p/'.$user->getUsername().'/') ->addClass('core-menu-item') + ->setAural(pht('Profile')) ->setOrder(100); $classes = array( diff --git a/src/applications/settings/application/PhabricatorApplicationSettings.php b/src/applications/settings/application/PhabricatorApplicationSettings.php --- a/src/applications/settings/application/PhabricatorApplicationSettings.php +++ b/src/applications/settings/application/PhabricatorApplicationSettings.php @@ -46,6 +46,7 @@ ->addClass('core-menu-item') ->setSelected($selected) ->setHref('/settings/') + ->setAural(pht('Settings')) ->setOrder(400); $items[] = $item; } diff --git a/src/infrastructure/javelin/markup.php b/src/infrastructure/javelin/markup.php --- a/src/infrastructure/javelin/markup.php +++ b/src/infrastructure/javelin/markup.php @@ -36,6 +36,20 @@ } } + if (isset($attributes['aural'])) { + if ($attributes['aural']) { + $class = idx($attributes, 'class', ''); + $class = rtrim('aural-only '.$class); + $attributes['class'] = $class; + } else { + $class = idx($attributes, 'class', ''); + $class = rtrim('visual-only '.$class); + $attributes['class'] = $class; + $attributes['aria-hidden'] = 'true'; + } + unset($attributes['aural']); + } + return phutil_tag($tag, $attributes, $content); } diff --git a/src/view/form/control/PhabricatorRemarkupControl.php b/src/view/form/control/PhabricatorRemarkupControl.php --- a/src/view/form/control/PhabricatorRemarkupControl.php +++ b/src/view/form/control/PhabricatorRemarkupControl.php @@ -133,13 +133,22 @@ $target = '_blank'; } + $content = null; + $tip = idx($spec, 'tip'); if ($tip) { $meta['tip'] = $tip; + $content = phutil_tag( + 'span', + array( + 'class' => 'aural-only', + ), + $tip); } require_celerity_resource('sprite-icons-css'); + $buttons[] = javelin_tag( 'a', array( @@ -156,7 +165,7 @@ array( 'class' => 'remarkup-assist sprite-icons remarkup-assist-'.$action, ), - '')); + $content)); } $buttons = phutil_tag( diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -430,6 +430,10 @@ $classes[] = 'printable'; } + if ($this->getRequest()->getStr('__aural__')) { + $classes[] = 'audible'; + } + return implode(' ', $classes); } diff --git a/src/view/page/menu/PhabricatorMainMenuView.php b/src/view/page/menu/PhabricatorMainMenuView.php --- a/src/view/page/menu/PhabricatorMainMenuView.php +++ b/src/view/page/menu/PhabricatorMainMenuView.php @@ -34,9 +34,10 @@ $alerts = array(); $search_button = ''; $app_button = ''; + $aural = null; if ($user->isLoggedIn() && $user->isUserActivated()) { - list($menu, $dropdowns) = $this->renderNotificationMenu(); + list($menu, $dropdowns, $aural) = $this->renderNotificationMenu(); $alerts[] = $menu; $menus = array_merge($menus, $dropdowns); $app_button = $this->renderApplicationMenuButton($header_id); @@ -51,14 +52,24 @@ $search_menu = $this->renderPhabricatorSearchMenu(); if ($alerts) { - $alerts = phutil_tag( + $alerts = javelin_tag( 'div', array( 'class' => 'phabricator-main-menu-alerts', + 'aural' => false, ), $alerts); } + if ($aural) { + $aural = javelin_tag( + 'span', + array( + 'aural' => true, + ), + phutil_implode_html(' ', $aural)); + } + $application_menu = $this->renderApplicationMenu(); $classes = array(); $classes[] = 'phabricator-main-menu'; @@ -76,6 +87,7 @@ $search_button, $this->renderPhabricatorLogo(), $alerts, + $aural, $application_menu, $search_menu, $menus, @@ -236,12 +248,20 @@ 'class' => 'phabricator-main-menu-logo', 'href' => '/', ), - phutil_tag( - 'span', - array( - 'class' => 'sprite-menu menu-logo-image '.$class, - ), - '')); + array( + javelin_tag( + 'span', + array( + 'aural' => true, + ), + pht('Home')), + phutil_tag( + 'span', + array( + 'class' => 'sprite-menu menu-logo-image '.$class, + ), + ''), + )); } private function renderNotificationMenu() { @@ -256,6 +276,8 @@ 'alert-notifications', ); + $aural = array(); + $message_tag = ''; $message_notification_dropdown = ''; $conpherence = 'PhabricatorApplicationConpherence'; @@ -270,6 +292,20 @@ ->withParticipationStatus($unread_status) ->execute(); $message_count_number = idx($unread, $user->getPHID(), 0); + + if ($message_count_number) { + $aural[] = phutil_tag( + 'a', + array( + 'href' => '/conpherence/', + ), + pht( + '%s unread messages.', + new PhutilNumber($message_count_number))); + } else { + $aural[] = pht('No messages.'); + } + if ($message_count_number > 999) { $message_count_number = "\xE2\x88\x9E"; } @@ -333,6 +369,19 @@ $count_number = id(new PhabricatorFeedStoryNotification()) ->countUnread($user); + if ($count_number) { + $aural[] = phutil_tag( + 'a', + array( + 'href' => '/notification/', + ), + pht( + '%s unread notifications.', + new PhutilNumber($count_number))); + } else { + $aural[] = pht('No notifications.'); + } + if ($count_number > 999) { $count_number = "\xE2\x88\x9E"; } @@ -397,8 +446,12 @@ } return array( - hsprintf('%s%s', $bubble_tag, $message_tag), - $dropdowns + array( + $bubble_tag, + $message_tag, + ), + $dropdowns, + $aural, ); } diff --git a/src/view/phui/PHUIListItemView.php b/src/view/phui/PHUIListItemView.php --- a/src/view/phui/PHUIListItemView.php +++ b/src/view/phui/PHUIListItemView.php @@ -25,6 +25,16 @@ private $renderNameAsTooltip; private $statusColor; private $order; + private $aural; + + public function setAural($aural) { + $this->aural = $aural; + return $this; + } + + public function getAural() { + return $this->aural; + } public function setOrder($order) { $this->order = $order; @@ -170,10 +180,21 @@ $external = " \xE2\x86\x97"; } + // If this element has an aural representation, make any name visual + // only. This is primarily dealing with the links in the main menu like + // "Profile" and "Logout". If we don't hide the name, the mobile + // version of these elements will have two redundant names. + + $classes = array(); + $classes[] = 'phui-list-item-name'; + if ($this->aural !== null) { + $classes[] = 'visual-only'; + } + $name = phutil_tag( 'span', array( - 'class' => 'phui-list-item-name', + 'class' => implode(' ', $classes), ), array( $this->name, @@ -182,6 +203,16 @@ } } + $aural = null; + if ($this->aural !== null) { + $aural = phutil_tag( + 'span', + array( + 'class' => 'aural-only', + ), + $this->aural); + } + if ($this->icon) { $icon_name = $this->icon; if ($this->getDisabled()) { @@ -210,6 +241,7 @@ 'sigil' => $sigil, ), array( + $aural, $icon, $this->renderChildren(), $name, diff --git a/webroot/rsrc/css/core/core.css b/webroot/rsrc/css/core/core.css --- a/webroot/rsrc/css/core/core.css +++ b/webroot/rsrc/css/core/core.css @@ -123,3 +123,30 @@ background: #bbbbbb; border: none; } + +.aural-only { + position: absolute !important; + clip: rect(1px, 1px, 1px, 1px); +} + +.visual-only { + /* These elements are hidden by the 'aria-hidden' attribute. */ +} + +.audible .aural-only { + clip: auto; + background: #006699; + color: #ffffff; + z-index: 100; +} + +.audible .aural-only a { + color: #ffffff; + font-weight: bold; +} + +.audible .visual-only { + position: absolute !important; + background: #990066; + opacity: 0.25; +}