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' => 'c88bd7c6', + 'core.pkg.css' => 'd7ef9652', 'core.pkg.js' => '417722ff', 'darkconsole.pkg.js' => 'ca8671ce', 'differential.pkg.css' => '12c11318', @@ -104,7 +104,7 @@ 'rsrc/css/application/subscriptions/subscribers-list.css' => '5bb30c78', 'rsrc/css/application/tokens/tokens.css' => '5f7bca25', 'rsrc/css/application/uiexample/example.css' => 'd07a5869', - 'rsrc/css/core/core.css' => 'da26ddb2', + 'rsrc/css/core/core.css' => 'c0737fe7', 'rsrc/css/core/remarkup.css' => '0923dbd6', 'rsrc/css/core/syntax.css' => '3c18c1cb', 'rsrc/css/core/z-index.css' => '0d89d53c', @@ -684,7 +684,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' => '2d9db584', 'phabricator-drag-and-drop-file-upload' => 'ae6abfba', 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/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/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -414,6 +414,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); @@ -46,14 +47,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'; @@ -71,6 +82,7 @@ $search_button, $this->renderPhabricatorLogo(), $alerts, + $aural, $application_menu, $search_menu, $menus, @@ -231,12 +243,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() { @@ -251,6 +271,8 @@ 'alert-notifications', ); + $aural = array(); + $message_tag = ''; $message_notification_dropdown = ''; $conpherence = 'PhabricatorApplicationConpherence'; @@ -265,6 +287,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"; } @@ -328,6 +364,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"; } @@ -392,8 +441,12 @@ } return array( - hsprintf('%s%s', $bubble_tag, $message_tag), - $dropdowns + array( + $bubble_tag, + $message_tag, + ), + $dropdowns, + $aural, ); } 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; +}