From 291eee30da42456d25694d895d51610e59091740 Mon Sep 17 00:00:00 2001 From: Albina <51043550+albinazs@users.noreply.github.com> Date: Wed, 26 Jun 2024 13:18:59 +0300 Subject: [PATCH] Add help text for a11y errors --- CHANGELOG.txt | 1 + client/scss/components/_a11y-result.scss | 11 +++- client/scss/components/_status-tag.scss | 10 ++-- client/src/includes/a11y-result.ts | 12 ++++- client/src/tokens/colorThemes.js | 24 +++++++++ client/src/tokens/colorVariables.test.js | 10 +++- client/src/tokens/colors.js | 11 +++- docs/_static/wagtail_colors_tables.txt | 4 +- docs/releases/6.2.md | 1 + .../shared/side_panels/checks.html | 1 + .../userbar/item_accessibility.html | 1 + wagtail/admin/tests/test_userbar.py | 28 +++++++--- wagtail/admin/userbar.py | 52 ++++++++++++------- 13 files changed, 129 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index ddbc617cba7b..537b80427999 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -14,6 +14,7 @@ Changelog * Add `copy_for_translation_done` signal when a page is copied for translation (Arnar Tumi Þorsteinsson) * Remove reduced opacity for draft page title in listings (Inju Michorius) * Adopt more compact representation for StreamField definitions in migrations (Matt Westcott) + * Implement a new design for locale labels in listings (Albina Starykova) * Fix: Make `WAGTAILIMAGES_CHOOSER_PAGE_SIZE` setting functional again (Rohit Sharma) * Fix: Enable `richtext` template tag to convert lazy translation values (Benjamin Bach) * Fix: Ensure permission labels on group permissions page are translated where available (Matt Westcott) diff --git a/client/scss/components/_a11y-result.scss b/client/scss/components/_a11y-result.scss index caf97c80b6a7..0c3ae4dd8b21 100644 --- a/client/scss/components/_a11y-result.scss +++ b/client/scss/components/_a11y-result.scss @@ -4,7 +4,7 @@ } .w-a11y-result__header { - margin: 0; + margin: 0 0 theme('spacing.[0.5]'); width: 100%; display: flex; justify-content: space-between; @@ -21,6 +21,15 @@ font-weight: theme('fontWeight.semibold'); } +.w-a11y-result__help { + color: theme('colors.text-placeholder'); + font-size: theme('fontSize.14'); + + .w-dialog--userbar & { + font-size: theme('fontSize.16'); + } +} + .w-a11y-result__container { display: flex; flex-wrap: wrap; diff --git a/client/scss/components/_status-tag.scss b/client/scss/components/_status-tag.scss index 8d165683b863..4c6b613543aa 100644 --- a/client/scss/components/_status-tag.scss +++ b/client/scss/components/_status-tag.scss @@ -29,10 +29,12 @@ } &--label { - color: theme('colors.text-context'); - background: theme('colors.border-button-small-outline-default'); - border: theme('colors.border-button-small-outline-default'); - font-weight: 500; + padding: theme('spacing.[0.5]') theme('spacing.2'); + color: theme('colors.text-status-label'); + font-weight: theme('fontWeight.medium'); + background: theme('colors.surface-status-label'); + border: 1px solid theme('colors.text-status-label'); + border-radius: theme('borderRadius.xl'); } } diff --git a/client/src/includes/a11y-result.ts b/client/src/includes/a11y-result.ts index 3b208f450d46..7ba40cd67a31 100644 --- a/client/src/includes/a11y-result.ts +++ b/client/src/includes/a11y-result.ts @@ -39,10 +39,14 @@ export const sortAxeViolations = (violations: Result[]) => * Wagtail's Axe configuration object. This should reflect what's returned by * `wagtail.admin.userbar.AccessibilityItem.get_axe_configuration()`. */ +interface ErrorMessage { + error_name: string; + help_text: string; +} interface WagtailAxeConfiguration { context: ElementContext; options: RunOptions; - messages: Record; + messages: Record; } /** @@ -100,7 +104,11 @@ export const renderA11yResults = ( // Display custom error messages supplied by Wagtail if available, // fallback to default error message from Axe a11yErrorName.textContent = - config.messages[violation.id] || violation.help; + config.messages[violation.id].error_name || violation.help; + const a11yErrorHelp = currentA11yRow.querySelector( + '[data-a11y-result-help]', + ) as HTMLDivElement; + a11yErrorHelp.textContent = config.messages[violation.id].help_text || ''; const a11yErrorCount = currentA11yRow.querySelector( '[data-a11y-result-count]', ) as HTMLSpanElement; diff --git a/client/src/tokens/colorThemes.js b/client/src/tokens/colorThemes.js index 68a0738e0c50..fbae8dd2493d 100644 --- a/client/src/tokens/colorThemes.js +++ b/client/src/tokens/colorThemes.js @@ -100,6 +100,12 @@ const light = [ textUtility: 'w-text-surface-button-critical-hover', cssVariable: '--w-color-surface-button-critical-hover', }, + 'surface-status-label': { + value: 'var(--w-color-info-50)', + bgUtility: 'w-bg-surface-status-label', + textUtility: 'w-text-surface-status-label', + cssVariable: '--w-color-surface-status-label', + }, }, }, { @@ -189,6 +195,12 @@ const light = [ textUtility: 'w-text-text-button-critical-outline-hover', cssVariable: '--w-color-text-button-critical-outline-hover', }, + 'text-status-label': { + value: 'var(--w-color-info-100)', + bgUtility: 'w-bg-text-status-label', + textUtility: 'w-text-text-status-label', + cssVariable: '--w-color-text-status-label', + }, }, }, { @@ -358,6 +370,12 @@ const dark = [ textUtility: 'w-text-surface-button-critical-hover', cssVariable: '--w-color-surface-button-critical-hover', }, + 'surface-status-label': { + value: 'var(--w-color-grey-600)', + bgUtility: 'w-bg-surface-status-label', + textUtility: 'w-text-surface-status-label', + cssVariable: '--w-color-surface-status-label', + }, }, }, { @@ -447,6 +465,12 @@ const dark = [ textUtility: 'w-text-text-button-critical-outline-hover', cssVariable: '--w-color-text-button-critical-outline-hover', }, + 'text-status-label': { + value: 'var(--w-color-info-75)', + bgUtility: 'w-bg-text-status-label', + textUtility: 'w-text-text-status-label', + cssVariable: '--w-color-text-status-label', + }, }, }, { diff --git a/client/src/tokens/colorVariables.test.js b/client/src/tokens/colorVariables.test.js index 5d1b251828de..1785ed457a2a 100644 --- a/client/src/tokens/colorVariables.test.js +++ b/client/src/tokens/colorVariables.test.js @@ -80,7 +80,7 @@ describe('generateColorVariables', () => { "--w-color-grey-800-lightness": "11.4%", "--w-color-grey-800-saturation": "0%", "--w-color-info-100": "hsl(var(--w-color-info-100-hue) var(--w-color-info-100-saturation) var(--w-color-info-100-lightness))", - "--w-color-info-100-hue": "calc(var(--w-color-info-125-hue) - 1)", + "--w-color-info-100-hue": "calc(var(--w-color-info-125-hue) - 0.1)", "--w-color-info-100-lightness": "calc(var(--w-color-info-125-lightness) + 6.5%)", "--w-color-info-100-saturation": "calc(var(--w-color-info-125-saturation) + 0.7%)", "--w-color-info-125": "hsl(var(--w-color-info-125-hue) var(--w-color-info-125-saturation) var(--w-color-info-125-lightness))", @@ -91,6 +91,10 @@ describe('generateColorVariables', () => { "--w-color-info-50-hue": "calc(var(--w-color-info-125-hue) + 2.2)", "--w-color-info-50-lightness": "calc(var(--w-color-info-125-lightness) + 65.9%)", "--w-color-info-50-saturation": "calc(var(--w-color-info-125-saturation) + 15.1%)", + "--w-color-info-75": "hsl(var(--w-color-info-75-hue) var(--w-color-info-75-saturation) var(--w-color-info-75-lightness))", + "--w-color-info-75-hue": "calc(var(--w-color-info-125-hue) + 0.4)", + "--w-color-info-75-lightness": "calc(var(--w-color-info-125-lightness) + 36.3%)", + "--w-color-info-75-saturation": "calc(var(--w-color-info-125-saturation) - 27.4%)", "--w-color-positive-100": "hsl(var(--w-color-positive-100-hue) var(--w-color-positive-100-saturation) var(--w-color-positive-100-lightness))", "--w-color-positive-100-hue": "162.1", "--w-color-positive-100-lightness": "31.6%", @@ -213,6 +217,7 @@ describe('generateThemeColorVariables', () => { "--w-color-surface-menu-item-active": "var(--w-color-primary-200)", "--w-color-surface-menus": "var(--w-color-primary)", "--w-color-surface-page": "var(--w-color-white)", + "--w-color-surface-status-label": "var(--w-color-info-50)", "--w-color-surface-tooltip": "var(--w-color-primary-200)", "--w-color-text-button": "var(--w-color-white)", "--w-color-text-button-critical-outline-hover": "var(--w-color-critical-200)", @@ -228,6 +233,7 @@ describe('generateThemeColorVariables', () => { "--w-color-text-link-hover": "var(--w-color-secondary-400)", "--w-color-text-meta": "var(--w-color-grey-400)", "--w-color-text-placeholder": "var(--w-color-grey-400)", + "--w-color-text-status-label": "var(--w-color-info-100)", } `); }); @@ -260,6 +266,7 @@ describe('generateThemeColorVariables', () => { "--w-color-surface-menu-item-active": "var(--w-color-grey-700)", "--w-color-surface-menus": "var(--w-color-grey-800)", "--w-color-surface-page": "var(--w-color-grey-600)", + "--w-color-surface-status-label": "var(--w-color-grey-600)", "--w-color-surface-tooltip": "var(--w-color-grey-500)", "--w-color-text-button": "var(--w-color-white)", "--w-color-text-button-critical-outline-hover": "var(--w-color-critical-50)", @@ -275,6 +282,7 @@ describe('generateThemeColorVariables', () => { "--w-color-text-link-hover": "var(--w-color-secondary-75)", "--w-color-text-meta": "var(--w-color-grey-150)", "--w-color-text-placeholder": "var(--w-color-grey-200)", + "--w-color-text-status-label": "var(--w-color-info-75)", } `); }); diff --git a/client/src/tokens/colors.js b/client/src/tokens/colors.js index 0cede50b82ea..3fe6911b552d 100644 --- a/client/src/tokens/colors.js +++ b/client/src/tokens/colors.js @@ -212,13 +212,22 @@ const staticColors = { }, 100: { hex: '#1D7792', - hsl: 'hsl(193 66.9% 34.3%)', + hsl: 'hsl(193.9 66.9% 34.3%)', bgUtility: 'w-bg-info-100', textUtility: 'w-text-info-100', cssVariable: '--w-color-info-100', usage: 'Background and icons for information messages', contrastText: 'white', }, + 75: { + hex: '#80B6C7', + hsl: 'hsl(194.4, 38.8%, 64.1%)', + bgUtility: 'w-bg-info-75', + textUtility: 'w-text-info-75', + cssVariable: '--w-color-info-75', + usage: 'Info text in the dark theme', + contrastText: 'primary', + }, 50: { hex: '#E2F5FC', hsl: 'hsl(196.2 81.3% 93.7%)', diff --git a/docs/_static/wagtail_colors_tables.txt b/docs/_static/wagtail_colors_tables.txt index 327557f08793..fb3014a1acec 100644 --- a/docs/_static/wagtail_colors_tables.txt +++ b/docs/_static/wagtail_colors_tables.txt @@ -1,4 +1,4 @@ -

Make sure to test any customisations against our Contrast Grid. Try out your own customisations with this interactive style editor:

Static colours

VariableUsage
--w-color-blackShadows only
--w-color-grey-800Backgrounds for panels in dark theme
--w-color-grey-700Backgrounds for panels in dark theme
--w-color-grey-600Body copy, user content
--w-color-grey-500Panels, dividers in dark mode
--w-color-grey-400Help text, placeholders, meta text, neutral state indicators
--w-color-grey-200Dividers, button borders
--w-color-grey-150Field borders
--w-color-grey-100Dividers, panel borders
--w-color-grey-50Background for panels, row highlights
--w-color-whitePage backgrounds, Panels, Button text
--w-color-primaryWagtail branding, Panels, Headings, Buttons, Labels
--w-color-primary-200Accent for elements used in conjunction with primary colour in sidebar
--w-color-secondaryPrimary buttons, action links
--w-color-secondary-600Hover states for two-tone buttons
--w-color-secondary-400Two-tone buttons, hover states
--w-color-secondary-100UI element highlights over dark backgrounds
--w-color-secondary-75UI element highlights over dark text
--w-color-secondary-50Button backgrounds, highlighted fields background
--w-color-info-125Hover background only, for information messages
--w-color-info-100Background and icons for information messages
--w-color-info-50Background only, for information messages
--w-color-positive-100Positive states
--w-color-positive-50Background only, for positive states
--w-color-warning-100Background and icons for potentially dangerous states
--w-color-warning-50Background only, for potentially dangerous states
--w-color-critical-200Dangerous actions or states (over light background), errors
--w-color-critical-100Dangerous actions or states (over dark background)
--w-color-critical-50Background only, for dangerous states

Light & dark theme colours

LightDarkVariable
Surfaces - General
--w-color-surface-page
--w-color-surface-field
--w-color-surface-field-inactive
--w-color-surface-header
--w-color-surface-menus
--w-color-surface-menu-item-active
--w-color-surface-tooltip
--w-color-surface-button-default
--w-color-surface-button-hover
--w-color-surface-button-inactive
--w-color-surface-button-outline-hover
--w-color-surface-button-critical-hover
Text
--w-color-text-button
--w-color-text-label-menus-default
--w-color-text-label-menus-active
--w-color-text-label
--w-color-text-context
--w-color-text-meta
--w-color-text-placeholder
--w-color-text-link-default
--w-color-text-link-hover
--w-color-text-button-outline-default
--w-color-text-button-outline-hover
--w-color-text-highlight
--w-color-text-error
--w-color-text-button-critical-outline-hover
Icons
--w-color-icon-primary
--w-color-icon-primary-hover
--w-color-icon-secondary
--w-color-icon-secondary-hover
Borders
--w-color-border-furniture
--w-color-border-button-small-outline-default
--w-color-border-field-default
--w-color-border-field-inactive
--w-color-border-field-hover
--w-color-border-button-outline-default
--w-color-border-button-outline-hover
Misc
--w-color-focus
--w-color-box-shadow-md
+}

Static colours

VariableUsage
--w-color-blackShadows only
--w-color-grey-800Backgrounds for panels in dark theme
--w-color-grey-700Backgrounds for panels in dark theme
--w-color-grey-600Body copy, user content
--w-color-grey-500Panels, dividers in dark mode
--w-color-grey-400Help text, placeholders, meta text, neutral state indicators
--w-color-grey-200Dividers, button borders
--w-color-grey-150Field borders
--w-color-grey-100Dividers, panel borders
--w-color-grey-50Background for panels, row highlights
--w-color-whitePage backgrounds, Panels, Button text
--w-color-primaryWagtail branding, Panels, Headings, Buttons, Labels
--w-color-primary-200Accent for elements used in conjunction with primary colour in sidebar
--w-color-secondaryPrimary buttons, action links
--w-color-secondary-600Hover states for two-tone buttons
--w-color-secondary-400Two-tone buttons, hover states
--w-color-secondary-100UI element highlights over dark backgrounds
--w-color-secondary-75UI element highlights over dark text
--w-color-secondary-50Button backgrounds, highlighted fields background
--w-color-info-125Hover background only, for information messages
--w-color-info-100Background and icons for information messages
--w-color-info-75Info text in the dark theme
--w-color-info-50Background only, for information messages
--w-color-positive-100Positive states
--w-color-positive-50Background only, for positive states
--w-color-warning-100Background and icons for potentially dangerous states
--w-color-warning-50Background only, for potentially dangerous states
--w-color-critical-200Dangerous actions or states (over light background), errors
--w-color-critical-100Dangerous actions or states (over dark background)
--w-color-critical-50Background only, for dangerous states

Light & dark theme colours

LightDarkVariable
Surfaces - General
--w-color-surface-page
--w-color-surface-field
--w-color-surface-field-inactive
--w-color-surface-header
--w-color-surface-menus
--w-color-surface-menu-item-active
--w-color-surface-tooltip
--w-color-surface-button-default
--w-color-surface-button-hover
--w-color-surface-button-inactive
--w-color-surface-button-outline-hover
--w-color-surface-button-critical-hover
--w-color-surface-status-label
Text
--w-color-text-button
--w-color-text-label-menus-default
--w-color-text-label-menus-active
--w-color-text-label
--w-color-text-context
--w-color-text-meta
--w-color-text-placeholder
--w-color-text-link-default
--w-color-text-link-hover
--w-color-text-button-outline-default
--w-color-text-button-outline-hover
--w-color-text-highlight
--w-color-text-error
--w-color-text-button-critical-outline-hover
--w-color-text-status-label
Icons
--w-color-icon-primary
--w-color-icon-primary-hover
--w-color-icon-secondary
--w-color-icon-secondary-hover
Borders
--w-color-border-furniture
--w-color-border-button-small-outline-default
--w-color-border-field-default
--w-color-border-field-inactive
--w-color-border-field-hover
--w-color-border-button-outline-default
--w-color-border-button-outline-hover
Misc
--w-color-focus
--w-color-box-shadow-md
diff --git a/docs/releases/6.2.md b/docs/releases/6.2.md index d523264d04cf..7e9bef204dcf 100644 --- a/docs/releases/6.2.md +++ b/docs/releases/6.2.md @@ -24,6 +24,7 @@ depth: 1 * Add `copy_for_translation_done` signal when a page is copied for translation (Arnar Tumi Þorsteinsson) * Remove reduced opacity for draft page title in listings (Inju Michorius) * Adopt more compact representation for StreamField definitions in migrations (Matt Westcott) + * Implement a new design for locale labels in listings (Albina Starykova) ### Bug fixes diff --git a/wagtail/admin/templates/wagtailadmin/shared/side_panels/checks.html b/wagtail/admin/templates/wagtailadmin/shared/side_panels/checks.html index 85f17b73b92e..16f83eb06978 100644 --- a/wagtail/admin/templates/wagtailadmin/shared/side_panels/checks.html +++ b/wagtail/admin/templates/wagtailadmin/shared/side_panels/checks.html @@ -6,6 +6,7 @@

{% trans 'Issues found' %}

+
diff --git a/wagtail/admin/templates/wagtailadmin/userbar/item_accessibility.html b/wagtail/admin/templates/wagtailadmin/userbar/item_accessibility.html index 75e4c0d91a78..4918a178773a 100644 --- a/wagtail/admin/templates/wagtailadmin/userbar/item_accessibility.html +++ b/wagtail/admin/templates/wagtailadmin/userbar/item_accessibility.html @@ -21,6 +21,7 @@

{% trans 'Issues found' %}

+
diff --git a/wagtail/admin/tests/test_userbar.py b/wagtail/admin/tests/test_userbar.py index 98528bddef05..b56670681c68 100644 --- a/wagtail/admin/tests/test_userbar.py +++ b/wagtail/admin/tests/test_userbar.py @@ -208,22 +208,32 @@ def test_messages(self): config = self.get_config() self.assertIsInstance(config.get("messages"), dict) self.assertEqual( - config["messages"]["empty-heading"], - "Empty heading found. Use meaningful text for screen reader users.", + config["messages"]["empty-heading"]["error_name"], + "Empty heading found", + ) + self.assertEqual( + config["messages"]["empty-heading"]["help_text"], + "Use meaningful text for screen reader users", ) def test_custom_message(self): class CustomMessageAccessibilityItem(AccessibilityItem): # Override via class attribute axe_messages = { - "empty-heading": "Headings should not be empty!", + "empty-heading": { + "error_name": "Headings should not be empty!", + "help_text": "Use meaningful text!", + }, } # Override via method def get_axe_messages(self, request): return { **super().get_axe_messages(request), - "color-contrast-enhanced": "Increase colour contrast!", + "color-contrast-enhanced": { + "error_name": "Insufficient colour contrast!", + "help_text": "Ensure contrast ratio of at least 4.5:1", + }, } with hooks.register_temporarily( @@ -234,8 +244,14 @@ def get_axe_messages(self, request): self.assertEqual( config["messages"], { - "empty-heading": "Headings should not be empty!", - "color-contrast-enhanced": "Increase colour contrast!", + "empty-heading": { + "error_name": "Headings should not be empty!", + "help_text": "Use meaningful text!", + }, + "color-contrast-enhanced": { + "error_name": "Insufficient colour contrast!", + "help_text": "Ensure contrast ratio of at least 4.5:1", + }, }, ) diff --git a/wagtail/admin/userbar.py b/wagtail/admin/userbar.py index e1f64ea24c8d..af8db8f2c184 100644 --- a/wagtail/admin/userbar.py +++ b/wagtail/admin/userbar.py @@ -67,26 +67,38 @@ class AccessibilityItem(BaseItem): #: to use as the error messages. If an enabled rule does not exist in this #: dictionary, Axe's error message for the rule will be used as fallback. axe_messages = { - "button-name": _( - "Button text is empty. Use meaningful text for screen reader users." - ), - "empty-heading": _( - "Empty heading found. Use meaningful text for screen reader users." - ), - "empty-table-header": _( - "Table header text is empty. Use meaningful text for screen reader users." - ), - "frame-title": _( - "Empty frame title found. Use a meaningful title for screen reader users." - ), - "heading-order": _("Incorrect heading hierarchy. Avoid skipping levels."), - "input-button-name": _( - "Input button text is empty. Use meaningful text for screen reader users." - ), - "link-name": _( - "Link text is empty. Use meaningful text for screen reader users." - ), - "p-as-heading": _("Misusing paragraphs as headings. Use proper heading tags."), + "button-name": { + "error_name": _("Button text is empty"), + "help_text": _("Use meaningful text for screen reader users"), + }, + "empty-heading": { + "error_name": _("Empty heading found"), + "help_text": _("Use meaningful text for screen reader users"), + }, + "empty-table-header": { + "error_name": _("Table header text is empty"), + "help_text": _("Use meaningful text for screen reader users"), + }, + "frame-title": { + "error_name": _("Empty frame title found"), + "help_text": _("Use a meaningful title for screen reader users"), + }, + "heading-order": { + "error_name": _("Incorrect heading hierarchy"), + "help_text": _("Avoid skipping levels"), + }, + "input-button-name": { + "error_name": _("Input button text is empty"), + "help_text": _("Use meaningful text for screen reader users"), + }, + "link-name": { + "error_name": _("Link text is empty"), + "help_text": _("Use meaningful text for screen reader users"), + }, + "p-as-heading": { + "error_name": _("Misusing paragraphs as headings"), + "help_text": _("Use proper heading tags"), + }, } def get_axe_include(self, request):