Jump to content

Icon standardisation

From mediawiki.org

The purpose of this page is to document current methods for styling icons across MediaWiki extensions.

Icons in Flow

[edit]

Overview

[edit]

Flow uses a local copy of WikiFont for most of its icons.

It has some implicit icons with no text: [···] for drop-down, v for its sort menu, but most are [glyph] Action name.

Example

[edit]

To render a lock icon, but with backup text for screen readers and older/incompatible browsers (text does not render in modern browsers; just the icon):

<span class="wikiglyph wikiglyph-lock">Lock</span>

Browser support

[edit]

Icons: IE9+, FF3.5+, Chrome, Safari 3.2+. (IE8 support can be added with an uglier CSS hack, if applied to the patch linked below.)

Backup text: all browsers, screen readers.

Code

[edit]

icons.less (backup text support is in a patch)

Icons in Growth

[edit]

Overview

[edit]

We generally use background images, with SVG and PNG fallback. We are fine with using WikiFont for permanent features.

However, we want the ability to use SVG/PNG (or a similar low-friction method) for experimentation, so not every experiment needs to go through the full WikiFont workflow.

This workflow is:

  • Regenerate the output font files (which currently is not scripted)
  • Commit the updated/added source file, and updated generated files to the WikiFont repository (getting code review for this)
  • Commit the built version of WikiFont to core (getting code review for that)
  • If a Growth extension needs the icon before the normal train deployment, cherry-pick the core commit to a core deployment branch

Then (in the event we decide not to make it a permanent feature) remove the icon, doing the whole thing (except the cherry-pick) in reverse.

Example

[edit]

[1]

Browser support

[edit]

As with MobileFrontend, we rely on having a correctly sized fallback PNG for those browsers that don't support SVG and the `background-size` property.

Code

[edit]
<div class="mw-icon-copyedit"></div>
<div class="mw-icon-edit"></div>
.mw-icon-copyedit { 
  .background-image-svg('images/light_bulb_dark_gray.svg', 'images/light_bulb_dark_gray.png');
   margin: 2px 0 2px -10px;
   height: 43px;
   width: 43px;
}
.mw-icon-edit { 
  .background-image-svg('images/pencil_white.svg', 'images/pencil_white.png');
}

Icons in MobileFrontend

[edit]

Overview

[edit]
  • In mobile we use SVGs and fallback pngs with background-size
  • All our classes are top level without nesting. This is an OOCSS approach.
  • In mobile we use a generic icon class which defaults to 24px width and height and is designed to hide any text inside the element the class is associated with
  • We use modifiers to tweak an icon.
    • Modifiers define the icon image e.g. icon-settings.
    • Modifiers can change the icon size. e.g. icon-32px would set the icon to use a background size of 32px
    • Modifiers can make the icon appear to the left of the text by using the icon-text class

Example

[edit]
<ul>
  <li>
    <a href="/w/index.php?title=Special:Preferences&amp;returnto=Special%3AMobileMenu"
      class="icon-settings icon icon-text">Preferences</a>
  </li>
</ul>
/* generic case - an icon with no specified image that is 24px and text is hidden */
.icon {
   background-repeat: no-repeat;
   .background-size( 24px, auto );
   cursor: pointer;
   background-position: center center;
   text-indent: -9999px;
   overflow: hidden;
   .default-icon-size( 24px );
 
   &[disabled] {
       opacity: .5;
   }
}

/* icons which carry text are aligned to left with no indent so text is visible */
.icon-text {
   text-indent: 0;
   background-position: left center;
}

/* add the image to the icon - in this case a svg with png fallback of a cog */
.icon-settings {
   .background-image-svg-quick( 'images/cog' );
}

See output of this example on MobileMenu

Browser support

[edit]
  • The only browsers that experience problems with our method are browser which do not support `background-size`. These also correspond with browsers that do not support SVG. To get around this we ensure the fallback PNG has the same dimensions as the situation where it used. For example if an element has the class "icon-unicorn icon icon-64px" the fallback png would (or should) be 64px by 64px.

Code

[edit]

Our icons less file documents usage in MobileFrontend.

Icons in VisualEditor

[edit]

Overview

[edit]

VisualEditor uses the OO.ui.IconedElement mixin from OOjs UI to add icons to widgets. There is also a related mixin, OO.ui.IndicatedElement, that adds an "indicator". Icons are 24x24px and generally go on the left, whereas indicators are 12x12px and generally go on the right. A good example of an indicator is the down arrow to the right of the label on a dropdown.

Mixing in IconedElement and IndicatedElement is simple. The mixins read the icon and indicator parameters from the config object and generate spans with the right CSS in this.$icon and this.$indicator. All the caller has to do is pass in the icon/indicator parameter to the widget's constructor, and all the widget's constructor has to do is take this.$icon and this.$indicator and put them in the DOM somewhere.

Most widgets are optionally iconed/indicated, and default to not using them. Some widgets have a default icon/indicator which will be used unless overridden.

Example

[edit]

TextInputWidget, for example, mixes in both IconedElement and IndicatedElement. It doesn't specify a default for either the icon or the indicator, so neither will be displayed unless the constructor is called in a way that explicitly calls for an icon and/or indicator.

OO.ui.TextInputWidget = function OoUiTextInputWidget( config ) {
	// Configuration initialization
	config = config || {};

	// Parent constructor
	OO.ui.TextInputWidget.super.call( this, config );

	// Mixin constructors
	OO.ui.IconedElement.call( this, this.$( '<span>' ), config );
	OO.ui.IndicatedElement.call( this, this.$( '<span>' ), config );

	// (...)

	// Initialization
	this.$element
		.addClass( 'oo-ui-textInputWidget' )
		.append( this.$icon, this.$indicator );
};

/* Setup */

OO.inheritClass( OO.ui.TextInputWidget, OO.ui.InputWidget );
OO.mixinClass( OO.ui.TextInputWidget, OO.ui.IconedElement );
OO.mixinClass( OO.ui.TextInputWidget, OO.ui.IndicatedElement );

Then this is how SearchWidget constructs a TextInputWidget. It specifies the icon by symbolic name. The indicator is not specified, and TextInputWidget doesn't have a default indicator, so no indicator will be used.

	this.query = new OO.ui.TextInputWidget( {
		icon: 'search',
		placeholder: config.placeholder,
		value: config.value
	} );

Here's how OutlineControlsWidget mixes in IconedElement, with a default icon of 'add-item'. This can still be overridden by the caller through the config parameter.

OO.ui.OutlineControlsWidget = function OoUiOutlineControlsWidget( outline, config ) {
	// Configuration initialization
	config = $.extend( { icon: 'add-item' }, config );

	// Parent constructor
	OO.ui.OutlineControlsWidget.super.call( this, config );

	// Mixin constructors
	OO.ui.GroupElement.call( this, this.$( '<div>' ), config );
	OO.ui.IconedElement.call( this, this.$( '<div>' ), config );

	// (...)

	this.$element.append( this.$icon, this.$group, this.$movers );
};

OO.inheritClass( OO.ui.OutlineControlsWidget, OO.ui.Widget );
OO.mixinClass( OO.ui.OutlineControlsWidget, OO.ui.GroupElement );
OO.mixinClass( OO.ui.OutlineControlsWidget, OO.ui.IconedElement );

For what the CSS/LESS looks like, see the links in the "Code" section below.

Browser support

[edit]
  • OOjs UI supports both SVG and PNG, and has both SVG and PNG versions of all(*) icons
  • OOjs UI does not itself provide for a mechanism to detect whether SVG is supported and use SVG if so or PNG otherwise
  • VisualEditor used to feature-detect SVG support and dynamically choose whether to use the SVG or the PNG stylesheet from OOjs UI
  • Nowadays, VE is SVG-only because our other browser support requirements already exclude all non-SVG browsers anyway

(*) Or at least that's what we aim for, it's possible that we don't literally have every single icon supported in PNG at this precise moment

Code

[edit]

See also

[edit]