Improve the accessibility of focus outlines

Created on 13 February 2025, 5 months ago

Problem/Motivation

There are several problems in the context of focus outlines in Gin:

  • Many visible focus colors don’t meet the minimum color contrast of 3:1 in light and dark mode. See the following Google sheet https://docs.google.com/spreadsheets/d/1won35PxhRFexJYE8FmZ4DCNTo7xEAxC8... or focus_outlines.xlsx.zip for a table.
  • The focus outline is defined by box-shadow: 0 0 0 1px var(--gin-color-focus-border),0 0 0 4px var(--gin-color-focus) - in addition to that for some selectors the outline property is set to none (gin.css in line 994 and 3466). Problem is, focus outlines defined by the box-shadow property are not visible in Forced Colors mode (see https://stackoverflow.com/a/52616313 - looks like Claro uses the same approach @andrewmacpherson proposed in the linked post)
  • The one pixel wide focus border is barely visible and it looks more like a visual glitch than a stylistic device helping to visually separate the focus outline from the element it highlights. At the moment that focus border only helps to make the focus outline pass formally SC1.4.11 for most of the focus and accent colors in light mode, even though it is barely visible and not the selected focus or accent color but just semi-transparent black in light and dark mode instead.
  • There is the problem of simultaneous contrast, at the moment the focus outline is directly "touching" the elements it highlights, while Claro is employing an outline offset. An offset isn’t solving the problem of the simultaneous contrast, but it is at least easing it. To illustrate the problem, if you set for example the focus color to "gin focus color (Default)" and the accent color to “Gin blue” in light mode and you focus the blue Drupal logo in the navigation sidebar or the primary button you have a hard time recognizing that the elements are in focus, since the color of the focus outline and the element in focus are close.

Discussed and iterated on the issue with @mgifford, @the_g_bomb, @katannshaw, and @drupa11y

Steps to reproduce

  • Go to admin/appearance/settings/gin and test the different focus and accent color settings directly on the page. For testing the issue in Forced Colors mode make sure that before activating you’ve switched to dark mode.

Proposed resolution

The group had a consensus about the following steps to improve the current state:

  • All focus colors need to have a color contrast of at least 3:1, accent colors have to have to have a color contrast of at least 4.5:1 (see [#Primary and secondary buttons])
  • Drop the focus border and just use a single colored focus outline like Claro, that way the focus outline would even be compliant with SC2.4.13 as well.
  • Use the pattern with the transparent outline that is used in Claro to fix the invisible focus outlines in Forced Colors mode (see https://stackoverflow.com/a/52616313)
  • Add an offset to the focus outline, to ease the problem with simultaneous contrast.

Remaining tasks

User interface changes

API changes

Data model changes

🐛 Bug report
Status

Active

Version

4.0

Component

User interface

Created by

🇩🇪Germany rkoller Nürnberg, Germany

Live updates comments and jobs are added and updated live.
  • Accessibility

    It affects the ability of people with disabilities or special needs (such as blindness or color-blindness) to use Drupal.

Sign in to follow issues

Comments & Activities

  • Issue created by @rkoller
  • 🇩🇪Germany rkoller Nürnberg, Germany
  • 🇩🇪Germany rkoller Nürnberg, Germany
  • 🇨🇦Canada mgifford Ottawa, Ontario

    So I think this would help a lot:

    .page-wrapper *:focus,.ui-dialog *:focus,.dropbutton .dropbutton__item > *:focus,.dropbutton .dropbutton__item > *:focus:hover,.dropbutton__items > .dropbutton__item:first-of-type > *:focus,.dropbutton__items > .dropbutton__item:first-of-type ~ .dropbutton__item > *:focus,.dropbutton__items > .dropbutton__item:first-of-type ~ .dropbutton__item > *:focus:hover,.form-boolean:focus:active,.form-boolean:focus:hover,.cke.cke_chrome.cke_focus,.horizontal-tabs ul.horizontal-tabs-list li.horizontal-tab-button a:focus,.form-element:focus,.form-element:hover:focus,.form-element.error:hover:focus,.form-actions .action-link:focus,.paragraphs-tabs-wrapper .field-multiple-table .draggable.drag,.layout-paragraphs-field .layout-paragraphs-actions input.layout-paragraphs-edit:focus,.layout-paragraphs-field .layout-paragraphs-actions input.layout-paragraphs-remove:focus,.ck .ck.ck-button:active,.ck .ck.ck-button:focus,.ck .ck.ck-button:active:focus,.toolbar-box .toolbar-handle:focus,.field-option:has(.field-option-radio:checked),.field-option:focus-within,.subfield-option:has(.field-option-radio:checked),.subfield-option:focus-within,#edit-submit.form-submit:focus,.toolbar-dropdown__menu .toolbar-dropdown__link:focus {
        outline: none;
        box-shadow: 0 0 0 2px var(--color-background, #fff), 0 0 0 5px var(--gin-color-focus);
    }

    I don't know why it isn't there on hover as well. I do think this would assist mouse users.

    We need to do more to enhance the forced colors mode here, especially as the radio button doesn't look like a radio button, but rather three different buttons:

    @media (forced-colors: active) {
      *:focus {
        outline: 6px solid Highlight !important;
        outline-offset: 2px;
        box-shadow: none !important;
      }
    }

    I am also a fan of how the UK government does their focus styling. As an example of how that could be done.

    :root {
      --gin-color-focus-text: #0b0c0c;         /* GOV.UK black */
      --gin-color-focus-background: #ffdd00;   /* GOV.UK yellow */
    }
    
    /* -----------------------------------
       GDS-inspired global focus styling
    ----------------------------------- */
    
    *:focus-visible {
      outline: 3px solid rgba(0, 0, 0, 0); /* suppress default outline */
      color: var(--gin-color-focus-text);
      background-color: var(--gin-color-focus-background);
      box-shadow:
        0 -2px var(--gin-color-focus-background),
        0 4px var(--gin-color-focus-text);
      text-decoration: none;
      z-index: 10;
    }
    
    /* -----------------------------------
       Gin-specific Form Focus Styles
    ----------------------------------- */
    
    /* Light/Dark/Auto radio selection */
    [data-drupal-selector="edit-enable-darkmode"] .form-radios input:checked + .form-item__label {
      color: var(--gin-color-button-text);
      background-color: var(--gin-color-primary);
      box-shadow:
        0 -2px var(--gin-color-focus-background),
        0 4px var(--gin-color-focus-text);
    }
    
    /* Classic toolbar and layout density radios */
    [data-drupal-selector="edit-classic-toolbar"] .form-radios .form-item input:checked:focus,
    [data-drupal-selector="edit-layout-density"] .form-radios .form-item input:checked:focus {
      box-shadow:
        0 -2px var(--gin-color-focus-background),
        0 4px var(--gin-color-focus-text);
      background-color: var(--gin-color-focus-background);
      color: var(--gin-color-focus-text);
    }
    
    /* Checkboxes not inside known exception wrappers */
    :not(.form-checkboxes):not(td):not(.tabledrag-cell-content__item):not(.media-library-item__click-to-select-checkbox):not(.field-content):not(.ajax-new-content):not(.tablesaw-cell-content) 
    > .form-type--checkbox input:focus {
      box-shadow:
        0 -2px var(--gin-color-focus-background),
        0 4px var(--gin-color-focus-text);
      background-color: var(--gin-color-focus-background);
      color: var(--gin-color-focus-text);
    }
    
    /* Optional: input[type="radio"] and input[type="checkbox"] globally */
    input[type="radio"]:focus-visible,
    input[type="checkbox"]:focus-visible {
      box-shadow:
        0 -2px var(--gin-color-focus-background),
        0 4px var(--gin-color-focus-text);
      background-color: var(--gin-color-focus-background);
      color: var(--gin-color-focus-text);
    }
    
    /* -----------------------------------
       Forced Colors Mode (High Contrast)
    ----------------------------------- */
    @media (forced-colors: active) {
      *:focus-visible {
        outline: 2px solid Highlight !important;
        outline-offset: 2px;
        box-shadow: none !important;
        background-color: Highlight !important;
        color: HighlightText !important;
      }
    
      input[type="radio"]:checked + label,
      input[type="checkbox"]:checked + label {
        background-color: Highlight !important;
        color: HighlightText !important;
        font-weight: bold !important;
        border: 2px solid HighlightText !important;
        padding: 0.2em 0.5em !important;
      }
    }
  • 🇨🇦Canada mgifford Ottawa, Ontario
  • 🇨🇦Canada mgifford Ottawa, Ontario

    Some more observations on the colors in Gin.

    There are 11 Accent colors, 4 different Focus colors, plus Dark/Light modes. That's a lot of combinations. Probably too many combinations to be able to provide good guidance.

    Drupal Core should be providing a good example. We have as a community committed to striving to meet WCAG 2.2 AA standards. This is especially true of Core and Drupal CMS.

    Light Mode:

    Dark Mode (Looks good):

    I believe adding a space in the focus outline should make addressing this easier. We can state a white or dark inner circle before the focus outline color.

    The WCAG Success Criterion 1.4.1 (Use of Color) in question.

    The least 3:1 I think would just need to be the background.

    LIght Mode

    • Gin Focus color (Default) - #71B3FC / #FFFFFF - Failed (Could we not just use #5996EC)
    • Green - #78C9BE / #FFFFFF - Failed (Could we not just use #58A19D)
    • Claro Green - #85CBA8 / #FFFFFF - Failed (Could we not just use #739E8C)<
    • Orange - #F4B39E / #FFFFFF - Failed (Could we not just use #709F87)
    • Neutral - #A3A3A3 / #FFFFFF - Failed (Could we not just use #989494)
    • Same as Accent color - See above
    • Custom - N/A - we are restricting this to good defaults

    Dark Mode

    WebAim also has this interesting Link Contrast Checker.

  • 🇨🇦Canada mgifford Ottawa, Ontario
  • 🇨🇦Canada mgifford Ottawa, Ontario
Production build 0.71.5 2024