5 cose che ho imparato leggendo il codice di Dawn

Dawn è il nuovo tema gratuito predefinito disponibile nei negozi Shopify di tutto il mondo. È stato annunciato durante Shopify Unite lo scorso Luglio e include tutte le nuove funzionalità dell'Online Store 2.0, tra le quali i metafield dinamici, i template JSON, compatibili con i blocchi e le sezioni in tutte le pagine, e le estensioni delle app. È un tema ultra veloce poiché sfrutta le funzionalità native del browser invece che librerie JavaScript e polyfill.

Dawn - il nuovo tema gratuito di Shopify

Come sviluppatori, possiamo imparare molto leggendo il codice di questo tema, poiché è stato scritto per combinare tutte le migliori pratiche per lo sviluppo front end nel 2021, con una particolare attenzione all'accessibilità, alle prestazioni e al codice snello e facile da mantenere. In quest'articolo, vi parlerò di 5 cose che ho imparato leggendo il codice di Dawn. Se volete avere il codice a portata di mano, andate sul repository di Dawn seguendo le istruzioni del nostro ultimo articolo.

1. Non sempre c'è bisogno di JavaScript

Per avere successo, un negozio online deve essere veloce. Un tema che garantisca un'esperienza di shopping veloce per il cliente ha un grande impatto sulle conversioni, sul ranking nei motori di ricerca e sulla customer retention. Negli ultimi anni, Shopify si è focalizzata molto sul tema dell'ottimizzazione della velocità dei negozi online, sviluppando il proprio strumento di misurazione della velocità online (visibile sotto il tema pubblicato in ogni negozio) e scrivendo molta documentazione e risorse su come ottimizzare i temi esistenti.

Ora che il Theme Store ufficiale è di nuovo aperto, Shopify ha anche introdotto standard molto elevati per i temi presentati, che per essere approvati devono ottenere un punteggio Lighthouse superiore a 60 nella home page, nella pagina del prodotto e nella pagina della collezione.

E ha creato il tema Dawn raccogliendo tutte le best practices per ottimizzare la velocità di caricamento delle pagine. Uno dei modi in cui lo fa è utilizzando JavaScript solo quando assolutamente necessario. Come descritto nella  documentazione del tema, "Dawn fa leva sulle funzionalità native dei browser più all'avanguardia, pur mantenendo il supporto per quelli più vecchi attraverso miglioramenti progressivi, non polyfill".

Un esempio tra i tanti riguarda il caricamento delle immagine tramite lazy loading, grazie al quale solo le immagini immediatamente visibili all'utente vengono caricate, mentre tutte le altre vengono mostrate solo quando l'utente ci si avvicina attraverso lo scrolling della pagina. In Dawn il lazy loading viene implementato tramite loading attribute aggiunto direttamente all'HTML dell'immagine, invece che con l'uso una libreria JavaScript esterna come lazysites, che appesantirebbe il codice e quindi rallenterebbe il negozio. Quindi se analizziamo il codice della sezione image-width-text, vediamo che l'attributo loading=lazy è impostato per ogni immagine direttamente nell'HTML. Il codice imposta anche gli attributi di larghezza e altezza per migliorare l'accessibilità e gli attributi srcset e size che consentiranno allo user agent di selezionare l'immagine appropriata in base alle preferenze dell'utente o alle condizioni di larghezza di banda.

<img 
srcset="{%- if section.settings.image.width >= 165 -%}
{{ section.settings.image | img_url: '165x' }} 165w,
{%- endif -%} {%- if section.settings.image.width >= 360 -%}
{{ section.settings.image | img_url: '360x' }} 360w,
{%- endif -%} {%- if section.settings.image.width >= 535 -%}
{{ section.settings.image | img_url: '535x' }} 535w,
{%- endif -%} {%- if section.settings.image.width >= 750 -%}
{{ section.settings.image | img_url: '750x' }} 750w,
{%- endif -%} {%- if section.settings.image.width >= 1070 -%}
{{ section.settings.image | img_url: '1070x' }} 1070w,
{%- endif -%} {%- if section.settings.image.width >= 1500 -%}
{{ section.settings.image | img_url: '1500x' }} 1500w,
{%- endif -%}"
src="
{{ section.settings.image | img_url: '1500x' }}"
sizes="(min-width: {{ settings.page_width }}px) {{ settings.page_width | minus: 100 | divided_by: 2 }}px, (min-width: 750px) calc((100vw - 130px) / 2), calc((100vw - 50px) / 2)"
alt="{{ section.settings.image.alt | escape }}"
loading="lazy"
width="{{ section.settings.image.width }}"
height="{{ section.settings.image.height }}" />

Un altro esempio per il quale JavaScript viene utilizzato quando solo strettamente necessario si può trovare nella galleria immagini della pagina prodotto. Il team di UX design di Shopify ha voluto mettere in evidenza le immagini prodotto in una maniera diversa dal classico slider con immagini thumbnail. Ha creato un layout a due colonne che può essere sfogliato in parallelo. Questo nuovo layout ha creato delle complessità al livello di codice, che sono state risolte con una soluzione in puro CSS tramite l'utilizzo delle propriety di scroll snap. JavaScript non è ovviamente rimosso del tutto dalla pagina prodotto, in quanto viene utilizzato nella galleria immagini tradizionale (che però è visibile solo nella versione mobile della pagina) e per le funzionalità essenziali come ad esempio mostrare il prezzo o l'immagine corretta quando si clicca su ciascuna variante.

Riflettendo su tutte le scelte di UX e sforzandosi di creare soluzioni che facciano leva sulle funzionalità native dei browser o sul puro CSS (come nel caso della galleria immagini prodotto), Shopify ha creato un tema che raggiunge 96 come punteggio Lighthouse. Come linea guida, Shopify suggerisce agli sviluppatori di mantenere la dimensione complessiva dei file JavaScript all'interno di un tema al di sotto dei 16KB.

2. Programmazione modulare

Dividere il codice in più moduli indipendenti lo rende più leggibile e gestibile. Una delle maggiori differenze tra Debut e Dawn è la divisione in moduli dei file .css e .js. Mentre in Debut, la cartella asset conteneva un singolo file theme.js e theme.css, possiamo vedere che in Dawn ci sono un file base.css, un file global.js e una serie di stylesheet e file JavaScript specifici per ciascun componente.

Credo che si tratti di un'organizzazione sia dei file che della logica direttamente collegata all'introduzione delle sezioni in tutti i template, le famose sections everywhere. In questo modo si possono importare in ciascuna sezione gli script e gli stylesheet necessari per quel componente specifico, senza influire sulla velocità della pagina o sul rendering della sezione.

Continuando a seguire l'esempio della pagina prodotto, possiamo dare un'occhiata alla struttura del file per capire come è stato implementato il codice modulare nel tema. Come sappiamo, per implementare le nuove sezioni dell'Online Store 2.0, abbiamo bisogno di utilizzare i template in formato .json anziché .liquid. Il template della pagina prodotto è ora un file .json che richiama diverse sezioni:


{
  "sections": {
    "main": {
      "type": "main-product",
      "blocks": {
        "vendor": {
          "type": "text",
          "settings": {
            "text_style": "uppercase",
            "text": "{{ product.vendor }}"
          }
        },
        "title": {
          "type": "title"
        },
        "subtitle": {
          "type": "text",
          "settings": {
            "text": "{{ product.metafields.descriptors.subtitle.value }}",
            "text_style": "subtitle"
          }
        },
        "price": {
          "type": "price"
        },
        "variant_picker": {
          "type": "variant_picker"
        },
        "quantity_selector": {
          "type": "quantity_selector"
        },
        "buy_buttons": {
          "type": "buy_buttons"
        },
        "description": {
          "type": "description"
        },
        "share": {
          "type": "share"
        }
      },
      "block_order": [
        "vendor",
        "title",
        "subtitle",
        "price",
        "variant_picker",
        "quantity_selector",
        "buy_buttons",
        "description",
        "share"
      ]
    },
    "product-recommendations": {
      "type": "product-recommendations"
    }
  },
  "order": [
    "main",
    "product-recommendations"
  ]
}

Il template include due sezioni, main-product e product-recommendations, che a loro volta includono dei blocchi.

Guardando nel dettaglio il file main-product, vediamo che carica gli stylesheet per i componenti/blocchi che lo compongono. Ci sono dei file CSS e dei file JavaScript. Gli stylesheet che sono stati creati per ciascun componente vengono caricati per primi, quindi vediamo quello per l'accordion, per il prezzo o per lo slider. Poi viene caricato un foglio di stile chiamato component-deferred-media.css, che utilizza l'attributo media print, poi modificato in all al caricamento. Questo viene fatto per alcuni stylesheet per migliorare la velocità di caricamento della pagina, dato che il CSS è una risorsa che blocca il rendering e quindi rallenta il caricamento della pagina. Una volta che la pagina è pronta, l'attributo onload trasformerà lo stylesheet da non funzionale a funzionale. Poiché questo "trucco" provoca un flash di contenuto senza stile, viene utilizzato solo per alcuni fogli di stile e combinato con alcuni attributi css essenziali, che sono quelli caricati in modo sincrono.


{% comment %}theme-check-disable TemplateLength{% endcomment %}
{{ 'section-main-product.css' | asset_url | stylesheet_tag }}
{{ 'component-accordion.css' | asset_url | stylesheet_tag }}
{{ 'component-price.css' | asset_url | stylesheet_tag }}
{{ 'component-rte.css' | asset_url | stylesheet_tag }}
{{ 'component-slider.css' | asset_url | stylesheet_tag }}
{{ 'component-rating.css' | asset_url | stylesheet_tag }}
{{ 'component-loading-overlay.css' | asset_url | stylesheet_tag }}

<link rel="stylesheet" href="{{ 'component-deferred-media.css' | asset_url }}" media="print" onload="this.media='all'">

<script src="{{ 'product-form.js' | asset_url }}" defer="defer">

Per quanto riguarda JavaScript, vediamo che c'è un singolo file product-form.js caricato all'inizio del file e poi, più in basso nella pagina, un file JavaScript che crea la logica per i 3D models dei prodotti, caricato solo se un 3D model è di fatto disponibile per quel prodotto. Tutta l'altra logica per la pagina prodotto, come ad esempio quella per visualizzare l'immagine corretta per ogni variante, è nel file global.js che viene caricato nel file theme.liquid e quindi disponibile in tutto il tema.

Come in Debut, il codice JavaScript può anche essere inserito nel file stesso della sezione ed è questo il caso, ad esempio, della sezione recommended-products. Viene inserito direttamente nella sezione poiché risponde a una chiamata API al Product Recommendations API e può essere rendered solo quando l'oggetto Liquid "recommendations" è disponibile. Questa può essere visualizzata solo dopo che l'oggetto Liquid Recommendations è stato popolato come risposta alla chiamata API all'API Product Recommendations.

3. Pensare al design con i dati alla mano

Il team di UX design che ha concepito Dawn ha scritto un ottimo articolo sul processo di design di Dawn e su quali problemi sta cercando di risolvere. Descrive il processo in cui il team di designer ha lavorato a stretto contatto con gli sviluppatori per creare un tema che enfatizzasse le prestazioni ma creasse anche un'esperienza UX ottimale sia per merchant che per i consumatori finali. Due delle scelte che mi toccano come sviluppatrice sono quelle relative ai font e ai colori del tema.

Font di sistema e web font

L'attenzione al miglioramento delle prestazioni e della velocità del tema non ha penalizzato l'esperienza del merchant nel personalizzare il suo negozio.

Il team di UX design di Shopify ha fatto la scelta consapevole di rendere disponibili nell'editor del tema sia i font di sistema che i web font. I caratteri di sistema sono ancora la scelta predefinita e consigliata, in quanto sono i caratteri più veloci da caricare poiché sono già disponibili su tutti i sistemi operativi. Ma i merchant hanno la libertà di scegliere scegliere di utilizzare invece web font (con il limite di 2 per tema) per ottenere l'aspetto desiderato per il loro negozio. Anche il selettore di caratteri nell'editor del tema è stato aggiornato con informazioni sui costi di prestazione della selezione di caratteri non di sistema, in modo che il merchant abbia un'idea chiara dell'impatto del font selezionato sulle prestazioni del tema.

Theme editor font selector

Atomic design system, applicato ai colori

Analogamente ai componenti di codice in Dawn, l'approccio atomico è stato applicato ai colori del tema. Una delle criticità per i merchant nel corso degli anni è stato trovare una combinazione di colori per i loro temi che funzionasse, sia per l'estetica e l'identità del marchio, sia per soddisfare gli standard di accessibilità.

Dopo aver svolto un audit dei temi esistenti sul Theme Store, il team UX di Shopify ha stabilito che 7 è il numero perfetto di colori da utilizzare in un tema Shopify. Ci sono tre colori primari e 4 colori secondari. I principali sono il colore di sfondo, il colore del testo, il colore dell'etichetta del pulsante a tinta unita e il colore d'accento.

Quindi hanno aggiunto un secondo colore d'accento e un altro colore di sfondo per fornire flessibilità ai merchant, nonché un settimo colore per i link testo, che è lì per consentire ai link di risaltare sullo sfondo, restando comunque accessibili.

Atomic design system

Perché questo è utile a me come sviluppatore? È interessante vedere come l'approccio atomico è stato applicato alle decisioni di progettazione e come Shopify ha creato un tema audace che si sforza di combinare prestazioni e flessibilità. Leggere le motivazioni alla base delle loro scelte mi ha anche fatto riflettere sul ruolo dei sistemi di design nella costruzione dei temi e delle best practices per mantenere gli stili unificati in tutto il tema.

4. L'importanza dell'accessibilità

I siti web, e più in particolare i siti di e-commerce, devono essere accessibili a tutte le persone. Le persone con disabilità, infatti, rappresentano il 15% della popolazione mondiale e devono poter navigare all'interno del negozio, trovare informazioni sui prodotti e completare con successo gli acquisti. L'accessibilità è un argomento molto ampio e Shopify ha sempre attribuito una forte importanza a questo aspetto dello sviluppo del tema. Sono stati scritti molti articoli sulle best practice per la creazione di temi Shopify accessibili.

Per quanto riguarda Dawn, continua la tradizione dei temi completamente accessibili creati da Shopify. Alcuni esempi di best practices presenti nel tema sono:

  • A tutti gli elementi è stato assegnato un ARIA role appropriato, quindi i bottoni hanno il role="button" e le liste di elementi hanno il role="list". Ho anche visto l'utilizzo del role="combobox" per la prima volta, nel caso della ricerca predittiva nella sezione main-search o dell'input di ricerca nella sezione header.
  • Tutti i link hanno un attributo "name", come ad esempio i bottoni "previous" e "next" nello slider visibile su mobile nella sezione multicolomn.liquid
    
    <button type="button" class="slider-button slider-button--prev" name="previous" aria-label="{{ 'accessibility.previous_slide' | t }}">{% render 'icon-caret' %}
    
  • Gli elementi HTML creati tramite il Custom Element API hanno anche loro gli attributi di accessibilità, generati all'interno della classe JavaScript. Quindi, ad esempio, nel caso del pulsante di condivisione, elemento utilizzato nella sezione main-article gli attributi di accessibilità sono impostati nella funzione addAccessibilityAttributes.
    if (!customElements.get('share-button')) {
      customElements.define('share-button', class ShareButton extends DetailsDisclosure {
        constructor() {
          super();
    
          this.elements = {
            shareButton: this.querySelector('button'),
            shareSummary: this.querySelector('summary'),
            closeButton: this.querySelector('.share-button__close'),
            successMessage: this.querySelector('[id^="ShareMessage"]'),
            urlInput: this.querySelector('input')
          }
    
        addAccessibilityAttributes() {
          this.elements.shareSummary.setAttribute('role', 'button');
          this.elements.shareSummary.setAttribute('aria-expanded', 'false');
          this.elements.shareSummary.setAttribute('aria-controls', this.elements.shareSummary.nextElementSibling.id);
        }
      });
    }
    
    
                

5. Il potere dell'open source

Ho lasciato il meglio alla fine. Dawn è un tema open source, il che significa che il suo codice è accessibile pubblicamente su Github. Ciò significa anche che tutti possono contribuire a migliorare il codice, possono segnalare problemi o suggerire nuove funzionalità da implementare, ovviamente con l'approvazione dei manutentori del repository.

Come sviluppatori, possiamo imparare non solo leggendo il codice del tema caricato nel repository Github, ma anche dalle issue aperte da chi contribuisce al repository e dalle interazioni con i manutentori. Ad esempio, durante la ricerca per questo articolo, ho digitato "product recommendations" nelle issue e ho imparato qualcosa in più su intersectionObserver e del motivo per cui viene utilizzato in questa sezione.

Ho trovato interessante anche dare un'occhiata alla sezione "Projects" del repository, che ha una roadmap per le funzionalità in via di sviluppo.

Come mai Shopify ha deciso di rendere Dawn un tema open source? Perché l'open source aiuta a diffondere le best practices e a favorire l'adozione di un Open Standard, creando un circolo virtuoso per il quale vengono sviluppati temi di qualità superiore e messi a disposizione dei merchant, che a loro volta decidono di utilizzare Shopify come piattaforma di e-commerce.


Al momento sullo Store per temi di Shopify sono disponibili 40 temi compatibili con le funzionalità dell'Online Store 2.0. Sono tutti temi premium sviluppati secondo gli standard di qualità delineati dall'azienda nella loro documentazione, e sono stati sviluppati così velocemente sia grazie alla divulgazione di Shopify delle loro best practices sia alla presenza di Dawn come tema esempio da seguire. Non vediamo l'ora di provare questi nuovi temi!

Foto di Rossella Ferrandino

Note sull'autore

Rossella Ferrandino

Rossella è la Front End Developer di Nama, e si occupa di tradurre i requisiti strategici dei clienti in codice. Con un background in eventi e marketing a Londra, Rossella è diventata developer sul campo, prima come autodidatta e successivamente con diverse esperienze nel settore tech. È volontaria per Women Who Code, community dedicata all'empowerment delle donne nel tech. Il suo Pokémon preferito è Snorlax.