Flexible Icons API

The Flexible icons API is a new feature in Totara Learn 9. In previous releases core icons were served as individual graphic assets using the HTML image tag. There are a number of problems with that approach:

  • Changing the icon, or even just modifying styles such as style and colour, requires the theme developer to produce their own image resources
  • Images were of a fixed pixel size, meaning they did not look as crisp as they should on high-resolution displays
  • A page with lots of icons would make a large number of separate HTTP requests, impacting page load times

It is now more common to deliver icons via font sets such as Glyphicons and Font Awesome or more recently via SVG sprites. These approaches may require icon-specific classes or structural variation in their HTML for subgroups of icons. The Flexible icons API provides a way to accommodate this. The Pix Icon API is still the preferred method for outputting individual image-based graphic assets and existing non-icon uses should be unaffected.

In Totara Learn 9 we have included the Font Awesome icon font, and also added a custom font icon set named 'Font Totara' to provide some Totara specific icons to replace the image-based icons. Theme developers can make use of any icons from both sets, and optionally can choose to provide their own icon fonts if they want to modify specific icons or add their own.

For live previews and code examples see the Flex Icons page in the Element Library (Site Administration > Appearance > Themes > Element Library).

From Totara 13 the flexible icons are replaced by Icons

Backwards compatibility

The API is designed to be backwards compatible with the Pix icon API previously used to output icons. Existing calls to output pix icons (e.g. $OUTPUT->pix_icon('edit')) check whether a new Flexible icon is available for the pix component / path and if found, output the new HTML instead. If a font icon equivalent is not found they will fall back to displaying the original image. Therefore there should be no need to update any existing third party code unless you want to make use of font based icons.

Overriding icons

The Flexible icons API takes its cues from the Pix icon API where overriding and inheritance are concerned. Overrides for one or many core icons may be provided at the theme level and child themes inherit their parents' overrides. Additional icon definitions may also be provided within a theme's flex_icons.php file.

Key to how the API works are flex_icons.php manifest files which can be found in components' respective pix directory (see pix/flex_icons.php for an example using the core manifest). Among other things these files contain a data structure stored in an $icons variable which defines the template to be used for a given icon, context data which should be passed to the template for that icon and the ability to set defaults for all of these. Items in the $icons array are keyed by a unique identifier for the icon.

Note that after adding or updating flex_icons.php manifests you will need to purge the caches for changes to take effect.

To override a particular icon via a theme add a pix/flex_icons.php file and override the corresponding key in the $icons array. For example:

theme/mytheme/pix/flex_icons.php
$icons = array(
  'backpack' => array(
    'template' => 'mytheme/customicon',
    'data' => array(
      'classes' => 'my-overridden-class', 
      'mycustomdata' => 'Custom string for template',
    ),
  ),
);

In this instance we're assuming some CSS styles have been added in the theme for the .my-overridden-class selector and a new template added in the theme for the icon.

Outputting icons

The Flexible icons API provides methods to output Flexible icons using PHP, JavaScript and within Mustache templates via a template helper (lambda).

PHP

// Output the edit icon.
$OUTPUT->render(new core\output\flex_icon('edit'));

// This helper is a synonym for the above.
$OUTPUT->flex_icon('edit');

It is also possible to pass instance-specific custom template data using an optional second parameter which should be an array. The default Font Totara icons implementation allows additional classes to be passed to icon templates using 'classes' key:

$OUTPUT->flex_icon('edit', ['classes' => 'ft-state-danger ft-size-300']);

This will output an icon using the 'danger' state (colour as per Bootstrap framework's brand-danger variable) with a font-size of 20px as per the ft-size-300 class.

Creating a Flex icon for a template (9.20, 10.9, 11.3, evergreen-20180514 and later).

Using the following PHP function call will return the appropriate flex or pix icon (depending on your theme settings):

\core\output\flex_icon::get_icon($identifier, $component, $data);

Where :

  • $identifier is either a pix or flex identifier
  • $component is the component that the icon belongs to (defaults to 'core')
  • $data is an array containing any additional parameters (including 'alt' is highly recommended for accessibility)

Once the icon has been retrieved, the get_template and export_for_template methods can then be called to determine the template required and context for the appropriate template.

JavaScript

Rendering icons using JavaScript is an async operation and returns a jQuery promise (note that this differs slightly from the ES2015 implementation but has much wider browser support). This is as the first time an icon is rendered the template and some icon data must be retrieved from the server. This data is cached client-side so that subsequent rendering of a given icon should resolve almost immediately.

require(['jquery', 'core/templates'], function($, templates) {
  
  // templates.renderIcon returns a jQuery promise via $.Deferred().promise()
  templates.renderIcon('edit')
    .done(function(renderedIcon) {
      $('#my-ui-widget').html(renderedIcon);
    });
  
  // Add alt text (2nd paramter).
  // Note that alt text is a last resort for legacy code. Icons should
  // accompany descriptive text in the user interface.
  templates.renderIcon('edit', 'Edit program')
    .done(function(renderedIcon) {
      $('#my-ui-widget').html(renderedIcon);
    });

  // Add custom classes (3rd parameter).
  templates.renderIcon('edit', '', 'ft-state-danger ft-size-300')
    .done(function(renderedIcon) {
      $('#my-ui-widget').html(renderedIcon);
    });

  // Pass instance specfic custom template data (useful for bespoke icon implementations).
  templates.renderIcon('my-custom-icon', '', '', { 'custom_foo': 7, 'custom_bar': ['a', 'b', 'c'] })
    .done(function(renderedIcon) {
      $('#my-ui-widget').html(renderedIcon);
    });

  // Options object combining all the above into 2nd parameter.
  var options = {
    'classes': 'ft-state-danger ft-size-300',
    'alt': 'Some alt text here',
    'custom_foo': 7,
    'custom_bar': ['a' , 'b', 'c']
  };
  templates.renderIcon('my-custom-icon', options)
    .done(function(renderedIcon) {
      $('#my-ui-widget').html(renderedIcon);
    });

});


Creating a Flex icon for a template (9.20, 10.9, 11.3, evergreen-20180514 and later).

Using the following JavaScript function call will return the appropriate flex or pix icon (depending on your theme settings):

require(['core/flex_icon'], function(flex_icon) {
	flex_icon.getIconData(identifier, component, data).then(function(icon) {
		template = icon.template;
		context = icon.context;
	});
});

Where :

  • identifier is either a pix or flex identifier
  • component is the component that the icon belongs to (defaults to 'core')
  • data is an array containing any additional parameters (including 'alt' is highly recommended for accessibility)

Once the icon has been retrieved, the template and context variables can then be used in a mustache template to display the appropriate flex or pix icon (dependent on your theme). Note that it is an async operation and returns a jQuery promise which is likely to be resolved immediately (depending on whether the flex icon cache has been populated).

Mustache

Valid uses of the flex icon helper are as follows:

mytemplate.mustache
{{#flex_icon}}identifier{{/flex_icon}}
{{! Note the syntax below is supported in Learn 12+ only }}
{{#flex_icon}}identifier, alt_identifier, alt_component {{/flex_icon}}
{{#flex_icon}}identifier, alt_identifier, alt_component, classes{{/flex_icon}}

Where:

  • identifier is the flex icon identifier, e.g. move_up, t/class
  • alt_identifier is a lang string identifier for the alt text
  • alt_component is a lang string component for the alt text
  • classes is a  list of classes to add to the flex icon

These are not allowed to be supplied via the context variable.

Dynamic icons

In situations where an icon is generated dynamically, we recommend creating the following structure within your context variable:

{
...
	"icon": {
		"template": "core/flex_icon",   // Output from $icon->get_template() (PHP) or icon.template (JavaScript)
		"context": { ... }              // Output from $icon->export_for_template() (PHP) or icon.context (JavaScript)
	}
...
}

This allows the following code to display your icon in the mustache template (note that this could cause an issue if there is a "template" variable within context:

{{#icon}}{{#context}}{{> &&template }}{{/context}}{{/icon}}


Totara 9 - Totara 11 (before evergreen 20180514, deprecated in Totara 12)

The below used in a mustache template will also output the edit icon.

mytemplate.mustache
{{#flex_icon}}edit{{/flex_icon}}

As per the PHP example it is possible to pass instance specific data as a second parameter:

mytemplate.mustache
{{! Pass alt text for legacy icon support only. New icons should be accompanied by descriptive text in the UI }}
{{#flex_icon}}edit, {"alt": "Edit program"}{{/flex_icon}}

{{! Pass in custom classes }}
{{#flex_icon}}edit, {"classes": "ft-state-danger ft-size-300"}{{/flex_icon}}

{{! Pass instance specfic custom template data (useful for bespoke icon implementations). }}
{{#flex_icon}}edit, {"custom_foo": 7, "custom_bar": ["a", "b", "c"]}{{/flex_icon}}

When using this API, we strongly recommend that all context variables used within this helper are processed before being sent to the helper.

Legacy spacer icons

The legacy pix_icon approach provided a positioning workaround in the form of an empty / transparent icon used to equally space icons. Generally speaking we would prefer a pure CSS approach however there are times where this is not feasible due to the need to refactor logic to accommodate it.

// Output a standard size icon spacer.
$OUTPUT->flex_icon('spacer');

// Output a spacer for a 16px spacer. NOTE that the size classes
// correspond to the default icon sizings. Other values do not
// work and should not be added e.g. 'ft-spacer-15'.
$OUTPUT->flex_icon('spacer', array('classes' => 'ft-spacer-16'));

This outputs an empty span with the appropriate classes to correctly space by an icon width / height.