Awesemo for Craft CMS

Usage

Twig

One function, one filter, and they are the same thing:

{{ awesemo('user') }}                      {# fa-solid fa-user — the site default style #}
{{ 'user'|awesemo }}                       {# identical #}

Naming an icon

Every spelling of every version is understood, and you never have to think about which one the site is on:

{{ awesemo('user') }}                      {# bare name, default style #}
{{ awesemo('regular:user') }}              {# style:name #}
{{ awesemo('fa-regular fa-user') }}        {# v6 classes #}
{{ awesemo('far fa-user') }}               {# v5 classes #}
{{ awesemo('fa fa-user') }}                {# v4 classes #}
{{ awesemo('brands:github') }}             {# brands #}
{{ awesemo('sharp-solid:user') }}          {# family and style #}
{{ awesemo('custom:acme-logo') }}          {# your own SVG #}

Retired v4 and v5 names resolve through Font Awesome's own shim table, so {{ awesemo('fa fa-thumbs-o-up') }} renders fa-regular fa-thumbs-up instead of a blank square. Nearly 400 of them work on a stock free install.

Options

{{ awesemo('star', { size: '2x', fixedWidth: true }) }}
{{ awesemo('spinner', { animation: 'spin' }) }}
{{ awesemo('trash', { label: 'Delete this entry' }) }}
{{ awesemo('star', { mode: 'svg' }) }}     {# this one inline, whatever the site default is #}
{{ awesemo('check', { color: '#0a0', class: 'ml-2', 'data-role': 'done' }) }}

mode · size · fixedWidth · listItem · rotate · flip · animation · spinReverse · border · pull · swapOpacity · inverse · class · label · title · id · color · tag. Anything else you pass becomes an HTML attribute.

A few spellings are aliased to whichever one you reached for first: classesclass, ariaLabel and aria-labellabel, fw and fixed-widthfixedWidth.

Accessibility is the default

Every icon comes out aria-hidden="true" unless you give it a label, at which point it becomes role="img" with that label and stops being hidden.

{{ awesemo('trash') }}                              {# beside the word "Delete" #}
{{ awesemo('trash', { label: 'Delete this entry' }) }}   {# on its own in a button #}

That is the right way round: nearly every icon in a CMS sits next to text that already says what it means, and a screen reader announcing "trash" after "Delete" is noise. title is a tooltip and is not an accessible name — use label when the icon is the only thing there.

The rest of the API

{{ craft.awesemo.icon('user') }}           {# an Icon object, or null #}
{{ craft.awesemo.exists('user') }}
{{ craft.awesemo.render('user', { size: 'lg' }) }}
{{ craft.awesemo.search('coffee', { style: 'solid' }, 24) }}
{{ craft.awesemo.styles() }}
{{ craft.awesemo.categories() }}
{{ craft.awesemo.head() }}                 {# the <link>, for manual delivery #}
{{ craft.awesemo.css() }}                  {# the stylesheet URLs #}
{{ craft.awesemo.version() }}              {# the version actually live #}
{{ craft.awesemo.source() }}
{{ craft.awesemo.spriteUrl() }}
{{ craft.awesemo.unicode('user') }}        {# the code point, for CSS content: #}

Fields

Icon holds one icon. Icons holds an ordered list of them. Both open the same picker as the control panel, so an author sees the same thing everywhere.

{{ entry.myIconField }}                            {# an Icon renders itself #}
{{ entry.myIconField.render({ size: 'lg' }) }}
{{ entry.myIconField.name }}                       {# 'user' #}
{{ entry.myIconField.style }}                      {# 'solid' #}

{% for icon in entry.myIconsField %}
  {{ icon.render({ fixedWidth: true }) }} {{ icon.label }}
{% endfor %}

An empty Icon field is empty in the Craft sense — {% if entry.myIconField %} does what you expect.

Restrict a field to particular styles, give it a default, cap the number of icons, and optionally let authors set size and fixed width per value. See Configuration.

Rich text

CKEditor and Redactor each get a toolbar button that opens the picker and inserts plain markup:

<i class="fa-solid fa-user" data-awesemo="solid:user" aria-hidden="true"></i>

No shortcode, no reference tag, nothing needed at render time. An icon written this way survives the plugin being removed, and moves between editors untouched.

Two things are worth knowing:

  • Redactor gets a <span>, not an <i>. Redactor rewrites <i> to <em> on the way out and the rename costs the attributes. Font Awesome renders from the classes and does not care which element carries them.
  • CKEditor's button is disabled when the selection is a block widget — an image, a table. That is correct behaviour and looks exactly like a broken button. Put the caret in a paragraph.

Upgrading what is already written

{{ entry.body|awesemoIcons }}

That rewrites Font Awesome markup already in the content to whatever the site's render mode is now — including SVG. It is what stops "switch the site to SVG mode" from orphaning everything authors have written so far, and it handles <i>, <span> and <em>.

In web-font mode the filter is close to a no-op, so leaving it on a field costs nothing and means switching mode later is a settings change rather than a migration.

Custom icons

Upload an SVG at Awesemo → Custom icons, give it a name, and use it exactly like a Font Awesome one:

{{ awesemo('custom:acme-logo') }}
{{ awesemo('custom:acme-logo', { size: '2x', label: 'Acme' }) }}

Uploads are sanitised on the way in — scripts, external references and event handlers removed — and rendered inline, so they inherit color and font-size like every other icon. Custom icons appear in the picker alongside everything else, and turning them off in settings hides the screen.

The sprite

Sprite mode renders <use href="/awesemo/sprite.svg#solid-user">. The sprite is built from the icons this site actually uses, which the usage scan finds:

php craft awesemo/icons/usage    # scan templates and element content
php craft awesemo/icons/sprite   # build the sprite from that scan

Run them in that order, and run them in your deploy. An icon that renders before it is in the sprite is drawn from the path data instead, so a stale sprite degrades rather than breaks.

The console

php craft awesemo/catalog/build          # download the icon list for the configured source
php craft awesemo/catalog/info           # what is installed, and where it came from
php craft awesemo/catalog/search coffee  # look a name up the way a template would
php craft awesemo/catalog/clear          # throw the built artifacts away
php craft awesemo/icons/usage            # every icon this site references, and the dead ones
php craft awesemo/icons/sprite           # build the subset sprite from that scan
php craft awesemo/health/check           # non-zero on an error; --strict for warnings too

A deploy that ends in:

php craft awesemo/catalog/build
php craft awesemo/icons/usage
php craft awesemo/icons/sprite
php craft awesemo/health/check

fails loudly instead of shipping a page of blank squares.

What it writes

One database table, for uploaded icons. Everything else is derived data in storage/awesemo/:

FileWhatRead by
runtime-<key>.jsonname → styles, aliases, shimsevery render
catalog-<key>.jsonlabels, search terms, categoriesthe control panel
paths-<key>.jsonSVG path dataonly on a subset miss
subset-<key>.jsonthe icons this site has actually drawnevery SVG render
sprite-<key>.svgthe generated spritethe browser
usage.jsonthe last usage scanthe sprite builder, the health screen

On a stock Font Awesome 6 Free install that is a 57 KB file on the hot path and a 1.4 MB one a normal request never opens. A page rendering twelve icons reads twelve icons' worth of JSON, not two thousand.

<key> hashes the source configuration, so changing version or kit builds a new catalogue beside the old one — and changing back is instant.

Next