angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Usando iconos en su sitio web Flask y reduciendo 'First Contentful Paint'

Utilice un macro de iconos Jinja para poner iconos en sus páginas y utilice <symbol> para definir los iconos una vez y utilizarlos varias veces en la misma página.

29 mayo 2020
post main image
https://unsplash.com/@codingtim

Hay muchos tipos de iconos de vectores. En este post sólo miro los iconos SVG, y me limito a los iconos de navegación, a veces también llamados iconos de interfaz. Estos iconos no sólo se ven bien en los sitios web, sino que también tienen color y escala como fuentes. Y son muy funcionales. Imagina un botón con el texto "Editar" en él. Sustituye este texto por un icono de lápiz y obtendrás más espacio en la página, mientras que todavía está muy claro lo que sucederá cuando hagas clic en el botón.

Otro ejemplo es la página de contacto. En lugar de poner un número de teléfono, una dirección de correo electrónico y una dirección de visita como sólo texto, se pueden poner iconos delante de ellos y se hace más fácil de leer y usar.

Probablemente los iconos más conocidos son el icono de búsqueda y el icono de inicio de sesión/cuenta (persona). En la pantalla de tu teléfono móvil no hay tanto espacio como en un ordenador desktop . En muchos casos la barra de menú cambia en un teléfono y los elementos horizontales del menú se colocan en un menú desplegable que se puede activar haciendo clic en un icono de hamburguesa. Para mantener la búsqueda y las funciones de login / cuenta siempre accesibles, a menudo el cuadro de búsqueda se sustituye por un icono de búsqueda y los textos de login / cuenta se sustituyen por un icono de persona.

Formas de añadir iconos a su sitio web

La forma más fácil es añadir iconos como fuente. Descargas la fuente (no usamos un CDN por razones de privacidad), añades la familia de fuentes y listo. Cuando empecé este sitio web elegí Font Awesome. ¿Por qué? Busqué en Internet los iconos de bootstrap y hubo muchas coincidencias mencionando Font Awesome. Font Awesome realmente tiene un muy buen conjunto de iconos gratuitos. Añadirlos a tus páginas es fácil. Por ejemplo, para añadir un icono user se añade el código:

	<i class="fas fa-user"></i>

Otra forma de añadir iconos es en línea. En lugar de añadir código para seleccionar un icono de la fuente Font Awesome , añadimos el código real del icono SVG. Puede descargar el código del icono SVG en la página web de Font Awesome , el código del icono user:

	<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="user" class="svg-inline--fa fa-user  fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M224 256c70.7 0 128-57.3 128-128S294.7 0 224 0 96 57.3 96 128s57.3 128 128 128zm89.6 32h-16.7c-22.2 10.2-46.9 16-72.9 16s-50.6-5.8-72.9-16h-16.7C60.2 288 0 348.2 0 422.4V464c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48v-41.6c0-74.2-60.2-134.4-134.4-134.4z"></path></svg>

Hay más formas, pero con la mayoría de ellas se pierde la capacidad de colorear y escalar. Un buen resumen se presenta en el artículo "Usando SVG", ver enlaces abajo.

Iconos y tiempo de carga de la página

Cuando se construye un sitio web, uno de los aspectos más importantes es el tiempo que tarda una página en aparecer en la pantalla. Muchos buscadores han hecho del tiempo de carga de su sitio web un factor de posicionamiento. Chromium Developer Tools tiene una buena herramienta para medir este tiempo llamada 'Audits'. Este es el programa Lighthouse que le da calificaciones de Rendimiento, Accesibilidad, Mejores Prácticas y SEO. Para el rendimiento, "First Contentful Paint" es muy importante.

Lighthouse me mostró que cargar y renderizar los archivos Font Awesome fue responsable de casi un segundo! Si revisas el sitio web de Font Awesome verás que hay unos 1500 iconos gratuitos en la fuente. Muy bonito, pero sólo uso 25 de ellos.

Porque quería reducir el tiempo de carga consideré usar una fuente de iconos personalizada. Por ejemplo, Icomoon, ver los enlaces de abajo, te permite crear una fuente de iconos con sólo los iconos que usas. Muy bien, pero decidí optar por los iconos en línea porque esto elimina la carga de archivos adicionales. La desventaja es que el tamaño de la página aumenta. Una buena discusión sobre los aspectos de rendimiento se puede encontrar en el artículo 'Inline SVG vs Icon Fonts [CAGEMATCH]', ver los enlaces de abajo.

Usar una macro Jinja para poner iconos en la página

Pensando en los iconos, probablemente es una buena idea tener en cuenta que quieres cambiar los iconos mañana. O incluso quieres mantener varios conjuntos de iconos. Como prueba he seleccionado los nuevos iconos Bootstrap 5 como un segundo conjunto de iconos. Para soportar ambos conjuntos de iconos creé la siguiente macro Jinja :

{%- macro icon(icon_name, fill=false, collection=none, inline=false) -%}

	{%- if not collection %}
		{%- set collection = 'fontawesome' -%}
	{%- endif %}

	{%- if collection == 'fontawesome' -%}

		{%- if icon_name in ['angle_up', 'chevron_up'] -%}
				
			{%- if inline -%}
				<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="angle-up" class="svg-inline--fa fa-angle-up fa-w-10" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M177 159.7l136 136c9.4 9.4 9.4 24.6 0 33.9l-22.6 22.6c-9.4 9.4-24.6 9.4-33.9 0L160 255.9l-96.4 96.4c-9.4 9.4-24.6 9.4-33.9 0L7 329.7c-9.4-9.4-9.4-24.6 0-33.9l136-136c9.4-9.5 24.6-9.5 34-.1z"></path></svg>
			{%- else -%}
				<i class="fas fa-angle-up"></i>
			{%- endif -%}

		{%- elif icon_name in ['arrow_clockwise', 'redo'] -%}
		...

	{%- endif -%}


	{%- if collection == 'bootstrap5' -%}

		{%- if icon_name in ['angle_up', 'chevron_up'] -%}

			{%- if inline -%}
				<svg class="bi bi-chevron-up" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.646 4.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1-.708.708L8 5.707l-5.646 5.647a.5.5 0 0 1-.708-.708l6-6z"/></svg>			{%- else -%}
			{%- else -%}

			{%- endif -%}

		{%- elif icon_name in ['arrow_clockwise', 'redo'] -%}
		...

	{%- endif -%}

{%- endmacro -%}

Puedo usar este macro tanto con los iconos Font Awesome como con los Bootstrap 5. Además, puedo especificar si quiero el icono de tipo relleno, y si quiero el icono SVG en línea. Modificando la macro puedo también dar salida al icono Font Awesome y al icono Bootstrap 5 para la inspección visual. Usar esta macro es muy fácil, en tu página puedes usarla:

	{{ icon('home') }} Hoogerheid, NL

Evitando iconos idénticos en línea SVG-iconos

Una prueba rápida me mostró que los iconos en línea SVG de hecho redujeron 'First Contentful Paint' en casi un segundo. ¡Grandioso! Pero a veces un icono estaba en una página varias veces. Con un tamaño medio de 400 - 600 caracteres por cada icono SVG, esto puede tener un serio impacto en el tiempo de carga de la página. Un ejemplo es una sección de comentarios donde cada comentario tiene un icono de calendario y un icono @ y hay cientos de comentarios.

Afortunadamente hay una forma de definir un bloque de iconos en una página y utilizarlos más tarde. Hacemos esto usando las etiquetas <svg> y <símbolo>:

<svg class="d-none">
	<symbol id="icon-angle-up" viewBox="0 0 16 16">
		<title>angle-up</title>
		<path fill-rule="evenodd" d="M7.646 4.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1-.708.708L8 5.707l-5.646 5.647a.5.5 0 0 1-.708-.708l6-6z"/>
	</symbol>
	<symbol id="icon-arrow-clockwise" viewBox="0 0 16 16">
		<title>arrow-clockwise</title>
		<path fill-rule="evenodd" d="M3.17 6.706a5 5 0 0 1 7.103-3.16.5.5 0 1 0 .454-.892A6 6 0 1 0 13.455 5.5a.5.5 0 0 0-.91.417 5 5 0 1 1-9.375.789z"/><path fill-rule="evenodd" d="M8.147.146a.5.5 0 0 1 .707 0l2.5 2.5a.5.5 0 0 1 0 .708l-2.5 2.5a.5.5 0 1 1-.707-.708L10.293 3 8.147.854a.5.5 0 0 1 0-.708z"/>
	</symbol>
	...
</svg>

Más información en el artículo "SVG 'symbol' a Good Choice for Icons", ver enlaces abajo. Ponemos este bloque inmediatamente debajo de la etiqueta <body>. Obsérvese que sólo ponemos las rutas de un icono en la definición. El título es opcional. Para utilizar el icono, sólo tenemos que especificar su id:

	<svg class="svg-icon"><use xlink:href="#icon-calendar"></use>

También podemos envolverlo en un <span> con una clase, por ejemplo, para cambiar su tamaño.

Implementación

Hay dos cosas que necesitan ser editadas cuando se añade un nuevo icono:

  • el conjunto de iconos de símbolos
  • la macro de iconos

Además, quiero el código HTML que puedo poner en una página para una rápida inspección visual. Muy pronto me cansé de la edición y decidí escribir un guión que pueda ser usado para generar estos datos. También decidí ir por los iconos Bootstrap 5 solamente, me gustaron más, son MIT licenciados y todos tienen la misma viewBox. El guión fue escrito muy rápido, así que no me disparen:

import sys

class  SVGIcon():

    def __init__(self,
        icon_collection=None,
        icon_name=None,
        icon_id=None,
        svg_class=None,
        svg_fill=None,
        svg_view_box=None,
        svg_path=None,
        symbol_title=None
        ):

        self.icon_collection = icon_collection
        self.icon_name = icon_name
        self.icon_id = icon_id
        self.svg_class = svg_class
        self.svg_fill = svg_fill
        self.svg_view_box = svg_view_box
        self.svg_path = svg_path
        self.symbol_title = symbol_title


def doit():
    
    svg_icons = []

    icon_name2icon_details = {
        'angle_up': {
            'refs': ['angle_up', 'chevron_up'],
            'svg_path': '<path fill-rule="evenodd" d="M7.646 4.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1-.708.708L8 5.707l-5.646 5.647a.5.5 0 0 1-.708-.708l6-6z"/>',
        },
        'arrow_clockwise': {
            'refs': ['arrow_clockwise', 'redo', 'resend_mail'],
            'svg_path': '<path fill-rule="evenodd" d="M3.17 6.706a5 5 0 0 1 7.103-3.16.5.5 0 1 0 .454-.892A6 6 0 1 0 13.455 5.5a.5.5 0 0 0-.91.417 5 5 0 1 1-9.375.789z"/><path fill-rule="evenodd" d="M8.147.146a.5.5 0 0 1 .707 0l2.5 2.5a.5.5 0 0 1 0 .708l-2.5 2.5a.5.5 0 1 1-.707-.708L10.293 3 8.147.854a.5.5 0 0 1 0-.708z"/>',
        },
        ...
    }

    icon_collection = 'bootstrap5'
    svg_view_box = "0 0 16 16"
    svg_fill = "currentColor"

    for icon_name in icon_name2icon_details:
        icon_details = icon_name2icon_details[icon_name]
        svg_path = icon_details['svg_path']

        svg_icons.append(SVGIcon(
            icon_collection = icon_collection,
            icon_name = icon_name,
            icon_id = 'icon-'  +  icon_name.replace('_', '-'),
            svg_fill = svg_fill,
            svg_view_box = svg_view_box,
            svg_path = svg_path,
            symbol_title = icon_name.replace('_', '-'),
        ))

    # generate svg symbols list
     symbols_list_lines = []
     symbols_list_lines.append( '<svg class="d-none">' )

    for svg_icon in svg_icons:
         symbols_list_lines.append( "\t"  +  '<symbol id="'  +  svg_icon.icon_id  +  '" viewBox="'  +  svg_icon.svg_view_box  +  '">' )
         symbols_list_lines.append( "\t\t"  +  '<title>'  +  svg_icon.symbol_title  +  '</title>' )
         symbols_list_lines.append( "\t\t"  +  svg_icon.svg_path )
         symbols_list_lines.append( "\t"  +  '</symbol>' )

     symbols_list_lines.append( '</svg>' )
     symbols_list  = "\n".join(symbols_list_lines)

    # generate svg symbols preview
    preview_lines = []
    for svg_icon in svg_icons:
        preview_lines.append( 'abc'  +  ' '  +  '<span class="font-size: 1em;"><svg class="svg-icon">'  +  '<use xlink:href="#'  +  svg_icon.icon_id  +  '"></use></svg>'  +  '</span>'  +  ' '  +  svg_icon.icon_name )

     preview_list  = '<p>'  +  "</p>\n<p>".join(preview_lines)  +  '</p>'

    # generate icon macro code
    icon_macro_lines = []
    first = True        
    for icon_name in icon_name2icon_details:
        icon_details = icon_name2icon_details[icon_name]
        if 'refs' not in icon_details:
            print('refs missing for {}'.format(icon_name))
            sys.exit()
        refs = icon_details['refs']
        # get icon
        found = False
        for svg_icon in svg_icons:
            if svg_icon.icon_name == icon_name:
                found = True
                break
        if not found:
            print('cannot find icon, icon_name = {}'.format(icon_name))
            sys.exit()
                
        if_start = 'elif'
        if first:
            if_start = 'if'
            first = False

        # expand refs
        ref_items = []
        for ref in refs:
            ref_items.append( "'"  +  ref  +  "'" )
            ref_list = '['  +  ', '.join(ref_items)  +  ']'

        icon_macro_lines.append( "\t"  +  '{%- '  +  if_start  +  ' icon_name in '  +  ref_list  +  ' -%}' )
        icon_macro_lines.append( '<span class="font-size: 1em;"><svg class="svg-icon">'  +  '<use xlink:href="#'  +  svg_icon.icon_id  +  '"></use></svg>'  +  '</span>' )

    icon_macro_lines.append( "\t"  +  '{%- endif -%}' )
     icon_macro_list  = "\n".join(icon_macro_lines)

    print('symbols_list:')
    print('{}'.format(symbols_list))
    print('preview_list:')
    print('{}'.format(preview_list))
    print('icon_macro_list:')
    print('{}'.format(icon_macro_list))

Después sólo copio-pego las partes symbols_list, preview_list y icon_macro_list en la plantilla base, el archivo de previsualización y el archivo de macros.

Algunos problemas

En las tablas de mi lista, las columnas que se pueden ordenar tenían el icono de ordenación adjunto a la última palabra del nombre de la columna. Cuando una columna se hace más pequeña quiero que el icono se pegue a la última palabra. Esto ya no funciona. Aún no he encontrado una solución para esto, pero no lo he estudiado en detalle.

Otra cosa es el posicionamiento vertical de los iconos. Hay soluciones para esto, una se describe en "Align SVG Icons to Text and Say Goodbye to Font Icons", ver enlaces abajo. Después de empezar a utilizar los iconos Bootstrap 5 como se describe arriba, parecía que no era necesario hacer ningún posicionamiento, pero es posible que quieras mirar en él.

Resumen

Usando los iconos SVG-iconos realmente hace que una página web se vea y navegue mejor. Debido a que utilizo sólo 25 iconos decidí utilizar el método SVG-iconos para incluir los iconos en las páginas, lo que añade un extra de 11,5 KB. Comparado con la solución Font Awesome la 'First Contentful Paint' bajó casi un segundo! Y el uso de un macro icono Jinja () me da la flexibilidad de cambiar los iconos en un lugar en lugar de tener que actualizar todas las páginas. Por el momento todos los iconos se cargan en cada página. En el futuro puedo optimizar esto incluyendo sólo los iconos usados en una página.

Enlaces / créditos

Align SVG Icons to Text and Say Goodbye to Font Icons
https://blog.prototypr.io/align-svg-icons-to-text-and-say-goodbye-to-font-icons-d44b3d7b26b4

Blog Optimization: Replacing Font Awesome with SVG
https://www.wouterbulten.nl/blog/tech/blog-optimization-replacing-font-awesome-with-svg/

Flask | JINJA 2: render_template_string() with macro imported in context
https://stackoverflow.com/questions/61338841/flask-jinja-2-render-template-string-with-macro-imported-in-context

Icomoon
https://icomoon.io

Inline SVG vs Icon Fonts [CAGEMATCH]
https://css-tricks.com/icon-fonts-vs-svg/

Insert image after each list item
https://stackoverflow.com/questions/946403/insert-image-after-each-list-item

Is there a way to use SVG as content in a pseudo element :before or :after
https://stackoverflow.com/questions/19255296/is-there-a-way-to-use-svg-as-content-in-a-pseudo-element-before-or-after

Lighthouse | Tools for Web Developers | Google Developers
https://developers.google.com/web/tools/lighthouse

SVG 'symbol' a Good Choice for Icons
https://css-tricks.com/svg-symbol-good-choice-icons/

Using SVG
https://css-tricks.com/using-svg/

Deje un comentario

Comente de forma anónima o inicie sesión para comentar.

Comentarios

Deje una respuesta.

Responda de forma anónima o inicie sesión para responder.