Ottawa, Ontario
Account created on 19 July 2005, almost 20 years ago
#

Recent comments

🇨🇦Canada mgifford Ottawa, Ontario

I'm just playing around with options here, but:

tr.views-form__bulk-operations-row.selected {
  font-weight: 600;
  font-size: 1.1rem;

  background: linear-gradient(to right,
    rgba(var(--gin-color-primary-rgb), 0.15) 0%,
    #fff 10%,
    #fff 90%,
    rgba(var(--gin-color-primary-rgb), 0.15) 100%
  );
                              
  /* 12px solid outline in the accent color */
  outline: 12px solid rgba(var(--gin-color-primary-light-rgb), 0.5);  
  outline-offset: -12px;
}

/* Underline and darken links inside selected row */
tr.views-form__bulk-operations-row.selected a {
  text-decoration: underline;
  color: #111;
}

/* Ensure no other styles override the selected state */
tr.views-form__bulk-operations-row a:hover {
  text-decoration: underline;
  color: #111;
}

Now I don't pretend to be a designer, but I think we can find an alternative which can highlight the sticky bar (and the hover for that matter) while also meeting WCAG and looking sharp.

🇨🇦Canada mgifford Ottawa, Ontario

Using accessibility Insights I was able to duplicate and provide a screenshot of this.

I start in the Title field to make it easier.

The first round through the table goes fine, but when you get to Updated, it gets screwed up. You can see in the numbers that it looks like it goes 1, 2, 3, 4, 10, 6, 7 , 8, 9 (although the last numbers are hard to see).

After "Updated" what I see is that it goes to an empty focus item.

Then it jumps back to the beginning of the header menu and continues on. It just does that once, but it is an extra redundant (and buggy) step.

🇨🇦Canada mgifford Ottawa, Ontario

Now I'm not sure, but I just tried this and wasn't able to replicate it.

In your video it is easy to see the duplication.

I couldn't see it when I explored it locally:

I think this will required more steps for testing.

🇨🇦Canada mgifford Ottawa, Ontario

I like your suggestion around use of mask-image or mask @kentr.

🇨🇦Canada mgifford Ottawa, Ontario

Just looking at more info around strategies for this.

In terms of how we are addressing this:

https://tetralogical.com/blog/2022/12/20/foundations-target-size/

the Target Size (Minimum) criterion is perhaps misleading. It won't guarantee that there are no small targets. It defines an ideal minimum size, but also allows an exception for smaller targets with sufficient spacing. The intent of the criterion is not to make sure targets are large enough to be comfortably activated, but to avoid having targets that are too small clustered closely together, which would otherwise increase the likelihood of a person accidentally activating an adjacent target.

Some exceptions to the rule:

https://dubbot.com/dubblog/2024/staying-on-target.html

It is also important to take into consideration the exceptions to WCAG 2.5.8, which are:

  • If smaller targets (less than 24 x 24 CSS pixels) are arranged so that, when a 24 CSS pixel diameter circle is centered on each target's bounding box, the circles don't overlap with other targets or their circles.
  • Equivalent: The function can be achieved through a different control on the same page that meets this criterion. For instance, in the following screenshot, the targets in the upper right corner are 15px with no minimum spacing, while the ones further down the page are 24px with no minimum spacing. Therefore, the criterion has been met.
  • Inline: The target is in a sentence, or its size is otherwise constrained by the line height. For example, contextual links may be constrained by the vertical spacing between lines of text or the leading.
    • User-agent control: The size of the target is determined by the user agent and is not modified by the author. For example:
    • the options list of
      dropdowns
    • date picker for the HTML date input
    • color picker for the HTML color input
  • Essential: A particular presentation of the target is essential or is legally required for the information being conveyed. For example, the target must be a certain size or shape.

Some other good guidance from Eric Bailey https://www.smashingmagazine.com/2024/07/getting-bottom-minimum-wcag-con... and Adrian Roselli

🇨🇦Canada mgifford Ottawa, Ontario

I suspect that some JS like this will be needed:

(function () {
  const MIN_CONTRAST = 4.5;
  const BACKGROUNDS = [
    { hex: '#ffffff', name: 'white' },
    { hex: '#2A2A2D', name: 'dark grey' }
  ];

  const fields = [
    {
      inputId: 'edit-accent-color',
      warningId: 'accent-warning',
    },
    {
      inputId: 'edit-focus-color',
      warningId: 'focus-warning',
    },
  ];

  function hexToRgb(hex) {
    const val = hex.replace('#', '');
    return {
      r: parseInt(val.slice(0, 2), 16) / 255,
      g: parseInt(val.slice(2, 4), 16) / 255,
      b: parseInt(val.slice(4, 6), 16) / 255,
    };
  }

  function luminance({ r, g, b }) {
    const adjust = (c) =>
      c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
    return (
      0.2126 * adjust(r) + 0.7152 * adjust(g) + 0.0722 * adjust(b)
    );
  }

  function contrastRatio(l1, l2) {
    const [a, b] = [l1, l2].sort((x, y) => y - x);
    return (a + 0.05) / (b + 0.05);
  }

  function updateWarning(field) {
    const input = document.getElementById(field.inputId);
    const existing = document.getElementById(field.warningId);
    if (existing) existing.remove();

    const val = input.value;
    if (!/^#[0-9a-fA-F]{6}$/.test(val)) return;

    const fgLum = luminance(hexToRgb(val));
    const fails = BACKGROUNDS.filter((bg) => {
      const bgLum = luminance(hexToRgb(bg.hex));
      return contrastRatio(fgLum, bgLum) < MIN_CONTRAST;
    });

    if (fails.length) {
      const msg = document.createElement('div');
      msg.id = field.warningId;
      msg.style.color = 'red';
      msg.style.marginTop = '0.25rem';
      msg.textContent =
        '⚠️ Low contrast against: ' +
        fails.map((f) => f.name).join(', ') +
        ` (min ${MIN_CONTRAST}:1)`;
      input.parentNode.appendChild(msg);
    }
  }

  document.addEventListener('DOMContentLoaded', () => {
    fields.forEach((field) => {
      const input = document.getElementById(field.inputId);
      if (input) {
        input.addEventListener('input', () => updateWarning(field));
        updateWarning(field); // initial check
      }
    });
  });
})();
🇨🇦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

Can we spend some time optimizing them before bringing them into Core?

https://www.svgviewer.dev/
https://optimize.svgomg.net/

For instance, 948 bytes:

<svg aria-hidden="true" focusable="false" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path d="M11 0L14 3V16H2V0H11Z" fill="#fff"/>
  <path d="M11 0L14 3V16H2V0H11ZM3 15H13V4H10V1H3V15ZM11 3H12.5L11 1.5V3Z" fill="#7f7f7f"/>
  <path d="M4 2H9V3H4V2Z" fill="#dadada"/>
  <path d="M4 3H9V4H4V3Z" fill="#e0e0e0"/>
  <path d="M4 4H9V5H4V4Z" fill="#ebebeb"/>
  <path d="M4 5H12V6H4V5Z" fill="#eee"/>
  <path d="M4 6H12V7H4V6Z" fill="#f5f5f5"/>
  <path d="M4 7H12V8H4V7Z" fill="#f8f8f8"/>
  <path d="M4 8H12V9H4V8Z" fill="#fcfcfc"/>
  <path d="M4 3H9V4H4V3Z" fill="#9e9e9e"/>
  <path d="M6 8H4V7H6V8ZM12 8H7V7H12V8Z" fill="#9e9e9e" fill-opacity="0.65"/>
  <path d="M8 6H4V5H8V6ZM12 6H9V5H12V6Z" fill="#9e9e9e" fill-opacity="0.8"/>
  <path d="M8 10H4V9H8V10ZM12 10H9V9H12V10Z" fill="#9e9e9e" fill-opacity="0.45"/>
  <path d="M6 12H4V11H6V12ZM10 12H7V11H10V12ZM12 12H11V11H12V12Z" fill="#9e9e9e" fill-opacity="0.3"/>
</svg>

Than 803 bytes:
<svg xmlns="http://www.w3.org/2000/svg" fill="none" aria-hidden="true" viewBox="0 0 16 16"><path fill="#fff" d="m11 0 3 3v13H2V0z"/><path fill="#7f7f7f" d="m11 0 3 3v13H2V0zM3 15h10V4h-3V1H3zm8-12h1.5L11 1.5z"/><path fill="#dadada" d="M4 2h5v1H4z"/><path fill="#e0e0e0" d="M4 3h5v1H4z"/><path fill="#ebebeb" d="M4 4h5v1H4z"/><path fill="#eee" d="M4 5h8v1H4z"/><path fill="#f5f5f5" d="M4 6h8v1H4z"/><path fill="#f8f8f8" d="M4 7h8v1H4z"/><path fill="#fcfcfc" d="M4 8h8v1H4z"/><path fill="#9e9e9e" d="M4 3h5v1H4z"/><path fill="#9e9e9e" fill-opacity=".7" d="M6 8H4V7h2zm6 0H7V7h5z"/><path fill="#9e9e9e" fill-opacity=".8" d="M8 6H4V5h4zm4 0H9V5h3z"/><path fill="#9e9e9e" fill-opacity=".5" d="M8 10H4V9h4zm4 0H9V9h3z"/><path fill="#9e9e9e" fill-opacity=".3" d="M6 12H4v-1h2zm4 0H7v-1h3zm2 0h-1v-1h1z"/></svg>

These are little, but they add up, with every page load.

It could get smaller still if we simplifed how it looks.

🇨🇦Canada mgifford Ottawa, Ontario

Going to Chi's comment in #5

"Claro is using content property for drawing the separators. The possible fix would be to use background-image instead like Olivero does."

I think we need to go from:

.breadcrumb__item + .breadcrumb__item::before {
  display: inline-block;
  padding: 0 0.75rem;
  content: url(...); /* SVG image as content */
}

To something like:

.breadcrumb__item + .breadcrumb__item {
  position: relative;
  padding-left: 1rem; /* or adjust as needed */
}

.breadcrumb__item + .breadcrumb__item::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 0.5rem;
  height: 0.5rem;
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' height='8' width='5'%3e%3cpath fill='currentColor' d='M1.207.647L.5 1.354 3.146 4 .5 6.647l.707.707L4.561 4z'/%3e%3c/svg%3e");
  background-repeat: no-repeat;
  background-size: contain;
}

Using a background-image avoids the problem because backgrounds are considered purely decorative by user agents and not exposed to screen readers.

While messing with this code, why not optimize the carrot (less than arrow, whatever):

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5 8"><path fill="currentColor" d="m1.2.6-.7.8L3.1 4 .5 6.6l.7.8L4.6 4z"/></svg>

This is probably better:

.breadcrumb__item + .breadcrumb__item {
  position: relative;
  padding-left: 1rem; 
}

.breadcrumb__item + .breadcrumb__item::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 0.5rem;
  height: 0.5rem;
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 5 8'%3e%3cpath fill='currentColor' d='m1.2.6-.7.8L3.1 4 .5 6.6l.7.8L4.6 4z'/%3e%3c/svg%3e");
  background-repeat: no-repeat;
  background-size: contain;
}
🇨🇦Canada mgifford Ottawa, Ontario

I made some small changes to the issue summary.

The latest MR adds the ARIA search role to the existing form.

It would be better though to switch core/modules/search/src/Form/SearchBlockForm.php & core/modules/search/src/Form/SearchPageForm.php to just use <search> though. HTML (including search) is always better, if possible, than using ARIA.

🇨🇦Canada mgifford Ottawa, Ontario

I think this color contrast looks good #000000 / #EEEEEE .

If you wanted the edge of the button to have contrast so that it sits above the FFF you could argue for:
#8A8A8A / #FFFFFF

As a background for the button. But I don't think that is what is generally needed for.

Mind you that does seem to be what is suggested here:
https://www.w3.org/WAI/WCAG21/Understanding/non-text-contrast.html#symbo...

I gotta say the grey buttons are ugly.

I'm also not convinced this is a Gin issue, as it is common in any CKEditor config here admin/config/content/formats/manage/content_format

🇨🇦Canada mgifford Ottawa, Ontario

I can move this to the navigation module.

Is there a duplicate issue for this, or are you suggesting there should be another issue opened up?

🇨🇦Canada mgifford Ottawa, Ontario

Ya, I think I filed this in the wrong place. I can move it, but what is the issue.

🇨🇦Canada mgifford Ottawa, Ontario

Just wanted to say that the images at the top came from the Basic Target Size Highlighter from:
https://accessibility-tools.github.io/target-size-highlighter/

There are reasons to use either the basic or Advanced Bookmarklet for this.

To see the outline more clearly in a Chrome Inspector, we can add:

a,
button,
input,
[role="button"],
[role="link"] {
  outline: 2px dashed red !important;
}

To ensure all buttons/links/icons have a minimum clickable size, can't we do something like:

a, button, input, .icon-button, .dropbutton__toggle {
  position: relative;
  min-width: 24px;
  min-height: 24px;
}

a {
  padding: 10px 15px;
  display: inline-block;
}

I like this idea for mobile devices (but that is a AAA SC at this point):

@media (hover: none) and (pointer: coarse) {
  button {
    min-width: 48px;
    min-height: 48px;
    margin: 10px;
  }
}

More on some of these from:
https://allyship.dev/blog/click-target-size

Nice to see Craft CMS aiming for AAA and 44px:

🇨🇦Canada mgifford Ottawa, Ontario

Moving this to Drupal Core as this affects most of Drupal. Gin's moving into Core, but the base problem isn't Gin.

🇨🇦Canada mgifford Ottawa, Ontario

I think that this builds better with the custom colors, but also allows for greater visibility:

.dropbutton-wrapper.open .dropbutton__items,
.dropbutton__items {
  background: var(--gin-bg-layer3, #fff);
  border-radius: var(--gin-border-l, 6px);
  padding: 0.5em;
  min-width: 120px;
  z-index: 9999;
  box-shadow:
    0 4px 8px rgba(var(--gin-color-primary-rgb, 0, 0, 0), 0.4),
    0 8px 20px rgba(0, 0, 0, 0.15) !important;
}

This certainly allows us to customize it more.

🇨🇦Canada mgifford Ottawa, Ontario

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.

#0750E6 / #FFF
#2E6DD0 / #FFF
#4300BF / #FFF
#5B00FF / #FFF
#0F857F / #FFF - Fail (could we not just use #088488)
#00875F / #FFF
#D12E70 / #FFF
#D8002E / #FFF
#DA6303 / #FFF - Fail (Could we not just use #B56026)
#111111 / #FFF

🇨🇦Canada mgifford Ottawa, Ontario

With the image provided, I've updated the Steps to reproduce above.

There are some possible improvements from:

@media (prefers-contrast: more), (forced-colors: active) {

  /* Save button (primary) */
  #edit-submit.form-submit,
  input[type="submit"].button--primary {
    forced-color-adjust: none;
    background-color: ButtonFace !important;
    color: ButtonText !important;
    border: 2px solid ButtonText !important;
    font-weight: bold;
    box-shadow: 0 0 0 2px ButtonText !important;
  }

  #edit-submit.form-submit:hover,
  #edit-submit.form-submit:focus-visible {
    background-color: Highlight !important;
    color: HighlightText !important;
    border-color: HighlightText !important;
    outline: 2px solid Highlight !important;
    outline-offset: 2px;
  }

  /* Preview button (secondary) */
  #gin-sticky-edit-preview.form-submit {
    forced-color-adjust: none;
    background-color: Canvas !important;
    color: ButtonText !important;
    border: 2px dashed ButtonText !important;
    font-weight: normal;
    box-shadow: 0 0 0 1px ButtonText !important;
  }

  #gin-sticky-edit-preview.form-submit:hover,
  #gin-sticky-edit-preview.form-submit:focus-visible {
    background-color: Highlight !important;
    color: HighlightText !important;
    border-color: HighlightText !important;
    outline: 2px solid Highlight !important;
    outline-offset: 2px;
  }

  /* Hide Sidebar Panel button */
  .meta-sidebar__trigger.trigger {
    forced-color-adjust: none;
    background-color: ButtonFace !important;
    color: ButtonText !important;
    border: 1px solid ButtonText !important;
    border-radius: 50%;
    width: 2.25rem;
    height: 2.25rem;
  }

  .meta-sidebar__trigger.trigger:hover,
  .meta-sidebar__trigger.trigger:focus-visible {
    background-color: Highlight !important;
    color: HighlightText !important;
    outline: 2px solid Highlight !important;
    outline-offset: 2px;
  }

  .meta-sidebar__trigger.trigger svg {
    fill: ButtonText !important;
  }
}

But I got a different looking input with forced colors enabled.

🇨🇦Canada mgifford Ottawa, Ontario

I think the menu module is the right place to do this.

I don't know if this should be just for folks who have configured their browsers correctly, but we can start with them:

@media (prefers-contrast: more), (forced-colors: active) {

  .admin-toolbar__expand-button {
    forced-color-adjust: none;
    background-color: ButtonFace !important;
    color: ButtonText !important;
    border: 4px solid ButtonText !important;
    box-shadow: none !important;
  }

  .admin-toolbar__expand-button:hover,
  .admin-toolbar__expand-button:focus-visible {
    background-color: Highlight !important;
    color: HighlightText !important;
    border-color: HighlightText !important;
    outline: 4px solid Highlight !important;
    outline-offset: 2px;
  }

  /* Make SVG chevron icon visible */
  .admin-toolbar__expand-button-chevron {
    fill: ButtonText !important;
    stroke: none !important;
  }
}

It would be interesting to see how that button features in user research. Certainly unsure how it would do with users who are using screen magnifiers.

🇨🇦Canada mgifford Ottawa, Ontario

This isn't a Gin issue specifically.

🇨🇦Canada mgifford Ottawa, Ontario

Sadly we can't use color-contrast() yet
https://caniuse.com/?search=color-contrast

Eventually, I think we might be able to do something like:

.button--primary {
  background-color: var(--accent);
  color: color-contrast(var(--accent) vs ButtonText, Black, White);
}

We really need to fix the accent colors. People can make choices with bad color contrast, but Drupal should not be presenting bad defaults to admins. We can't assume they are testing for color contrast (if we aren't).

.button--primary:hover,
input[type="submit"].button--primary:hover {
  background-color: #003078;
  border-color: #003078;
}

.button--secondary:hover {
  background-color: #f3f2f1;
  color: #003078;
  border-color: #003078;
}

@media (prefers-contrast: more) {
  /* Save Button (more prominent) */
  #edit-submit.form-submit,
  [data-drupal-selector="gin-sticky-edit-submit"] {
    background-color: #000 !important;
    color: #fff !important;
    border: 2px solid #000 !important;
    font-weight: bold;
  }

  /* Preview Button (less prominent) */
  [data-drupal-selector="gin-sticky-edit-preview"] {
    background-color: #fff !important;
    color: #000 !important;
    border: 2px solid #000 !important;
  }

  /* Shared focus style */
  #edit-submit.form-submit:focus-visible,
  [data-drupal-selector="gin-sticky-edit-preview"]:focus-visible {
    outline: 3px solid #000 !important;
    outline-offset: 2px;
    background-color: #ffbf47 !important; 
    color: #000 !important;
  }
}

@media (forced-colors: active) {
  /* Save Button (more prominent) */
  #edit-submit.form-submit,
  [data-drupal-selector="gin-sticky-edit-submit"] {
    forced-color-adjust: none;
    background-color: ButtonText !important;
    color: ButtonFace !important;
    border: 2px solid ButtonText !important;
    font-weight: bold;
  }

  /* Preview Button (less prominent) */
  [data-drupal-selector="gin-sticky-edit-preview"] {
    forced-color-adjust: none;
    background-color: Canvas !important;
    color: ButtonText !important;
    border: 2px solid ButtonText !important;
  }

  /* Focus visibility */
  #edit-submit.form-submit:focus-visible,
  [data-drupal-selector="gin-sticky-edit-preview"]:focus-visible {
    outline: 2px solid Highlight !important;
    outline-offset: 2px;
    background-color: Highlight !important;
    color: HighlightText !important;
  }
}
🇨🇦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

Do we have a list of the currently supported chips?

I made some suggested CSS changes:

@media (forced-colors: active) {
  .gin-status,
  .gin-new-flag,
  .gin-experimental-flag {
    forced-color-adjust: none;
    background-color: ButtonFace !important;
    color: ButtonText !important;
    border: 3px solid ButtonText !important;
  }
}

@media (prefers-contrast: more) {
  .gin-status,
  .gin-new-flag,
  .gin-experimental-flag {
    background-color: ButtonFace !important;
    color: ButtonText !important;
    border: 5px solid ButtonText !important;
    font-weight: bold;
  }
}

However, I'm just seeing the one chip now.

🇨🇦Canada mgifford Ottawa, Ontario

This occurs in the media system, but not sure it is restricted to it.

🇨🇦Canada mgifford Ottawa, Ontario

This CSS is an issue in both Gin & Claro, so this isn't a Gin specific issue. The CSS works reasonably well in both.

🇨🇦Canada mgifford Ottawa, Ontario

Ok, lets minimize the SVG and have it use the ButtonFace so it is more easily customizable

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="ButtonFace" d="M14.6 3 13 1.4a1 1 0 0 0-1.4 0l-1.3 1.4 3 3 1.2-1.3a1 1 0 0 0 0-1.4M5.2 13.6l-3-3 7-7 3 3zm-4.3 1c0 .3 0 .5.3.4l2-.7-1.6-1.6z"/></svg>

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path stroke="ButtonFace" stroke-width="3" d="m2.3 2.3 11.4 11.4m-11.4 0L13.7 2.3"/></svg>

I think there will be better ways to do this, but just looking at the issue of the forced colors mode

@media (forced-colors: active) {
  /* Contextual trigger (pen icon) */
  .contextual .trigger {
    forced-color-adjust: none !important;
    background-color: ButtonFace !important;
    color: ButtonText !important;
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='ButtonFace' d='M14.6 3 13 1.4a1 1 0 0 0-1.4 0l-1.3 1.4 3 3 1.2-1.3a1 1 0 0 0 0-1.4M5.2 13.6l-3-3 7-7 3 3zm-4.3 1c0 .3 0 .5.3.4l2-.7-1.6-1.6z'/%3e%3c/svg%3e") !important;
    background-repeat: no-repeat;
    background-position: center;
    background-size: 16px 16px;
    border: 1px solid ButtonText !important;
    width: 26px;
    height: 26px;
    opacity: 1 !important;
  }

  .contextual .trigger:hover,
  .contextual .trigger:focus-visible {
    background-color: Highlight !important;
    color: HighlightText !important;
    outline: 2px solid Highlight !important;
  }

  /* Remove button (X icon) */
  .media-library-item__remove {
    forced-color-adjust: none !important;
    background-color: ButtonFace !important;
    color: ButtonText !important;
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath stroke='ButtonFace' stroke-width='3' d='m2.3 2.3 11.4 11.4m-11.4 0L13.7 2.3'/%3e%3c/svg%3e") !important;
    background-repeat: no-repeat;
    background-position: center;
    background-size: 0.75rem;
    border: 1px solid ButtonText !important;
    opacity: 1 !important;
  }

  .media-library-item__remove:hover,
  .media-library-item__remove:focus-visible {
    background-color: Highlight !important;
    color: HighlightText !important;
    outline: 2px solid Highlight !important;
  }

  /* Edit button (pen icon) */
  .media-library-item__edit {
    forced-color-adjust: none !important;
    background-color: ButtonFace !important;
    color: ButtonText !important;
    border: 1px solid ButtonText !important;
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='ButtonFace' d='M14.6 3 13 1.4a1 1 0 0 0-1.4 0l-1.3 1.4 3 3 1.2-1.3a1 1 0 0 0 0-1.4M5.2 13.6l-3-3 7-7 3 3zm-4.3 1c0 .3 0 .5.3.4l2-.7-1.6-1.6z'/%3e%3c/svg%3e") !important;
    background-repeat: no-repeat;
    background-position: center;
    background-size: 0.75rem;
    opacity: 1 !important;
  }

  .media-library-item__edit:hover,
  .media-library-item__edit:focus-visible {
    background-color: Highlight !important;
    color: HighlightText !important;
    outline: 2px solid Highlight !important;
  }
}

There is some JS which is is hiding edit pen when the mouse isn't hovering over the form:

<form class="views-exposed-form" aria-label="Filter the contents of the Media view" data-drupal-selector="views-exposed-form-media-library-page" action="/admin/content/media-grid" method="get" id="views-exposed-form-media-library-page" accept-charset="UTF-8" data-once="exposed-form">

It converts this:

<button class="trigger focusable visually-hidden" type="button" aria-pressed="false">Open  configuration options</button>

to this:
<button class="trigger focusable" type="button" aria-pressed="false">Open configuration options</button>

But it doesn't trigger for keyboard users (focus).

It should always be visible, especially in a high contrast mode.

🇨🇦Canada mgifford Ottawa, Ontario

This occurs in Claro too, so is a Core issue.

🇨🇦Canada mgifford Ottawa, Ontario

Interesting. It works as expected in the Add Field Page.

But not in the Add Field Dialog.

🇨🇦Canada mgifford Ottawa, Ontario

@kentr I'd say that we make this change globally. If it were only in core themes it would be confusing. I'd agree that this should be fixed in core, and as such the Gin maintainers were right in having it marked it Won't Fix.

Would be nice to fix this bug before it turns 13.

🇨🇦Canada mgifford Ottawa, Ontario

We're now at 1.2 and there is a draft of 1.3
https://www.w3.org/WAI/standards-guidelines/aria/

Should we update this or start a new issue?

🇨🇦Canada mgifford Ottawa, Ontario

Might be a good fit first for Drupal CMS? Think we could be starting to ask more questions there? It is perhaps more useful to start asking questions and gathering information from those new to the community, than those already entrenched into D.o.

Both could be useful. I'm not arguing against involvement in Core. I see lots of ways that we could use Drupal itself to start promoting involvment in the Drupal community.

🇨🇦Canada mgifford Ottawa, Ontario

@scott_euser here is some reason comparisons between the two:

All three articles agree that AVIF’s superior compression leads to smaller image file sizes, which can reduce bandwidth usage and potentially lower energy consumption during data transmission. However, AVIF’s increased decoding time may require more processing power on the client side, possibly offsetting some energy savings.

🇨🇦Canada mgifford Ottawa, Ontario

With Drupal CMS the impact would be even bigger with all the contrib files. I do think this is worth exploring more. Would there ever be a need to produce a developer version (with the extra code), or is that just what you would find in Git?

And agreed @borisson_ it is about setting up good defaults for when everyone works to download Drupal (or Drupal CMS).

🇨🇦Canada mgifford Ottawa, Ontario

Are there advantages here with the latest version of PHPUnit? I don't actually know but there have been improvements since the last comment https://phpunit.de/announcements/phpunit-12.html

  • I believe we need to be searching for tests extending BrowserTestBase that perform setup through UI interactions.()
  • Then looking replacing browser interactions with direct API calls using services like entity_display.repository or entity storage handlers.()
  • Provide clear documentation within the test code to explain the rationale for using API calls over browser interactions.()
  • Measure test execution time before and after refactoring to quantify improvements.

Does that seem right?

🇨🇦Canada mgifford Ottawa, Ontario

So if we enabled File Hash and ensured that the duplicate file prevention was enabled, then this would be closed.

That seems more useful than the Audit Files module approach.

Think this should module should be considered for Drupal CMS?

🇨🇦Canada mgifford Ottawa, Ontario

Should we mark this as a duplicate and focus in on #3432765?

🇨🇦Canada mgifford Ottawa, Ontario

My understanding is that the Lighthouse test are the same as Deque's axe-core. Google uses axe-core.

🇨🇦Canada mgifford Ottawa, Ontario

Just adding @dunx to the metadata of this issue.

🇨🇦Canada mgifford Ottawa, Ontario

Bringing this up as there seem to be some outstanding issues.

We should be much closer to making this conversion the default though.

🇨🇦Canada mgifford Ottawa, Ontario

It has been over a year since someone looked at it. If @chi's perception of this is right, there are thousands more in savings that have been lost (let alone the environmental impact of running millions of processes on servers as part of this).

@joachim’s 2023 suggestion was to split the problem into separate issues for each test type. I tried to summarize would this would look like using ChatGPT to highlight what this would look like, along with justification and next steps. I'm mostly doing this to nudge this ahead.

Do these new issues sound like good places to begin? Has there been other work to do this already?

1. Kernel Tests: Optimize setUp() Execution

Goal: Avoid redundant setUp() execution for each method in Kernel tests.

Justification:
• Kernel tests do not boot the full stack and have fewer side effects.
• A shared setUp() could save significant time across large test classes.

Suggested New Issue:

“Optimize PHPUnit Kernel test execution by avoiding repeated setUp() calls per method.”

Action Items:
• Benchmark time saved when sharing setUp() across methods.
• Propose an attribute or annotation to allow opting in/out per class.
• Ensure tearDown cleanup logic remains reliable.

2. Browser Tests (Functional + FunctionalJavascript): Improve Test Efficiency

Goal: Investigate safe ways to cache installed sites between test methods in browser-based test classes.

Justification:
• Browser tests are the slowest due to full Drupal installs and HTTP interactions.
• Current design reinstalls Drupal for every method—even within the same class.

Suggested New Issue:

“Enable shared Drupal site installations across Browser test methods to reduce test run time.”

Action Items:
• Explore integration with #2900208 for site install caching.
• Identify classes that could safely share a site environment.
• Flag test classes with dependencies on state or side effects to avoid breaking isolation.

3. JavaScript Tests: Evaluate Efficiency Gains from Shared setUp()

Goal: Identify whether JS tests (Nightwatch or others) could benefit from setup sharing, or if they already do.

Justification:
• JS test infrastructure is separate (Node/Nightwatch/WebDriver), but setup redundancy may still exist.
• Even if not using PHPUnit, parallels in redundant test bootstrapping may exist.

Suggested New Issue:

“Audit and optimize Nightwatch JS tests for redundant setup executions across test methods.”

Action Items:
• Confirm whether test fixtures or browser states are rebuilt between methods.
• Identify opportunities for fixture reuse or browser session persistence.

Cross-Issue Meta Task: Central Coordination and Tracking

Create a meta issue to link all three above:

“Optimize test performance by limiting redundant setUp() execution across test types.”

Justification for Splitting

Joachim’s 2023 comment was correct: different test types have different performance bottlenecks, state requirements, and risk profiles. Attempting to solve them all in one issue creates blockers and lack of clarity. Splitting enables:
• Focused benchmarking
• Different contributors to work in parallel
• Faster consensus and review
• Targeted test infrastructure changes

🇨🇦Canada mgifford Ottawa, Ontario

Looking back at this issue now.

ImageAPI Optimize looks quite flexible, it will work for D10 & 11 but does require another 3rd party system. It would be interesting to see if that could be incorporated into Drupal CMS but unsure if it would be a good candidate for Core. Image optimization should be easy, but building in a 3rd party into Core would be a problem (not matter which one).

Imagemagick could also do the trick, and has more installs.

Some MIT projects that might be worth looking at:

🇨🇦Canada mgifford Ottawa, Ontario

There is going to be some overlap with https://www.drupal.org/project/gin/issues/3506350 🐛 Several icons and images are invisible in Forced Colors mode Active

Target is the Gin settings page admin/appearance/settings/gin

For the first item, this patch should be useful as this does provide an alternative for forced-colors:

@media (forced-colors: active) {
  input[type="radio"] {
    appearance: auto !important;
    forced-color-adjust: auto !important;
  }

  /* Reset default label styling for all options */
  input[type="radio"] + label {
    all: unset;
    display: inline-block;
    padding: 0.2em 0.5em;
  }

  /* Style the selected option */
  input[type="radio"]:checked + label {
    background-color: ButtonFace !important;
    color: ButtonText !important;
    font-weight: bold !important;
    text-decoration: underline !important;
    border: 2px solid HighlightText !important;
  }

  input[type="radio"]:focus-visible + label {
    outline: 2px solid Highlight !important;
    outline-offset: 2px;
  }
}
🇨🇦Canada mgifford Ottawa, Ontario

Removing forced-colors to avoid confusion with "high contrast" as they are treated the same here.

🇨🇦Canada mgifford Ottawa, Ontario

Looking at admin/people/roles I think this would address the problem:

<a href="#" title="Change order" class="tabledrag-handle tabledrag-handle-y">
  <div class="handle" aria-hidden="true">↕</div>
  <span class="visually-hidden">Drag to reorder row</span>
</a>
@media (forced-colors: active) {
  .tabledrag-handle .handle {
    background: none !important; /* Removes background image or mask */
    color: ButtonText !important; /* Uses system text color */
    border: 1px solid ButtonText;
    padding: 2px 4px;
    font-size: 1em;
    display: inline-block;
  }

  .tabledrag-handle {
    cursor: row-resize !important;
  }
}

🇨🇦Canada mgifford Ottawa, Ontario

Looking at this now, I think that what we're talking about is:

.icon-link:focus {
    box-shadow: 0 0 0 1.5px var(--color-white),0 0 0 3.5px var(--color-focus);
}

Which you can see in places like where @andrewmacpherson described.

I think it is part of Edge/Chrome as described here (in the 'A More Visible Focus Ring' section):
https://blog.chromium.org/2020/03/updates-to-form-controls-and-focus.html

I don't know where we are overriding the Chrome/Edge defaults though.

Plus, now we have to think about how this applies to Gin.

🇨🇦Canada mgifford Ottawa, Ontario

Thanks for updating this @xjm.

We do indeed want to remove the target="_blank" attribute where we can.

🇨🇦Canada mgifford Ottawa, Ontario

So if I understand this correctly, we can overwrite this by simply, ensuring that everywhere we are leveraging System Color names we don't overwrite them with systems colors. I think with:

@media (forced-colors: active) {
  .gin-toolbar,
  .toolbar-menu-administration {
    background-color: Canvas;
    color: CanvasText;
    border-color: ButtonText;
  }

  a,
  .link {
    color: LinkText;
    text-decoration: underline;
  }

  .button,
  input[type="submit"],
  .form-submit {
    background-color: ButtonFace;
    color: ButtonText;
    border: 1px solid ButtonText;
  }

  .form-item input,
  .form-item textarea,
  .form-item select {
    background-color: Field;
    color: FieldText;
    border: 1px solid GrayText;
  }

  .focus-ring,
  :focus {
    outline: 2px solid Highlight;
    outline-offset: 2px;
  }

  /* Avoid hidden text in HC mode */
  .visually-hidden {
    position: static !important;
    clip: auto !important;
    height: auto !important;
    width: auto !important;
    overflow: visible !important;
  }
}

I don't think we need to protect the entire system colors, just those we are using.

🇨🇦Canada mgifford Ottawa, Ontario

So putting up a warning here when forced-colors is active could be done by modifying the HTML:

<span class="fieldset__label fieldset__label--group">Appearance</span>

to

<span class="fieldset__label fieldset__label--group">Appearance <span class="forced-colors-warning" role="note">– Unavailable in Forced Colors Mode</span></span>

And adding this CSS:

/* Hide by default */
.forced-colors-warning {
  display: none;
  font-weight: bold;
  border: 0px solid transparent;
  color: ButtonText;
  background-color: ButtonFace;
}

/* Show only in forced colors mode */
@media (forced-colors: active) {
  .forced-colors-warning {
    display: inline;
    border-color: ButtonText;
  }
}
🇨🇦Canada mgifford Ottawa, Ontario

Ok, so this won't work for older browsers, but I think what you're suggesting here is essentially just:

/* Hide the label/input/description if the alias input is disabled */
.js-form-item-path-0-alias:has(input[disabled]) {
  display: none;
}
🇨🇦Canada mgifford Ottawa, Ontario

Focus indicators should be highly visible, especially for keyboard-only users, and distinct from hover and checked states to meet WCAG 2.1/2.2 guidelines (notably 2.4.7: Focus Visible and 1.4.11: Non-Text Contrast).

The .media-library-item--grid.checked element, and you’re looking to clearly distinguish:
• Focus (keyboard navigation) - Focus styles are too subtle: Low contrast
• Checked (selected state) - Checked (selected) and focus may overlap visually, creating ambiguity for users.
• Hover (mouse interaction) - Hover gets stronger visual weight than focus, which undermines keyboard accessibility.

@rkoller has great points. I'd like to see:
1. Visibly highlight focus — bold border or shadow with high contrast.
2. Maintain distinct styles for .checked vs :focus.
3. Prioritize accessibility over aesthetics for keyboard users.

This code works:

/* Default item */
.media-library-item--grid {
  border-radius: var(--gin-border-xl);
  border: 2px solid transparent; /* prepare for overrides */
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

/* Checked state */
.media-library-item--grid.checked {
  border-color: #0071bc; /* or your primary theme color */
  background-color: #e6f0fa; /* soft background to indicate selection */
}

/* Focus state */
.media-library-item--grid:focus-within {
  border-color: #ffbf47; /* strong yellow or contrasting color */
  box-shadow: 0 0 0 2px rgba(255, 191, 71, 0.8); /* high-contrast ring */
  outline: none;
}

/* Focused + checked (combined) */
.media-library-item--grid.checked:focus-within {
  border-color: #004080; /* darker shade of primary for differentiation */
  box-shadow: 0 0 0 3px rgba(0, 64, 128, 0.8);
}

/* Hover (less prominent than focus) */
.media-library-item--grid:hover {
  border-color: #cccccc;
  background-color: #f8f8f8;
}

The CSS here can definitely be improved.

Testing Guidance

  • Use Tab to navigate to the element — the focus style should be clearly visible, even on low-quality screens.
  • Confirm that focus is visually different from both hover and checked.
  • Check color contrast of focus styles — ensure at least 3:1 contrast with the background for non-text indicators per WCAG 2.1 1.4.11.

Accessibility Rationale

  • Keyboard users rely on visible focus indicators for navigation and interaction. If hover gets more attention than focus, it violates WCAG and harms usability.
  • Combining :focus-within and .checked gives you clear visual cues without clutter or confusion.
  • The use of borders and shadows improves non-text contrast and is screen-reader independent.
🇨🇦Canada mgifford Ottawa, Ontario

I think this is a general issue with tables in forms. Maybe Tables are elsewhere though.

🇨🇦Canada mgifford Ottawa, Ontario

Yes, this is an issue in Core though, not just Gin. With Claro (and Olivero) and /admin/content/media I have the same effect.

🇨🇦Canada mgifford Ottawa, Ontario

Looking at my DDEV site I can repeat this problem on https://drupal-cms-1.ddev.site:8014/node/7/edit

I think the cleanest illustration of that is here.

  1. For testing if you
  2. click in the "Tags" field at the bottom (or whatever the bottom field is in the form,
  3. tab (nothing is in focus), tab (nothing is in focus) - you can later click on this to see where it goes seems to go to the first field in the form
  4. go ahead past "Save" and "Preview" then to the "Hide Sidebar Panel" button.
  5. Then try to go back using shift-tab
  6. You can't seem to get back into the main body of the page to make edits.

And yes, it is a complicated page so having a better way to skip into the content would be helpful.

🇨🇦Canada mgifford Ottawa, Ontario

The current color can be described by:

hsl(46, 67%, 52%)
rgb(216, 178, 52)
#D8B234

I'm no color expert but changing it to this

hsl(41, 95%, 31%)
rgb(157, 110, 4)
#9D6E04

Would meet color contrast accordign to Tanaguru.com's Contrast Finder.

🇨🇦Canada mgifford Ottawa, Ontario

Does this need another review? I didn't see the comments from @rinku jacob 13 visible.

It looks like @tomislav.matokovic has a patch and have before/after images.

I can see that there does seem to be better support for darkmode, but I don't understand this CSS enough to evaluate the code.

🇨🇦Canada mgifford Ottawa, Ontario

@tomislav.matokovic just wanted to follow up to see if you still think this issue should be closed or if @rkoller has clarified things.

🇨🇦Canada mgifford Ottawa, Ontario

Gin will be in core for the release of 11.3 . But yes, agreed that this should remain open for D10 to see if it can be addressed.

🇨🇦Canada mgifford Ottawa, Ontario

Any updates @sandip? It would be good to address the challenges that @kanchan bhogade described.

🇨🇦Canada mgifford Ottawa, Ontario

@batigolix We can change the active menu item to have a color that is distinct from the background and foreground colour, right?

Could we use the new CSS contrast() feature?

🇨🇦Canada mgifford Ottawa, Ontario

@batigolix with this announcement about Gin being included into Core, do you think it is better to improve the colors in the Gin specific styling?

https://www.drupal.org/about/core/blog/drupal-core-will-adopt-gin-admin-...

Think we could apply a gradient so that we can avoid the problems with the submit button, and still have a sticky bar that clearly indicates a change of status?

🇨🇦Canada mgifford Ottawa, Ontario

@saschaeggi do you want to review the new code from @batigolix? @snehal-chibde, thanks for the screenshots with the improvement.

🇨🇦Canada mgifford Ottawa, Ontario

@batigolix @snehal-chibde Do the existing screenshots clarify things in the latest version? It seems like with code verification we may be able to get this to RTBC.

Production build 0.71.5 2024