Page MenuHomePhabricator

Use CSS filters to change icon colors
Open, Needs TriagePublic

Description

CSS filters (not to be confused with svg filters) are quite widely supported now (exceptions: Edge 12, IE11 and Opera Mini)
https://caniuse.com/#search=css%20filter

Luckily feature queries are supported by Opera Mini and all browsers that support SVG filters (https://caniuse.com/#search=feature%20queries)

Thus we can provide icon variants for free with CSS filters using PNG (uri) fallbacks.

Currently 3 icon variants require 3 data uris which can be quite large.

I propose we go back to 1, and use css filters to color icons accordingly
https://codepen.io/anon/pen/VdNWGy

Update: CodePen with an OOUI derived, progressive, frameless button and a normal one with CSS filter and same icon:
https://codepen.io/Volker_E/pen/pxPRLM?editors=1100

Note: For this to work, an SVG must be added to the HTML either via JS or HTML which defines the available variant filters. The required matrixes for these filters can be derived from rgb values.

acceptance criteria

  • PNGs are generated for all icons
  • Move all SVG background image declarations into a feature query
  • Add support for a progressive variant.

Icons would need to be progressive by default (blue) to support conversion to all 4 variants.

Sample CSS output for 1 icon and 4 variants.

// progressive is default
.mw-ui-icon-minerva-clock:before {
   background-image: url(/w/load.php?modules=mobile.startup.images.variants&image=clock&format=rasterized&lang=en&skin=minerva&version=1s9zh6w);
}

.mw-ui-icon-minerva-clock-black:before {
        background-image: url(/w/load.php?modules=mobile.startup.images.variants&image=clock&variant=black&format=rasterized&lang=en&skin=minerva&version=1s9zh6w);
}
.mw-ui-icon-minerva-clock-gray:before {
        background-image: url(/w/load.php?modules=mobile.startup.images.variants&image=clock&variant=gray&format=rasterized&lang=en&skin=minerva&version=1s9zh6w);
}
.mw-ui-icon-minerva-clock-invert:before {
        background-image: url(/w/load.php?modules=mobile.startup.images.variants&image=clock&variant=invert&format=rasterized&lang=en&skin=minerva&version=1s9zh6w);
}


@supports ( filter: invert( 0 ) ) {
    .mw-ui-icon-minerva-clock-black:before,
    .mw-ui-icon-minerva-clock:before,
    .mw-ui-icon-minerva-clock-gray:before,
    .mw-ui-icon-minerva-clock-invert:before {
       // must be a progressive icon
        background-image: linear-gradient(transparent,transparent),url(data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%222…0zm-1.037 5v4.524l3.904 2.298.66-1.1-3.192-1.877V5H10.39z%22/%3E%3C/svg%3E);
    }
   .mw-ui-icon-minerva-clock:before {
    filter: brightness(0);
  }
   .mw-ui-icon-minerva-clock-gray:before {
    filter: contrast(0.05);
  }
   .mw-ui-icon-minerva-clock-invert:before {
    filter: invert(1);
  }
}

Developer notes

Next steps - we should setup a code pen for one icon and test in the problematic browsers.
A patch was written: https://gerrit.wikimedia.org/r/#/c/mediawiki/core/+/463380/ - for OOUI 50% of the CSS module was trimmed
but it wasn't clear what we should do for older browsers.

Event Timeline

Restricted Application added a subscriber: Aklapper. · View Herald TranscriptJul 3 2018, 10:26 PM
Volker_E renamed this task from Use svg filters to change icon colors to Use SVG filters to change icon colors.Sep 11 2018, 3:49 PM

From today's front-end developer group meeting:

Given the extra SVG element with matrix filter in HTML might be a) overly complex and b) run into caching issues on Special:Pages as HTML and CSS run out of sync – which in turn could be addressed by outputting SVG element through JS, but that would add to a), I think relying on

  • a combination of CSS filters might be sufficient to start from a black icon
    • and provide all color hues and opacities needed for states as long as we don't add multi-color icons imagery.
    • It would also separate cleaner presentation from content reyling only on CSS
    • we could continue using our current setup with all the different images and progressively enhance by @support feature query setting filters and resetting background-images

We need to make sure though, that the

  • browsers don't load the icons reset in @support feature queries and also, that
  • the CSS filters are applied consistently across browsers.

I've seen a minor issue of different resulting hues in a test a while ago at https://gerrit.wikimedia.org/r/#/c/oojs/ui/+/324014/

Change 463373 had a related patch set uploaded (by Jdlrobson; owner: Jdlrobson):
[mediawiki/skins/MinervaNeue@master] Use default filters baked into core for last modified

https://gerrit.wikimedia.org/r/463373

This comment was removed by Jdlrobson.

Change 463380 had a related patch set uploaded (by Jdlrobson; owner: Jdlrobson):
[mediawiki/core@master] Allow icon variants without the additional image

https://gerrit.wikimedia.org/r/463380

Change 463381 had a related patch set uploaded (by Jdlrobson; owner: Jdlrobson):
[mediawiki/core@master] OOUI icon packs use CSS filters rather than image assets for inverted icons

https://gerrit.wikimedia.org/r/463381

I experimented with this and for little risk to old browsers, we can have gray, black and white icons using contrast and invert filters.

If we want progressive icons, right now I don't see any way to do that without adding a custom CSS filter (none of the predefined ones seem to be able to color icons that initially have no color).

Big wins to be had here in OOUI with just the inverted icon:

mw.inspect();
var ooIcons = Object.keys(mw.loader.moduleRegistry).filter((key) => key.indexOf( 'oojs-ui.styles.icons' ) > -1);
mw.loader.using(ooIcons).then( function () {
    console.log(ooIcons.map((name)=> mw.inspect.getModuleSize(name)).reduce((a,b) => a + b, 0 ))
} );

Before: 401, 164
After: 176, 040

Krinkle added a subscriber: Krinkle.EditedSep 27 2018, 10:38 PM

@Jdlrobson Check you compare the transfer size of the request that included the module instead? Compression is a big deal, even more so in a batch with other stylesheets. Requesting them in isolation, or without compression, or from mw.inspect is not representative.

I intend to remove the raw sizes from mw.inspect in the next quarter when we work on T194684, in favour of Resource Timing information which exposes the transfer sizes instead. Apart from the aforementioned skews and misrepresentation, it also means it can correctly report 0 for cache hits from localStorage and/or browser cache.

Is there an easy way to generate the size? I guess I'd need to construct a RL url using Object.keys(mw.loader.moduleRegistry).filter((key) => key.indexOf( 'oojs-ui.styles.icons' ) > -1) ?

Krinkle added a comment.EditedSep 27 2018, 11:05 PM

View or make a page that uses them in some representative way, then with "Disable cache" checked and local storage cleared once, reload the page and check the transfer size in DevTools / Network.

(This is from prod, but the same applies locally.)

iew or make a page that uses them in some representative way

That's where i'm struggling. I can't find one that uses all of them :) I'll construct the URI...

Krinkle added a comment.EditedSep 27 2018, 11:08 PM

iew or make a page that uses them in some representative way

That's where i'm struggling. I can't find one that uses all of them :)

Let's use realistic examples. If common usage only uses a few of them, our comparison should also use a few of them only. Otherwise we're optimising something that users wouldn't actually experience.

Separate from this, we can speculate how much bandwidth we're saving humanity overall. But that's a separate metric we'd measure and present in a different way.

Jdlrobson added a comment.EditedSep 27 2018, 11:24 PM

Let's use realistic examples.

Sorry. I didn't mean to suggest this was not realistic. VisualEditor loads all of these icons. I just didn't have VisualEditor setup locally so I couldn't test.

I can see it uses all of these icons like so:

> ooModules = Object.keys(mw.loader.moduleRegistry).filter((key) => key.indexOf( 'oojs-ui.styles.icons' ) > -1);
> readyModules = ooModules.filter((key)=>mw.loader.getState(key) === 'ready')
> readyModules.length
> 0

Click edit.

> readyModules = ooModules.filter((key)=>mw.loader.getState(key) === 'ready').length
> readyModules.length
> 15

Before change: 26622
content-length: 21157

So 5k after gzipping

Another point in the replacement of invert icons is, that we would need to remove the SVG fallback for browsers which don't support filter immediately otherwise we have the PNG fallback reference, the embedded inverted icon inlined and the filter property in the CSS. Increased file size, not gain.

Volker_E added a comment.EditedOct 3 2018, 8:58 PM

My last comment was about relying only on CSS filter not about the current task description of SVG filters.
And the problem of relying only on them would affect IE9-11 & Edge 12 and Opera Mini which sounds like a blocker to me – pushing at best 1x PNG fallbacks at it?

Jdlrobson renamed this task from Use SVG filters to change icon colors to Use CSS filters to change icon colors.Oct 4 2018, 11:33 PM
Jdlrobson updated the task description. (Show Details)
Jdlrobson updated the task description. (Show Details)Oct 4 2018, 11:54 PM
Jdlrobson updated the task description. (Show Details)Oct 4 2018, 11:59 PM
Jdlrobson updated the task description. (Show Details)

From conversation with @Jdlrobson today. How I would see the best setup:

  1. Have PNGs (to decide if 1x or 2x or nx) as icons default per background-image
  2. Use SVG (only one color progressive for being able to hue-rotate) of each icon within @supports query and apply filters to corresponding variants

This way we make sure, that browsers without support fallback to PNGs and load only one image per icon variants classes.

Volker_E updated the task description. (Show Details)Oct 5 2018, 12:04 AM
Volker_E updated the task description. (Show Details)Oct 10 2018, 6:03 PM
Volker_E added a comment.EditedOct 10 2018, 7:05 PM

Provided CodePen with an OOUI derived, progressive, frameless button icon+label output with CSS filter https://codepen.io/Volker_E/pen/pxPRLM?editors=1100

Benefits:

  • more shades easily possible
  • less data (to be evaluated) how much this gains us
  • nice transitions between action item states possible through filter, no need to inline other state icons

Cautious about:

  • Browser support of filters, QAing strongly advised.
    • Opera Mini and IE 11 would fallback to PNGs. IE 11 on HiDPI would probably see a minor regression, but due to the nature of IE 11's natural environment that might be a negligible number of folks.
    • Could it go wrong anywhere in filter supporting browsers due to browser quirks?! Resulting in showing progressive icons in non-actionable places. Possible intermediate way to deal with this would be to rely on Accent10 icon as starting point
Restricted Application added a subscriber: Masumrezarock100. · View Herald TranscriptNov 5 2019, 7:59 PM
Krinkle removed a subscriber: Krinkle.Nov 5 2019, 11:26 PM
Jdlrobson updated the task description. (Show Details)Dec 18 2019, 12:09 AM

Change 463380 abandoned by Jdlrobson:
Allow icon variants without the additional image

Reason:
Please continue conversing on ticket

https://gerrit.wikimedia.org/r/463380

Change 463373 abandoned by Jdlrobson:
Use default filters baked into core for last modified

Reason:
Please continue conversation on ticket.

https://gerrit.wikimedia.org/r/463373

Jdlrobson updated the task description. (Show Details)Dec 18 2019, 12:11 AM

Change 463381 abandoned by Jdlrobson:
OOUI icon packs use CSS filters rather than image assets for inverted icons

Reason:
please continue discussion on phab ticket

https://gerrit.wikimedia.org/r/463381