Salta al contenuto principale

Creare un tema Wordpress da zero con Bootstrap 5

Profile picture for user luca77king

Nel 2016 scrissi questo tutorial in un mio sito che ormai non esiste più. Ma ho conservato il testo dell'articolo su Google Drive e, ritrovandolo, mi sono deciso di pubblicarlo nuovamente. Aggiornandolo per la versione 5 di Bootstrap.

Ebbene si, torniamo a parlare di come creare un tema per WordPress da zero, utilizzando lo starter theme Underscores e il framework Bootstrap.

Per questo complesso argomento ho già caricato su YouTube una serie di video che spiegano dettagliatamente i procedimenti (sebbene relativi a Bootstrap 4) ma, ma mi sono reso conto che poteva essere utile anche una versione testuale del tutorial.

Cominciamo! Il primo passaggio consiste nello scaricare lo starter theme underscores e scompattarlo all'interno della cartella wp-content/themes della vostra installazione di WordPres. Underscores è un tema veramente basico, sviluppato da Automatic, la società che gestisce Wordpress.

Io ho scelto come tema del tema "Friday Wp" che produrrà una serie di files del template engine di Wordpress, tutti contenuti all'interno di una cartella chiamata "friday-wp"

Image
Starter Theme

Se state lavorando su ambiente Linux, occorrerà dare i permessi al web server in modo che Wordpress possa gestire i file e le cartelle del nuovo tema. Supponendo che abbiamo seguito il precedente tutorial su come installare Wordpress su Ubuntu, la nostra installazione di Wordpress si troverà in /var/www/html/wordpress  

Diamo per scontato anche che abbiamo seguito il tutorial su come installare Apache, MySql e Php su Ubuntu e che quindi abbiamo già l'utente corrente nel gruppo www-data 

Dopo aver dato per scontato tutte queste cose, aprite il terminale e digitate la seguente riga di comando.

sudo chown -R $USER:www-data /var/www/html/wordpress/wp-content/themes/friday-wp

Dobbiamo essere in grado di poter editare i files, per cui abbiamo bisogno dei permessi di lettura, scrittura ed esecuzione.

sudo chmod -R 775 /var/www/html/wordpress/wp-content/themes/friday-wp

Ora entriamo nel backoffice di Wordpress, rechiamoci all'interno della sezione Aspetto -> temi, dove il nostro nuovo tema dovrebbe essere disponibile. Attiviamolo e andiamo a vedere il frontend, dove noteremo subito come questo tema sia veramente povero di stile e struttura HTML.

Apriamo il file style.css del tema e cancelliamo tutte le regole che sono state inserite subito dopo la fine dei commenti iniziali, che invece vanno mantenuti.

/*!
Theme Name: Friday WP
Theme URI: http://underscores.me/
Author: Underscores.me
Author URI: http://underscores.me/
Description: Description
Version: 1.0.0
Tested up to: 5.4
Requires PHP: 5.6
License: GNU General Public License v2 or later
License URI: LICENSE
Text Domain: friday-wp
Tags: custom-background, custom-logo, custom-menu, featured-images, threaded-comments, translation-ready

This theme, like WordPress, is licensed under the GPL.
Use it to make something cool, have fun, and share what you've learned.

Friday WP is based on Underscores https://underscores.me/, (C) 2012-2020 Automattic, Inc.
Underscores is distributed under the terms of the GNU GPL v2 or later.

Normalizing styles have been helped along thanks to the fine work of
Nicolas Gallagher and Jonathan Neal https://necolas.github.io/normalize.css/
*/

Installazione di Bootstrap 5

Adesso dobbiamo scaricare il framework bootstrap, uno strumento che semplifica di molto la creazione dello stile e che dispone di un gran numero di elementi HTML stilizzati, facilmente utilizzabili nelle varie componenti di un sito web.

Abbiamo diversi modi per installare Bootstrap: attraverso composer, npm o semplicemente scaricando il bundle. Optiamo per quest'ultima opzione, scaricando il pacchetto Compiled CSS and JS che non include la documentazione, i file sorgenti o qualsiasi dipendenza opzionale di JavaScript, come Popper.

Scarichiamo il pacchetto generato e salviamolo all'interno di una cartella del nostro tema che chiameremo "lib".

Struttura tema

Ripetiamo le operazioni per settare proprietario e permessi, come visto in precedenza.

È venuto il momento di includere il framework all'interno del nostro tema, per cui apriamo il file functions.php e andiamo a cercare la funzione che carica gli stili e gli script.

Prima di andare avanti, spieghiamo un attimo come funziona un tema di Wordpress. Essenzialmente, la cosa più importante da sapere è che il codice viene reso disponibile attraverso dei “ganci”, hooks in inglese. Che cosa s’intende con ganci? Praticamente sono delle porzioni di codice, inserite in punti strategici del tema, come header e footer, dove possiamo iniettare delle funzioni personalizzate, attraverso due metodi di WP, add_action e add_filter. 

L'hook su cui dobbiamo inserirci è wp_enqueue_scripts. Cerchiamolo nel file functions.php e notiamo che a questo hook è associata la funzione nome_tema_scripts

add_action( 'wp_enqueue_scripts', 'friday_wp_scripts' );

Quindi dobbiamo modificare la funzione friday_wp_scripts, caricando gli assets di Bootrasp.

/**
 * Enqueue scripts and styles.
 */
function friday_wp_scripts() {
    // Carico Bootstrap CSS
    wp_enqueue_style('bootstrap-style', get_template_directory_uri() . '/lib/bootstrap-5.0.2-dist/css/bootstrap.css');
    wp_enqueue_style( 'friday-wp-style', get_stylesheet_uri(), array(), _S_VERSION );
    wp_style_add_data( 'friday-wp-style', 'rtl', 'replace' );
    // Carico Bootstrap JS
    wp_enqueue_script( 'boostrap-bundle', get_template_directory_uri() . '/lib/bootstrap-5.0.2-dist/js/bootstrap.bundle.js', array(), _S_VERSION, true );
    wp_enqueue_script( 'friday-wp-navigation', get_template_directory_uri() . '/js/navigation.js', array(), _S_VERSION, true );
   if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
       wp_enqueue_script( 'comment-reply' );
   }
}

Bootstrap 5 non dipende più da jQuery, ma questa immortale libreria è ancora abbondantemente usata. E soprattutto è ancora parte di WordPress. Quindi faremo un'operazione per collocare jQuery in fondo alla pagina, dando così la priorità ad altri elementi.

Nella funzione che abbiamo appena modificato, inseriamo un blocco condizionale per escludere l'hack dal pannello di amministrazione. In questo blocco andiamo a eliminare jQuery per includerlo subito dopo, ma specificando che vogliamo inserirlo nel footer, dopo i contenuti.

/**
 * Enqueue scripts and styles.
 */
function friday_wp_scripts() {
    // Carico Bootstrap CSS
    wp_enqueue_style('bootstrap-style', get_template_directory_uri() . '/lib/bootstrap-5.0.2-dist/css/bootstrap.css');
    wp_enqueue_style( 'friday-wp-style', get_stylesheet_uri(), array(), _S_VERSION );
    wp_style_add_data( 'friday-wp-style', 'rtl', 'replace' );
    // Carico Bootstrap JS
    wp_enqueue_script( 'boostrap-bundle', get_template_directory_uri() . '/lib/bootstrap-5.0.2-dist/js/bootstrap.bundle.js', array(), _S_VERSION, true );
    wp_enqueue_script( 'friday-wp-navigation', get_template_directory_uri() . '/js/navigation.js', array(), _S_VERSION, true );
    // jQuery in Footer
    if(!is_admin()) {
        wp_deregister_script('jquery');
        wp_register_script( 'jquery', includes_url('/js/jquery/jquery.js'), false, false, true );
        wp_enqueue_script('jquery');
    }
    if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
      wp_enqueue_script( 'comment-reply' );
    }
}

Che cosa abbiamo fatto con la funzione wp_deregister_script()? Specificando nel primo parametro, l'identificativo dello script (handle) abbiamo detto a Wordpress di eliminare jQuery.

Nella riga successiva, con la funzione wp_register_script(), andiamo a includere nuovamente jQuery, ma specificando nell’ultimo parametro un valore booleano true, che comunica a WordPress la nostra volontà di inserire jQuery nel footer.

A questo punto si potrebbero rappresentare dei problemi con dei plugin che utilizzano la libreria. Bisognerebbe intervenire sul codice sorgente di questi, per far sì che gli script dipendenti da jQuery siano caricati anch'essi nel footer. 

Utilizzare la griglia di Bootstrap per il layout di Wordpress

Entriamo nel vivo del tutorial costruendo il layout del tema. Che cosa intendiamo per layout? Molto semplicemente é la disposizione degli elementi principali di un'interfaccia, come appunto un sito web.
 
Questa operazione, applicata al tema di Wordpress, sarà molto rapida perché possiamo sfruttare le classi CSS di Bootstrap, ottimizzate per qualsiasi tipo di risoluzione. 

Il sistema a griglia di Bootstrap 5 si basa su flexbox, un potente strumento di layout che permette agli elementi di flettersi e scorrere all'interno di un contenitore. Il sistema a griglia di Bootrstap ha 12 colonne, che possono essere divise in tre parti disuguali o in quattro parti uguali. Le colonne vengono dimensionate automaticamente per adattarsi al contenuto e possono essere riordinate e annidate l'una nell'altra.

Il sistema a griglia flexbox è reattivo, ovvero si adatta alle dimensioni dello schermo. Inoltre è mobile-first, cioè si adatta perfettamente ai dispositivi mobili. Infine, il sistema a griglia è facile da usare e rappresenta un'ottima scelta per i web designer di qualsiasi livello di esperienza.

Per questo tutorial ho deciso di assegnare 8 colonne al contenuto principale e 4 alla sidebar.

Griglia Bootstrap

Prima di addentrarci nell'implementazione della griglia dobbiamo creare un div contenitore che racchiuda tutti gli altri elementi della pagina. Apriamo il file header.php e subito dopo il tag di apertura del body inseriamo questa semplice stringa.

<div class="container">

Andiamo a chiudere il div aprendo il file footer.php e inserendo il tag di chiusura subito prima della chiusura del body.

<!-- Chiudo il container -->
</div>
</body>
</html>

Con questa operazione, abbiamo delimitato il contenuto del tema entro limiti ben precisi stabiliti dal framework ma, in caso di bisogno, possiamo fare un override nel file style.css con dimensioni custom.

Adesso cominciamo a implementare la griglia. Cominciamo dalla pagina principale di Wordpress, aprendo il file index.php

Inseriamo questa riga subito dopo la funzione get_header()

<div class="row">

Adesso prestate molta attenzione, perché il div va chiuso dopo la funzione che richiama la sidebar, get sidebar(). Procedete nel seguente modo, cercate:

<?php get_sidebar();
get_footer();

Sostituitelo con 

<?php get_sidebar(); ?>
</div>
<?php
get_footer();

Ora cercate:

<main id="primary" class="site-main">

Sostituite la riga con questo codice

<main id="primary" class="col-md-8 site-main">

Questa operazione va ripetuta nella nella stessa identica maniera per i seguenti file: 

  • single.php
  • search.php
  • page.php 

Prima di modificare il file page.php, fatene una copia e salvatelo con un nuovo nome, ad esempio page-full.php. Vi spiegherò in seguito il motivo di questa azione.

Ora apriamo il file sidebar.php e cerchiamo il seguente elemento 

<aside id="secondary" class="widget-area">
	<?php dynamic_sidebar( 'sidebar-1' ); ?>
</aside><!-- #secondary -->

Sostituiamolo con

<aside id="secondary" class="col-md-4 widget-area">
	<?php dynamic_sidebar( 'sidebar-1' ); ?>
</aside><!-- #secondary -->

Ora la griglia è perfettamente funzionante, dobbiamo però spiegare un piccolo concetto. Il selettore col-md-* identifica i normali desktop, i computer da scrivania per capirci. Se apriamo il foglio di stile di Bootstrap, ci accorgeremo che md (middle dekstop) è pari a 990 pixel. Quindi, quando si visualizza il tema con una risoluzione inferiore a questo valore, la sidebar collassa e scivola al di sotto del contenuto principale. 

Se volessimo che l'effetto responsive avvenga con una risoluzione inferiore, possiamo sostituire il selettore col-md-* con col-sm-* che identifica i small device. 

Possiamo anche combinare i due selettori, cambiando il valore delle colonne. Ad esempio, combiniamo col-md-8 con col-sm-6, ricordandoci che dobbiamo stare dentro 12 colonne, otterremo due effetti diversi a seconda delle risoluzioni utilizzate. 

Non vi preoccupate se non avete capito un granché di quest'ultimo concetto, perché basterà guardare il video di seguito per avere un’idea più chiara.

Modificare header e navbar

Andiamo nel pannello di amministrazione e clicchiamo su Aspetto e poi su Menù. Una volta arrivati nel modulo che ci interessa, dobbiamo creare un menu, dandogli come nome "main" e successivamente aggiungiamo gli elementi del menu che ci interessano, come categorie pagine o qualsiasi altro elemento del blog e infine clicchiamo sul pulsante Salva menu.

Assegniamo il menu all'unica posizione disponibile (primary) e clicchiamo nuovamente su Salva menu.

Image
Creazione del menu di Wordpress

Ora andiamo a fare le modifiche sul file header.php. Qui troviamo l'header di default che ha generato il tool di Automatic. Cancelliamolo il blocco <header> e colleghiamoci alla sezione navbar di Bootstrap, dove ci sono vari esempi d’implementazione. Copiamo il codice di un esempio che ci piace di più e torniamo sul file header.php. Qui incolliamo lo snippet appena prelevato, delimitandolo da due commenti in cui specifichiamo che si tratta del nuovo header.

<!-- Nuovo Header -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
    <div class="container-fluid">
        <a class="navbar-brand" href="#">Navbar</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarNav">
            <ul class="navbar-nav">
                <li class="nav-item">
                    <a class="nav-link active" aria-current="page" href="#">Home</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">Features</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">Pricing</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
                </li>
            </ul>
        </div>
    </div>
</nav>
<!-- /Nuovo Header -->

Da questo codice, eliminiamo il menu vero e proprio, cancellando il tag <ul> e il suo contenuto. Adesso utilizzeremo la pappa pronta, una classe scritta da uno dei tanti sviluppatori che contribuiscono alla community di GitHub. Questa classe modificherà l'output del menu, predisponendolo per accettare lo stile di Bootstrap.

La classe si chiama bootstrap_5_wp_nav_menu_walker ed è contenuta nel codice del file functions.php. Noi la collocheremo in un file apposito, per fare questo all'interno del nostro tema creiamo una nuova directory chiamata classes. All'interno di questa nuova cartella creiamo un file chiamato bootstrap_5_wp_nav_menu_walker.php e al suo interno incolliamo il codice della classe. Per comodità ve lo riporto di seguito

<?php

/**
 * Modifica l'output del menu
 */
class bootstrap_5_wp_nav_menu_walker extends Walker_Nav_menu
{
    private $current_item;
    private $dropdown_menu_alignment_values = [
        'dropdown-menu-start',
        'dropdown-menu-end',
        'dropdown-menu-sm-start',
        'dropdown-menu-sm-end',
        'dropdown-menu-md-start',
        'dropdown-menu-md-end',
        'dropdown-menu-lg-start',
        'dropdown-menu-lg-end',
        'dropdown-menu-xl-start',
        'dropdown-menu-xl-end',
        'dropdown-menu-xxl-start',
        'dropdown-menu-xxl-end'
    ];

    function start_lvl(&$output, $depth = 0, $args = null)
    {
        $dropdown_menu_class[] = '';
        foreach($this->current_item->classes as $class) {
            if(in_array($class, $this->dropdown_menu_alignment_values)) {
                $dropdown_menu_class[] = $class;
            }
        }
        $indent = str_repeat("\t", $depth);
        $submenu = ($depth > 0) ? ' sub-menu' : '';
        $output .= "\n$indent<ul class=\"dropdown-menu$submenu " . esc_attr(implode(" ",$dropdown_menu_class)) . " depth_$depth\">\n";
    }

    function start_el(&$output, $item, $depth = 0, $args = null, $id = 0)
    {
        $this->current_item = $item;

        $indent = ($depth) ? str_repeat("\t", $depth) : '';

        $li_attributes = '';
        $class_names = $value = '';

        $classes = empty($item->classes) ? array() : (array) $item->classes;

        $classes[] = ($args->walker->has_children) ? 'dropdown' : '';
        $classes[] = 'nav-item';
        $classes[] = 'nav-item-' . $item->ID;
        if ($depth && $args->walker->has_children) {
            $classes[] = 'dropdown-menu dropdown-menu-end';
        }

        $class_names =  join(' ', apply_filters('nav_menu_css_class', array_filter($classes), $item, $args));
        $class_names = ' class="' . esc_attr($class_names) . '"';

        $id = apply_filters('nav_menu_item_id', 'menu-item-' . $item->ID, $item, $args);
        $id = strlen($id) ? ' id="' . esc_attr($id) . '"' : '';

        $output .= $indent . '<li ' . $id . $value . $class_names . $li_attributes . '>';

        $attributes = !empty($item->attr_title) ? ' title="' . esc_attr($item->attr_title) . '"' : '';
        $attributes .= !empty($item->target) ? ' target="' . esc_attr($item->target) . '"' : '';
        $attributes .= !empty($item->xfn) ? ' rel="' . esc_attr($item->xfn) . '"' : '';
        $attributes .= !empty($item->url) ? ' href="' . esc_attr($item->url) . '"' : '';

        $active_class = ($item->current || $item->current_item_ancestor || in_array("current_page_parent", $item->classes, true) || in_array("current-post-ancestor", $item->classes, true)) ? 'active' : '';
        $nav_link_class = ( $depth > 0 ) ? 'dropdown-item ' : 'nav-link ';
        $attributes .= ( $args->walker->has_children ) ? ' class="'. $nav_link_class . $active_class . ' dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"' : ' class="'. $nav_link_class . $active_class . '"';

        $item_output = $args->before;
        $item_output .= '<a' . $attributes . '>';
        $item_output .= $args->link_before . apply_filters('the_title', $item->title, $item->ID) . $args->link_after;
        $item_output .= '</a>';
        $item_output .= $args->after;

        $output .= apply_filters('walker_nav_menu_start_el', $item_output, $item, $depth, $args);
    }
}

Adesso includiamo questa classe nel tema. Apriamo il file functions.php e inseriamo questa riga subito dopo il tag di apertura di php

require_once(get_stylesheet_directory() . '/classes/bootstrap_5_wp_nav_menu_walker.php');

Torniamo al file header.php e al posto del menu che abbiamo cancellato poco fa, inseriamo la funzione wp_nav_menu di Wordpress per stampare i menu, inserendo nei dovuti parametri le nostre impostazioni desiderate.

<div class="collapse navbar-collapse" id="navbarNav">
    <?php
    wp_nav_menu(
        array(
            'theme_location' => 'menu-1',
            'container' => false,
            'menu_class' => '',
            'fallback_cb' => '__return_false',
            'items_wrap' => '<ul id="%1$s" class="navbar-nav %2$s">%3$s</ul>',
            'depth' => 2,
            'walker' => new bootstrap_5_wp_nav_menu_walker()
        )
    );
    ?>
</div>

Perfetto, abbiamo finito. In seguito vedremo come gestire il logo tramite interfaccia. Per adesso mi limito a postare la struttura completa del nuovo header

<!-- Nuovo Header -->
<header id="masthead" class="site-header">
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
        <div class="container-fluid">
            <a class="navbar-brand" href="#">Navbar</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
                    aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarNav">
                <?php
                wp_nav_menu(
                    array(
                        'theme_location' => 'menu-1',
                        'container' => false,
                        'menu_class' => '',
                        'fallback_cb' => '__return_false',
                        'items_wrap' => '<ul id="%1$s" class="navbar-nav %2$s">%3$s</ul>',
                        'depth' => 2,
                        'walker' => new bootstrap_5_wp_nav_menu_walker()
                    )
                );
                ?>
            </div>
        </div>
    </nav>
</header>
<!-- /Nuovo Header -->