diff --git a/changelogs/DP-44594.yml b/changelogs/DP-44594.yml new file mode 100644 index 0000000000..0c81a17999 --- /dev/null +++ b/changelogs/DP-44594.yml @@ -0,0 +1,3 @@ +Fixed: + - description: SPIKE assess work to reskin Google Translate widget in accessible way. + issue: DP-44594 diff --git a/composer.json b/composer.json index ff645c5592..e8f4744eec 100644 --- a/composer.json +++ b/composer.json @@ -278,7 +278,7 @@ "geocoder-php/open-cage-provider": "^4.6", "google/cloud-bigquery": "^1.25", "league/commonmark": "^2.7", - "massgov/mayflower-artifacts": "dev-develop", + "massgov/mayflower-artifacts": "dev-feature/DP-44594-SPIKE-assess-work-to-reskin-Google-Translate-widget-in-accessible-way", "monolog/monolog": "^3", "npm-asset/ace-builds": "~1.0", "npm-asset/select2": "^4.1.0-rc.0", diff --git a/composer.lock b/composer.lock index d115e8e143..92e46b1bb0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7b9eaac987df7c8956b05827d445e02c", + "content-hash": "e7887f1f0f2b4701c636b4d4e4b5ca73", "packages": [ { "name": "akamai-open/edgegrid-auth", @@ -13614,16 +13614,16 @@ }, { "name": "massgov/mayflower-artifacts", - "version": "dev-develop", + "version": "dev-feature/DP-44594-SPIKE-assess-work-to-reskin-Google-Translate-widget-in-accessible-way", "source": { "type": "git", "url": "https://github.com/massgov/mayflower-artifacts", - "reference": "22291658613238853716a31ccd0560b388ee02cf" + "reference": "c631d55c89d0815c28f2fd8c517f9cd6d40a67ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/massgov/mayflower-artifacts/zipball/22291658613238853716a31ccd0560b388ee02cf", - "reference": "22291658613238853716a31ccd0560b388ee02cf", + "url": "https://api.github.com/repos/massgov/mayflower-artifacts/zipball/c631d55c89d0815c28f2fd8c517f9cd6d40a67ab", + "reference": "c631d55c89d0815c28f2fd8c517f9cd6d40a67ab", "shasum": "" }, "require": { @@ -13641,7 +13641,7 @@ } ], "description": "This repository is the product of the built artifact from massgov/mayflower", - "time": "2026-01-26T06:33:52+00:00" + "time": "2026-01-29T03:42:35+00:00" }, { "name": "masterminds/html5", @@ -26622,5 +26622,5 @@ "platform-overrides": { "php": "8.3" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/docroot/themes/custom/mass_theme/mass_theme.libraries.yml b/docroot/themes/custom/mass_theme/mass_theme.libraries.yml index 48af911ed9..a7d928d439 100644 --- a/docroot/themes/custom/mass_theme/mass_theme.libraries.yml +++ b/docroot/themes/custom/mass_theme/mass_theme.libraries.yml @@ -24,6 +24,7 @@ global-styling: overrides/css/sections.css: {} overrides/css/news-page.css: {} overrides/css/language-bar.css: {} + overrides/css/google-translate.css: {} overrides/css/view-location-listing.css: {} overrides/css/rich-text.css: {} overrides/css/info-details.css: {} @@ -63,6 +64,7 @@ global-styling-lp: overrides/css/sections.css: {} overrides/css/news-page.css: {} overrides/css/language-bar.css: {} + overrides/css/google-translate.css: {} overrides/css/view-location-listing.css: {} overrides/css/rich-text.css: {} overrides/css/info-details.css: {} diff --git a/docroot/themes/custom/mass_theme/mass_theme.theme b/docroot/themes/custom/mass_theme/mass_theme.theme index 53f6de7154..ee69980009 100644 --- a/docroot/themes/custom/mass_theme/mass_theme.theme +++ b/docroot/themes/custom/mass_theme/mass_theme.theme @@ -8,6 +8,7 @@ use Drupal\block\Entity\Block; use Drupal\Component\Utility\Html; use Drupal\Component\Utility\UrlHelper; +use Drupal\Component\Utility\Xss as XssAlias; use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Entity\ContentEntityInterface; @@ -16,6 +17,7 @@ use Drupal\Core\Link; use Drupal\Core\Menu\MenuTreeParameters; use Drupal\Core\Site\Settings; use Drupal\Core\StringTranslation\ByteSizeMarkup; +use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Core\Url; use Drupal\file\Entity\File; use Drupal\image\Entity\ImageStyle; @@ -4549,6 +4551,12 @@ function mass_theme_preprocess_html(&$variables) { $variables['languages'] = implode(',', array_filter(theme_get_setting('languages', 'mass_theme'))); } + if ($select_language_translations = theme_get_setting('select_language_translations', 'mass_theme')) { + $variables['select_language_translations'] = array_map(function($language_pair) { + return explode('|', $language_pair); + }, explode(PHP_EOL, $select_language_translations)); + } + // Add glossary functionality if the page references glossaries. $params = \Drupal::routeMatch()->getParameters(); if ($params->has('node')) { @@ -6731,6 +6739,25 @@ function mass_theme_form_system_theme_settings_alter(&$form, $form_state) { '#title' => t('Choose google translate languages?'), '#default_value' => theme_get_setting('languages', 'mass_theme'), ]; + + $codes = array_map(function($language, $code) { + return "
  • $language:
    $code
  • "; + }, $languages, array_keys($languages)); + $codes_html = ''; + + $form['mass_theme_settings']['select_language_translations'] = [ + '#type' => 'textarea', + '#title' => t('"Select language" in native languages'), + '#default_value' => theme_get_setting('select_language_translations', 'mass_theme'), + '#description' => t('Insert pairs in format "code|translation_in_native_language". List of language codes:'), + ]; + $form['mass_theme_settings']['info'] = [ + '#type' => 'details', + '#title' => t('Codes'), + '#open' => FALSE, + ]; + + $form['mass_theme_settings']['info']['#markup'] = XssAlias::filter($codes_html, ['ul', 'li', 'pre', 'code']); } /** diff --git a/docroot/themes/custom/mass_theme/overrides/css/google-translate.css b/docroot/themes/custom/mass_theme/overrides/css/google-translate.css new file mode 100644 index 0000000000..0a7acceea1 --- /dev/null +++ b/docroot/themes/custom/mass_theme/overrides/css/google-translate.css @@ -0,0 +1,331 @@ +/* Accessible Google Translate Widget Styles - Compact for Utility Nav */ + +/* Fix Google Translate spacing issue with notranslate elements */ +.notranslate { + padding-left: 0; + padding-right: 0; +} + +html.translated-ltr .notranslate, +html.translated-rtl .notranslate { + padding-left: 0.25em; + padding-right: 0.25em; +} + +/* Hide default Google Translate widget */ +.ads-goog-te-wrapper { + position: absolute; + left: -9999px; + width: 1px; + height: 1px; + overflow: hidden; +} + +/* Hide Google Translate top banner */ +.goog-te-banner-frame.skiptranslate { + display: none !important; +} + +.skiptranslate iframe { + display: none !important; +} + +body > .skiptranslate { + display: none !important; +} + +.goog-te-menu-frame { + display: none !important; +} + +body { + top: 0 !important; +} + +/* Compact styling for utility nav integration */ +.ma__utility-nav__translate .ads-translate-container { + display: inline-block; + margin: 0; +} + +/* Hide fieldset border and make it inline */ +.ma__utility-nav__translate .ads-translate-fieldset { + border: none; + padding: 0; + margin: 0; + display: inline-block; +} + +/* Hide the legend visually but keep for screen readers */ +.ma__utility-nav__translate .ads-translate-legend { + position: absolute; + left: -9999px; + width: 1px; + height: 1px; + overflow: hidden; +} + +/* Make the label visible and inline */ +.ma__utility-nav__translate .ads-translate-label { + display: inline-block !important; + color: #fff !important; + font-size: 14px !important; + font-weight: 400 !important; + margin: 0 8px 0 0 !important; + vertical-align: middle !important; + position: static !important; + width: auto !important; + height: auto !important; + overflow: visible !important; + left: auto !important; +} + +/* Make select wrapper inline */ +.ma__utility-nav__translate .ads-translate-select-wrapper { + display: inline-block; + position: relative; + margin: 0; +} + +/* Style the select to fit in utility nav */ +.ma__utility-nav__translate .ads-translate-select { + padding: 4px 8px; + font-size: 14px; + border: 1px solid rgba(255, 255, 255, 0.3); + border-radius: 2px; + background-color: transparent; + color: #fff; + cursor: pointer; + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + min-width: 120px; +} + +.ma__utility-nav__translate .ads-translate-select:hover { + background-color: rgba(255, 255, 255, 0.1); + border-color: rgba(255, 255, 255, 0.5); +} + +.ma__utility-nav__translate .ads-translate-select:focus { + outline: 2px solid #fff; + outline-offset: 2px; +} + +/* Hide the dropdown icon from example */ +.ma__utility-nav__translate .ads-translate-icon { + display: none; +} + +/* Hide help text visually but keep for screen readers */ +.ma__utility-nav__translate .ads-translate-help, +.ads-sr-only { + position: absolute; + left: -9999px; + width: 1px; + height: 1px; + overflow: hidden; +} + +/* Style Apply and English (Reset) buttons to fit inline in utility nav */ +.ma__utility-nav__translate .ads-translate-button { + display: inline-block; + padding: 4px 12px; + font-size: 14px; + font-weight: 500; + color: #fff; + background-color: rgba(255, 255, 255, 0.2); + border: 1px solid rgba(255, 255, 255, 0.3); + border-radius: 2px; + cursor: pointer; + margin-left: 8px; + margin-right: 0; + vertical-align: middle; + transition: background-color 0.2s ease, border-color 0.2s ease; +} + +.ma__utility-nav__translate .ads-translate-button:hover { + background-color: rgba(255, 255, 255, 0.3); + border-color: rgba(255, 255, 255, 0.5); +} + +.ma__utility-nav__translate .ads-translate-button:focus { + outline: 2px solid #fff; + outline-offset: 2px; +} + +.ma__utility-nav__translate .ads-translate-button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* English (Reset) button - slightly different styling */ +.ma__utility-nav__translate .ads-translate-reset-button { + background-color: rgba(255, 255, 255, 0.1); + border-color: rgba(255, 255, 255, 0.25); +} + +.ma__utility-nav__translate .ads-translate-reset-button:hover { + background-color: rgba(255, 255, 255, 0.25); + border-color: rgba(255, 255, 255, 0.4); +} + +/* Screen reader status messages */ +.ads-status-message { + position: absolute; + left: -9999px; + width: 1px; + height: 1px; + overflow: hidden; +} + + + +/* Responsive styles for mobile/tablet */ +@media (max-width: 768px) { + + /* Hamburger menu specific styles */ + .ma__header__hamburger__utility-nav .ma__utility-nav__translate .ads-translate-container { + display: block; + width: 100%; + } + + .ma__header__hamburger__utility-nav .ma__utility-nav__translate .ads-translate-fieldset { + display: flex; + flex-direction: column; + align-items: flex-start; + width: 100%; + padding: 16px 20px; + border: none; + background-color: transparent; + } + + .ma__header__hamburger__utility-nav .ma__utility-nav__translate .ads-translate-label { + display: block !important; + color: #141414 !important; + font-size: 16px !important; + font-weight: 500 !important; + margin: 0 0 12px 0 !important; + position: static !important; + width: 100% !important; + height: auto !important; + overflow: visible !important; + left: auto !important; + } + + .ma__header__hamburger__utility-nav .ma__utility-nav__translate .ads-translate-select-wrapper { + display: block; + width: 100%; + margin-bottom: 12px; + } + + .ma__header__hamburger__utility-nav .ma__utility-nav__translate .ads-translate-select { + width: 100%; + padding: 8px 12px; + font-size: 16px; + border: 2px solid #707070; + border-radius: 0; + background-color: #fff; + color: #141414; + cursor: pointer; + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + min-width: auto; + } + + .ma__header__hamburger__utility-nav .ma__utility-nav__translate .ads-translate-select:hover { + background-color: #f2f2f2; + border-color: #535353; + } + + .ma__header__hamburger__utility-nav .ma__utility-nav__translate .ads-translate-select:focus { + outline: 2px solid #3e94cf; + outline-offset: 2px; + border-color: #707070; + } + + .ma__header__hamburger__utility-nav .ma__utility-nav__translate .ads-translate-button { + display: block; + width: 100%; + padding: 10px 16px; + font-size: 16px; + font-weight: 500; + color: #fff; + background-color: #14558f; + border: none; + border-radius: 0; + cursor: pointer; + margin: 0 0 12px 0; + text-align: center; + transition: background-color 0.2s ease; + } + + .ma__header__hamburger__utility-nav .ma__utility-nav__translate .ads-translate-button:hover { + background-color: #104472; + } + + .ma__header__hamburger__utility-nav .ma__utility-nav__translate .ads-translate-button:focus { + outline: 2px solid #3e94cf; + outline-offset: 2px; + } + + .ma__header__hamburger__utility-nav .ma__utility-nav__translate .ads-translate-button:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + .ma__header__hamburger__utility-nav .ma__utility-nav__translate .ads-translate-reset-button { + background-color: #fff; + color: #141414; + border: 2px solid #707070; + margin-bottom: 0; + } + + .ma__header__hamburger__utility-nav .ma__utility-nav__translate .ads-translate-reset-button:hover { + background-color: #f2f2f2; + border-color: #535353; + } + + /* Stack elements vertically on mobile */ + .ma__utility-nav .ma__utility-nav__items .ma__utility-nav__item .ma__utility-nav__translate .ads-translate-fieldset { + display: flex !important; + flex-direction: column !important; + align-items: flex-start !important; + width: 100% !important; + padding: 12px !important; + border: none !important; + } + + .ma__utility-nav .ma__utility-nav__items .ma__utility-nav__item .ma__utility-nav__translate .ads-translate-label { + display: block !important; + margin-bottom: 8px !important; + margin-right: 0 !important; + font-size: 14px !important; + width: 100% !important; + } + + .ma__utility-nav .ma__utility-nav__items .ma__utility-nav__item .ma__utility-nav__translate .ads-translate-select-wrapper { + display: block !important; + width: 100% !important; + margin-bottom: 8px !important; + } + + .ma__utility-nav .ma__utility-nav__items .ma__utility-nav__item .ma__utility-nav__translate .ads-translate-select { + width: 100% !important; + min-width: auto !important; + } + + .ma__utility-nav .ma__utility-nav__items .ma__utility-nav__item .ma__utility-nav__translate .ads-translate-button { + display: block !important; + width: 100% !important; + margin-left: 0 !important; + margin-right: 0 !important; + margin-bottom: 8px !important; + text-align: center !important; + } + + .ma__utility-nav .ma__utility-nav__items .ma__utility-nav__item .ma__utility-nav__translate .ads-translate-reset-button { + margin-bottom: 0 !important; + } +} diff --git a/docroot/themes/custom/mass_theme/templates/layout/html.html.twig b/docroot/themes/custom/mass_theme/templates/layout/html.html.twig index 757ca996db..35212c83fa 100644 --- a/docroot/themes/custom/mass_theme/templates/layout/html.html.twig +++ b/docroot/themes/custom/mass_theme/templates/layout/html.html.twig @@ -135,19 +135,408 @@ {% endif %} - {# Google Translate JS #} + {# Google Translate Script #} + + + + {# Accessible Interface Script #}