Vai al contenuto

Piccoli siti web sviluppati con un baby-framework in PHP+SCSS

Scritto da:

carlopeluso

Nell’ultimo periodo, coinciso più o meno con l’inizio del primo lockdown, ho dato il mio contributo aiutando quattro piccole associazioni nella realizzazione dei loro siti web. Ho colto quest’occasione per rinfrescare un po di conoscenze che avevo accantonato 7-8 anni fa, riguardanti lo sviluppo web in ottica SEO. L’idea era di usare (o creare) un piccolissimo stack tecnologico che mi potesse aiutare a risparmiare tempo e soprattutto potesse essere risultare consono alla natura statica dei singoli progetti.

Unpopular opinion: data la natura minimalista di tutti e quattro i siti, ho ritenuto il PHP il linguaggio più consono alla mia soluzione. Ecco i motivi:

  • I server Apache/PHP sono inclusi nei domini/hosting piu economici (20 euro/anno. Io ho scelto supporthost.com),
  • Configurare un ambiente di sviluppo PHP nativo è da record: 3 minuti
  • E’ un ottimo linguaggio per progetti molto piccoli, specie per siti statici
  • Ha una curva di apprendimento rapida ed una community storica
  • E’ utilizzato da 8 siti su 10 a livello mondiale
  • E’ un ottimo generatore di HTML, perfettamente interpretabile dai motori di ricerca

e poi, narcisisticamente:

  • E’ il linguaggio che mi ha avvicinato al mondo dell’informatica e ne sono legato emotivamente (ebbene si anche gli informatici hanno un cuore 🙂 ).

Gestione Routes base

Partiamo dal requisito essenziale che tutti e quattro i siti avevano in comune, ovvero una semplicissima gestione route. Avendo ispezionato diversi framework per la creazione di Route di navigazione sono giunto alla conclusione che quello che mi serviva era una semplice regola nel file .htaccess unita ad un mini script index.php che facesse il dispach della route verso le varie pagine a partire dall’url.

file .htaccess

Options +FollowSymLinks
 RewriteEngine On
 RewriteCond %{REQUEST_URI} !(.css|.pdf|.js|.xml|.ico|.svg|.webp|.png|.jpg|.jpeg|.JPG|.gif|.mp4|robots.txt|.woff|.otf|.woff2|.ttf)$ [NC]
 RewriteRule ^(.*)$ index.php [NC,L]

In questo modo reindirizziamo tutte le richieste all’index.php, isolando le estensioni inserite nella lista (questa regola di sicurezza è molto utile per evitare lo sniffing di file di configurazione, anche da parte dei motori di ricerca)

file index.php

<?php

$request = $_SERVER['REQUEST_URI'];
$urlInfo = parse_url($request);

$pages = array(
   "/" => "home",
   "/page-one" => "page-one",
   "/page-two" => "page-two",
);

$currentPage = "404";
if (array_key_exists($urlInfo['path'], $pages)) {
    $currentPage = $pages[$urlInfo['path']];
} 

$r = "views/" . $currentPage . "/" . $currentPage . ".builder.php";

include($r);

?>

Attraverso questi due semplici set di codice, sono riuscito a gestire tutte le possibilità di route (comprese le query-string, che puoi leggere facilmente tramite variabile globale $_GET (nell’esempio ridotto questa parte non è presente).

Architettura, cartelle e files

Ho preso ispirazione dalla struttura “component-like” di Angular, data la sua natura fortemente scalabile (che mi permetterà di inserire nuovi componenti se necessari in futuro).

In quest’immagine evidenzio le varie parti del progetto:

  • Una cartella root che contiene i file essenziali all’avvio del sito: htaccess e index.php
  • package.json per l’installazione dei pacchetti NPM utili allo sviluppo
  • gulpfile per automatizzare alcune operazioni durante il build
  • un file di configurazione chiamato config.php che contiene la base-url e i percorsi degli assets
  • un file functions.php che è una classe per l’accesso ad API esterne (a cui ho dato il ruolo di “controller”)
  • la cartella scss, dove ci sono i file di stile condivisi (librerie)
  • models, che contiene una classe PHP chiamata Page, con le proprietà di ogni route
  • cartelle di assets images, font, download, external-data
  • la cartella css nella quale viene compilato, minimizzato e ottimizzato per tutti i browser il codice SCSS di ogni pagina
  • la cartella views nella quale sono “componentizzate” le varie pagine
  • la cartella shared che contiene elementi comuni al sito (header, footer)

Ogni componente-pagina ha al suo interno 3 file:

  • Un file pagina.scss specifico per la singola pagina
  • Un file pagina.content.php nel quale è presente il markup html
  • Un file pagina.builder.php nel quale vengono elencate le proprietà per il SEO (titolo, descrizione, immagini o:graph) e vengono prelevate le informazioni necessarie all’avvio della singola schermata.

Uno sguardo al backbone SCSS

Sono partito da un framework cosidetto “classless”, ovvero un set di regole CSS settate direttamente sui componenti nativi HTML (h1,h2,paragrafo) evitando di utilizzare classi (come fanno i piu blasonati Bootstrap, UI Kit ect). La cosa meravigliosa di questo tipo di approccio è che permette una straordinaria pulizia del codice. Le uniche classi che ho generato sono le seguenti:

$base: 16px;
$sides: "top", "bottom", "right", "left";

.margin {
    margin: $base * 2 !important;

    &-smallest {
        margin: $base / 2 !important;
    }

    &-small {
        margin: $base !important;
    }
    &-large {
        margin: 3 * $base !important;
    }
    &-largest {
        margin: 4 * $base !important;
    }
    &-none {
        margin: 0 !important;
    }
}

.padding {
    padding: $base * 2 !important;

    &-smallest {
        padding: $base / 2 !important;
    }
    &-small {
        padding: $base !important;
    }
    &-large {
        padding: 3 * $base !important;
    }
    &-largest {
        padding: 4 * $base !important;
    }
    &-none {
        padding: 0 !important;
    }
}

@each $side in $sides {
    .margin-#{$side} {
        margin-#{$side}: 2 * $base !important;

        &-smallest {
            margin-#{$side}: $base / 2 !important;
        }
        &-small {
            margin-#{$side}: $base !important;
        }
        &-large {
            margin-#{$side}: 3 * $base !important;
        }
        &-largest {
            margin-#{$side}: 4 * $base !important;
        }
        &-none {
            margin-#{$side}: 0 !important;
        }
    }

    .padding-#{$side} {
        padding-#{$side}: 2 * $base !important;

        &-smallest {
            padding-#{$side}: $base / 2 !important;
        }
        &-small {
            padding-#{$side}: $base !important;
        }
        &-large {
            padding-#{$side}: 3 * $base !important;
        }
        &-largest {
            padding-#{$side}: 4 * $base !important;
        }
        &-none {
            padding-#{$side}: 0 !important;
        }
    }
}

@include mobile {
    .m-margin {
        margin: $base * 2 !important;

        &-small {
            margin: $base !important;
        }
        &-large {
            margin: 3 * $base !important;
        }
        &-largest {
            margin: 4 * $base !important;
        }
        &-none {
            margin: 0 !important;
        }
    }

    .m-padding {
        padding: $base * 2 !important;

        &-small {
            padding: $base !important;
        }
        &-large {
            padding: 3 * $base !important;
        }
        &-largest {
            padding: 4 * $base !important;
        }
        &-none {
            padding: 0 !important;
        }
    }

    @each $side in $sides {
        .m-margin-#{$side} {
            margin-#{$side}: 2 * $base !important;

            &-small {
                margin-#{$side}: $base !important;
            }
            &-large {
                margin-#{$side}: 3 * $base !important;
            }
            &-largest {
                margin-#{$side}: 4 * $base !important;
            }
            &-none {
                margin-#{$side}: 0 !important;
            }
        }

        .m-padding-#{$side} {
            padding-#{$side}: 2 * $base !important;

            &-small {
                padding-#{$side}: $base !important;
            }
            &-large {
                padding-#{$side}: 3 * $base !important;
            }
            &-largest {
                padding-#{$side}: 4 * $base !important;
            }
            &-none {
                padding-#{$side}: 0 !important;
            }
        }
    }
}

Questo microframework è adatto per inquadrare qualsiasi tipo di componente all’interno di un box con margin e padding stabiliti da un algoritmo (in questo caso, ho generato 4 “unità” basate sul multiplo di 16px): ‘smallest‘, ‘small’, ‘large’, ‘largest‘, ‘non specificato’ e ‘none‘. Tutte le classi generate sono duplicate anche per la versione mobile, che ho preferito definire con il prefisso “m-“, utile nel markup se si vuole distinguere un margine su desktop diverso rispetto a quello mobile.

ecco un esempio di markup:

 <div class="margin-large m-padding-none"></div>

…che significa “aggiungi un margin large su tutti e quattro i lati se siamo su risoluzioni desktop, mentre su mobile elimina i margini“. Se non specificato, il mobile erediterà la classe dalla risoluzione desktop.

Usiamo solo le proprietà flexbox indispensabili al layout

Per alcuni tipici layout a due colonne, che siano orizzontali o verticali, utilizzo la proprietà flex in questo modo:

.flex {
    display: flex;
    &.center {
        align-items: center;
    }
    &.right {
        align-items: flex-end;
    }
    &.left {
        align-items: flex-start;
    }
    &.justify-center {
        justify-content: center;
    }
    &.column {
        flex-direction: column;
    }
    &.wrap {
        flex-wrap: wrap;
    }
    &.inline {
        display: inline-flex;
    }
    .flex-2 {
        flex: 2;
    }
    .flex-1 {
        flex: 1;
    }
    .flex-0 {
        flex: 0;
    }

    &.m-column {
        @include mobile {
            flex-direction: column;
        }
    }
}

Creando queste “constraints” (notare la possibilità di “pesare” l’item flex solo in 3 pesi: 0, 1 e 2, questo costringe alla creazione di layout con massimo 3 child items) ho limitato le possibilità di errore di layout o di varianti layout, consentendo solo ciò che è progettato nel design system del singolo sitoweb). Il risultato è stato un controllo veramente notevole di tutte le porzioni dello schermo e la certezza che ogni pagina avesse un layout solido.

Risultati

Allego alcune demo dei siti che ho realizzato con questo semplice framework del tutto personale, (chiaramente descritto in questo blog in maniera inesaustiva, ma chi volesse approfondire mi puo contattare)

Omnia Torino

www.omniatorino.it – OMNIA è un think tank di ragazzi under 30 che propongono soluzioni per il futuro di Torino
…e la sua versione mobile

Statistiche implementazione

  • Dimensioni complessive progetto: 16MB
  • Dimensioni codice: 726Kb
  • Numero commit totali: 24
  • Numero sezioni/viste implementate: 10
  • Tempo di sviluppo: 80 ore

TEDxLeverano

~~tedxleverano.com – TEDX è un format mondiale di Speech che diffondono idee di valore su vari ambiti (scienza, psicologia, arte, medicina, società)
…e la sua versione mobile

Statistiche implementazione

  • Dimensioni complessive progetto: 6MB
  • Dimensioni codice: 274Kb
  • Numero commit totali: 42
  • Numero sezioni/viste implementate: 6
  • Tempo di sviluppo: 140 ore

CiVuoleUnPaese

civuoleunpaese.it – “Ci vuole un Paese” è un festival musicale- he si svolge ogni anno a Leverano (Le)
….e la sua versione mobile

Statistiche implementazione

  • Dimensioni complessive progetto: 48MB
  • Dimensioni codice: 303Kb
  • Numero commit totali: 33
  • Numero sezioni/viste implementate: 8
  • Tempo di sviluppo: 100 ore

Glimmer

glimmermusic.it – Glimmer è un’agenzia artistica
…e la sua versione mobile

Statistiche implementazione

  • Dimensioni complessive progetto: 39MB
  • Dimensioni codice: 720Kb
  • Numero commit totali: 18
  • Numero sezioni/viste implementate: 12
  • Tempo di sviluppo: 54 ore

Disclaimer

Sicuramente è un baby-framework con delle potenzialità, ma anche grossi limiti. Non è la scelta giusta per un sito dinamico che richiede uno stack piu complesso e stratificato.

Articolo precedente

Preload di immagini in poche righe di CSS.

Articolo successivo

Creare un podcast in automatico a partire da audio vocali e musica