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 = '' . implode('', $codes) . '
';
+
+ $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 #}