loicpennamen/icons-bundle

Add a quick icon function to Twig templates. Compatible with font or SVG icons.

Installs: 8

Dependents: 0

Suggesters: 0

Security: 0

Type:symfony-bundle

v1.2.0 2024-08-10 16:43 UTC

This package is auto-updated.

Last update: 2024-11-10 17:06:12 UTC


README

🚀 Quickly insert webfont or SVG icons in Twig.
👍 Keep visual coherence of your icons throughout the app.

Why this bundle?

The problems

Whether you use font icons such as Font Awesome or Flaticon, or your own .svg icons... Integrating the source code of each icon in your Twig templates might be a hassle, and hard to maintain.

Furthermore, sometimes one developer chooses an icon for a context but another developer uses a different icon for the same context. For instance to represent a "Delete" action, developer 1 may choose a trash icon 🗑️, and developer 2 mays choose a cross icon ❌ thus leading to visual incoherence.

The solution

This bundle provides a Twig function called icon() to insert your icon with a clear view of the context.

{{ icon('delete') }}

Which generates this piece of HTML:

<i class="fi fi-rs-trash-bin"></i>

This bundle allows you to define one icon for one context. You can define a context as the string that defines "what is icon is used for". For instance user, dashboard, warning, etc... Contexts are defined in a single file under config/icons.yaml. This way, every time you need a user icon with {{ icon('delete') }}, the same visual will appear throughout the app.

Parameters

  • key (string): The context or arbitrary key identifying the icon.
  • template (string, optional): The template of a font icon, or the collection of the .svg icon.
  • classes (string, optional): Additional classes for the font icon tag, or the .svg wrapper.

Installation

Make sure Composer is installed globally, as explained in the installation chapter of the Composer documentation. Then, open a command console, enter your project directory and execute:

composer require loicpennamen/icons-bundle

For applications that don't use Symfony Flex, enable the bundle by adding it to the list of registered bundles in the config/bundles.php file of your project:

// config/bundles.php

return [
    // ...
    LoicPennamen\IconsBundle\LoicPennamenIconsBundle::class => ['all' => true],
];

How to use with icon fonts

Include the fonts

This bundle handles the back-end logic. You are free to include your icon fonts any way you like. There are various icon fonts available across the web, some very common options are:

With most solutions, you can either

  • Use a CDN
  • Download the font files and include them via CSS
  • Use NPM package manager.

Read the dedicated documentation for more information. In this documentation, we will be using Flaticons for our examples. Here are the docs for Flaticons.

In the following example, we used Flaticon's NPM package and the fonts are included in a Sass file:

 # Install the files
 npm i @flaticon/flaticon-uicons 
// app.scss
// With flaticons, each file defines a different style and shape: 
@import "~@flaticon/flaticon-uicons/css/regular/straight.css"; // regular straight 
@import "~@flaticon/flaticon-uicons/css/solid/rounded.css"; // solid rounded
@import "~@flaticon/flaticon-uicons/css/brands/all"; // Brand icons

Configure the contexts

Create the file config/packages/icons.yaml that will hold your configuration.

For visual coherence, you want to use the same icon for every context. As seen above, a context is the string that defines "what is icon is used for". For instance, a "delete" button should not display a trash can somewhere, a ban-sign elsewhere and a cross somewhere else... Contexts allow to define the icon to use via a unique string used throughout the app.

Here is an example on how to define contexts:

# config/packages/icons.yaml
icons:
  contexts:
    # Use any context key you want, but dots & spaces are forbidden!
    # context: "font key"
    delete: "trash" # We choose Flaticon "trash" icon for deletion everywhere
    cancel: "undo" # We choose Flaticon "undo" icon for cancellation everywhere
    danger: "triangle-warning" 
    error: "triangle-warning" # Different contexts can use the same icon
    menu: "burger-menu"

Context can be hierarchical for better organization

The hierarchy must use dots as separators in the icon() function. The hierarchy can be as deep as needed, but two levels are usually way enough. For example with the following configuration you could write icon('user.profile') or icon('alert.warning'):

# config/packages/icons.yaml
icons:
  contexts:
    user:
      profile: "user"
      dashboard: "stats"
      impersonate: "refer"
    alert:
      info: "info-cirle"
      warning: "exclamation-triangle"
      error: "exclamation-triangle"

Arbitrary method

The use of contexts is not mandatory. For instance, without configuring contexts you can still use the function icon('xxxx') as long as the key xxxx exists inside the font. This is called the arbitrary method. For instance this snippet of code:

{{ icon('kangaroo') }}

Generates this piece of HTML regardless whether or not the font has a key called kangaroo:

<i class="fi fi-rs-kangaroo"></i>

Configure the font template(s)

With default configuration, the function icon('user') generates this piece of HTML:

<i class="fi fi-rs-user"></i>

This only works if you included a very specific font from Flaticon, with straight corners and regular weight. For different icon collections or designs, you want to change the HTML template used.

You can configure several templates and select one in the icon() function. The first template in the list is used as default. The string [KEY] will be replaced by the context value (or arbitrary key) passed in the function. The string [CLASSES] will be replaced by the third parameter passed to the function.

# config/packages/icons.yaml
icons:
  templates:
    # @see https://www.npmjs.com/package/@flaticon/flaticon-uicons
    default: "<i class='fi fi-rs-[KEY] [CLASSES]'></i>" # Flaticons regular straight style
    solid: "<i class='fi fi-ss-[KEY] [CLASSES]'></i>" # Flaticons regular solid style

A configuration for Font Awesome could be:

# config/packages/icons.yaml
icons:
  templates:
    # @see https://docs.fontawesome.com/web/add-icons/how-to
    duotone: "<i class='fa-duotone fa-solid fa-[KEY] [CLASSES]'></i>" # Font-awesome duotone (by default as first of the list)
    solid: "<i class='fa-solid fa-[KEY] [CLASSES]'></i>" # Font-awesome solid
    sharp: "<i class='fa-sharp fa-solid fa-[KEY] [CLASSES]'></i>" # Font-awesome sharp
    #...

How to use SVG icons

Configuration

The icon() function can also display SVG files. Since they are handled back-end, you do not have to put them in the public folder. By default, the function looks for .svg files in the following folders:

/assets/svg/icons/<key>.svg
/public/svg/icons/<key>.svg
/public/build/svg/icons/<key>.svg

You can override this list with the svg_paths option:

# config/packages/icons.yaml
icons:
  svg_paths:
    - '/assets/custom-svg-icons/'
    - '/assets/brand-svg-icons/'

If a file exists in either of these directory, it is rendered with a wrapping span with a svg-icon class:

<span class="svg-icon"><!-- SVG file content --></span>

SVG files collections

You may also name the directories in order to create different collections. For instance you may have a collection of SVG icons divided in two versions: light and dark. If the icons in each collection (ie. folder) have the same file name, you can set the configuration as such:

# config/packages/icons.yaml
icons:
  svg_paths:
    light: '/assets/svg-icons/light/'
    dark: '/assets/svg-icons/dark/'

With this configuration, the second parameter of the icon() function will be the collection (ie. folder) in which the SVG file will be searched:

{# show a light SVG user icon, in the first directory of the list by default #}
{{ icon('user') }}
 
{# show a light SVG user icon #}
{{ icon('user', 'light') }} 

{# show a dark SVG user icon #}
{{ icon('user', 'dark') }}

Styling

You may want to add some extra CSS in your app, to ensure the correct display of your SVG icons. For instance:

.svg-icon svg{
  /* Contain the icon into font dimensions */
  width: 1rem;
  height: 1rem;
  /* Fix the icon alignment */
  position: relative;
  bottom: .1rem;
}

Customizing the SVG wrapper

If you want to change the HTML generated around the SVG content, override the svg_template option. It is mandatory that your template contains the string [SVG] wherever the file content has to be inserted.

icons:
  svg_template: '<span class="my-icon-class [CLASSES]">[SVG]</span>'

Methods priorities

Since the icon() function handles several methods to display an icon, here is their order of priority:

  1. Contextual: If the key passed as agument in icon() exists in the contexts list, the font icon of the context is displayed.
  2. SVG: If a file exists in any of the specified folders with key as filename (minus the .svg extension) it will be displayed.
  3. Arbitrary: If the previous steps fail, the icons font template will be displayed with the abitrary value of key, thus allowing to use any icon of the font collection without the use of contexts.

How to get a list of available icons?

If you work on some kind of content editor, you may need to create an icon selector.

There is no existing way to retrieve a list of every icon included in a font - ie listing all arbitrary icon keys. However the contextual and SVG icons can be listed via the getList() method inside the class LoicPennamen\IconsBundle\Services\IconsService.

It returns an array of items following this convention:

array(13) {
  [0] => array(3) {
    ["key"] => "cancel"
    ["type"] => "font"
    ["collection"] => NULL
  },
  [1] => array(3) {
    ["key"] => "test"
    ["type"] => "svg"
    ["collection"] => "light"
  }
}

The optional parameter $sortBy allows to sort the results by any of the items values. By default, items are sorted alphabetically by their key value. Here is an example on how to use it:

<?php
// src/Controller/IconController.php
namespace App\Controller;

// ...
use LoicPennamen\IconsBundle\Services\IconsService;

class IconController extends AbstractController
{
    // ... 
    public function chooseIcon(IconsService $iconsService): Response
    {
        $choices = $iconsService->getList();

        return $this->render('choose-icon.html.twig', [
            'choices' => $choices
        ]);
    }
}
{# /templates/choose-icon.html.twig #}

<b>Icons :</b>
<ul>
    {% for choice in choices %}
        <li>
            {{ icon(choice.key) }}
            {{ choice.key }}
        </li>
    {% endfor %}
</ul>

Roadmap

This bundle may be upgraded in the future, here are the upcoming changes:

  • Sanitize keys: throw error or unauthorized characters
  • Optimisation: Set a cache for SVG icons instead of reading every folders on each call to icon(). Add a console command for clearing the cache and a cache duration option.
  • Setup PHPUnit tests