Creare temi

Tutto ciò che scrivi in FolioPress — pagine, articoli, tag — vive separato da come viene mostrato. Quel livello di presentazione è un tema, e questa guida ne smonta uno: la cartella, i template, il file di impostazioni ed esattamente quali dati riceve ogni template. Se sei a tuo agio con HTML e CSS, hai tutto ciò che serve.

Un tema è una cartella

Cambia tema e le stesse pagine, gli stessi articoli e lo stesso menu vengono ridisegnati — nulla del contenuto cambia, solo il suo abito. Un tema è una cartella con tre cose dentro: un file di impostazioni, i template e le risorse statiche.

Un tema è una cartella obbligatorio facoltativo my-theme/ theme.yml nome, colori, quanti articoli elencare… templates/ base.html la struttura comune a tutte le pagine home.html la pagina iniziale page.html una pagina (Chi siamo, Contatti…) post.html un articolo del blog tag.html l’archivio di un tag search.html risultati della ricerca 404.html · 500.html page-<slug>.html un layout dedicato a una pagina partials/ frammenti inclusi da altri template locales/pt-PT.json … static/ styles.css · scripts.js favicon.svg · apple-touch-icon.png
Bastano quattro template e theme.yml; tutto il resto è una rifinitura facoltativa.

Quattro template sono obbligatori — base.html, home.html, page.html e post.html — e un tema che ne è privo non si carica. Tutto il resto è facoltativo: un archivio dei tag, una pagina di ricerca, pagine di errore personalizzate, un layout dedicato a una pagina specifica e una cartella locales/ per le parole del tema stesso. I temi si modificano direttamente nell’amministrazione, sotto Temi, si esportano in zip e si importano in un altro sito.

Da un URL a una pagina

Aiuta vedere cosa fa FolioPress con una richiesta prima ancora che il tuo template giri, perché spiega perché i template sono così brevi.

1 Un visitatore apre un URL /it-it/chi-siamo 2 FolioPress trova la lingua e la pagina it-IT · “Chi siamo” 3 Costruisce il contesto page, site_title, languages, navigation… 4 Sceglie un template page-chi-siamo.html? altrimenti page.html 5 Renderizzato dentro base.html HTML inviato al browser Ogni valore arriva già tradotto nella lingua del visitatore; il template stampa solo ciò che riceve.
Una richiesta dall’inizio alla fine. I passi 3 e 4 sono quelli in cui il tema fa il suo lavoro.

Due cose da notare. La lingua viene letta dal prefisso dell’URL, quindi /it-it/chi-siamo e /about sono la stessa pagina in due lingue, e il contesto consegnato al tuo template è già in quella giusta — ogni titolo, slug, voce di menu e contenuto. E il template viene scelto per te: per una pagina il cui slug nella lingua principale è about, FolioPress cerca prima page-about.html e ripiega su page.html. Non c’è nulla da collegare.

Una struttura, molte pagine

I template sono Jinja2 — HTML con due tipi di parentesi in più. {{ page.title }} stampa un valore. {% if page.poster_image_url %} … {% endif %} è un’istruzione: cicli, condizioni ed ereditarietà.

base.html la struttura comune a tutte le pagine <head>, navigazione, selettore della lingua {% block content %} l’unico spazio che ogni template riempie piè di pagina, script Ognuno di questi riempie lo spazio: home.html {% extends %} page.html {% extends %} post.html {% extends %} tag.html {% extends %} search.html {% extends %}
Intestazione e piè di pagina vivono in un solo posto; gli altri template scrivono solo il centro.

base.html contiene tutto ciò che ogni pagina condivide — l’<head>, la navigazione, il selettore della lingua, il piè di pagina, gli script — con un unico spazio vuoto al centro. Ogni altro template inizia allo stesso modo e riempie quello spazio:

{% extends "base.html" %}

{% block content %}
  <h1>{{ page.title }}</h1>
  {{ page.content | safe }}
{% endblock %}

Tutto ciò che si ripete tra i template — una scheda articolo, una barra di paginazione — va in partials/ e si include con {% include "partials/post-card.html" %}.

theme.yml: il pannello delle impostazioni

È qui che un tema si descrive e configura alcuni comportamenti, senza toccare un template. Ecco cosa usano i temi inclusi:

title: "Ink"
description: "Un tema pulito e minimale"
version: "1.0.0"
author: "FolioPress"

meta:
  supports_dark_mode: true
  responsive: true
  categories: [minimal, portfolio]

settings:                    # libero; disponibile nei template come theme_settings
  primary_color: "#2563eb"

home:
  posts: 10                  # quanti articoli riceve la pagina iniziale
  order_by: "-published_at"  # i più recenti prima
  has_pagination: false      # un taglio, o veri collegamenti di pagina?
  image_ratios:
    poster: "16:9"           # cosa offre qui lo strumento di ritaglio dell’amministrazione
    gallery: "free"

page:  { posts: 10, order_by: "-published_at", has_pagination: false }
post:  { posts: 3,  order_by: "-published_at" }   # articoli correlati sotto un articolo
tag:   { posts: 12, order_by: "-published_at", has_pagination: true }

page-about:                  # override per una pagina, tramite il suo slug nella lingua principale
  image_ratios: { poster: "1:1" }
post-tutorials:              # override per gli articoli il cui primo tag ha questo slug
  image_ratios: { poster: "4:3" }
Chiave Cosa controlla
title, description, version, author Mostrati nella pagina Temi dell’amministrazione.
meta.supports_dark_mode, meta.responsive, meta.categories Descrittivi; usati per la galleria dei temi.
meta.social_image Un file nella cartella static/ del tema, 1200x630, usato come immagine di anteprima quando una pagina non ha un'immagine di copertina propria. Senza, quella pagina viene condivisa con l'icona da 180x180.
settings Quello che vuoi. Consegnato a ogni template come theme_settings, così un colore o uno slogan può vivere qui invece che nel CSS.
home, page, post, tag L’elenco di articoli che riceve quel tipo di template: posts (quanti), order_by, e se has_pagination aggiunge veri collegamenti di pagina o taglia semplicemente l’elenco.
order_by -published_at (i più recenti prima) o published_at; lo stesso per created_at e updated_at, con o senza il - iniziale; oppure random.
image_ratios Cosa offre lo strumento di ritaglio dell’amministrazione quando si modifica quel tipo di contenuto: una proporzione come 16:9, un elenco di proporzioni, o free. Valori distinti per poster e gallery.
page-<slug>, post-<tag-slug> Le stesse chiavi, che sovrascrivono page/post per una pagina (tramite il suo slug nella lingua principale) o per gli articoli il cui primo tag ha quello slug.

Cosa vede ogni template

Un template non va mai a prendersi nulla. Riceve un insieme di valori pronti all’uso — FolioPress lo chiama il contesto — e stampa ciò che gli serve. Parte dell’insieme è uguale in ogni pagina; il resto dipende dal template che viene renderizzato.

Ogni template riceve questo insieme page site_title site_description navigation_menu languages current_language language_switch_urls footer static_url media_url home_url canonical_url hreflang_urls t() …e inoltre, a seconda del template: home.html posts pages is_home page.html posts is_contact_page post.html related_posts is_post tag.html tag posts search.html search_query search_results results_count
L’insieme comune è lo stesso in ogni pagina; gli extra sono ciò che distingue ogni tipo di template.

Comune a tutti i template

Variabile Che cos’è
page La pagina o l’articolo visualizzato, già nella lingua corrente (vedi sotto). None nell’archivio dei tag e nella ricerca.
site_title, site_description, footer Dalle Impostazioni del sito, nella lingua corrente. footer è già renderizzato da Markdown.
navigation_menu Il menu configurato nell’amministrazione: un elenco di {title, url, slug, page_id}, ogni url pronto all’uso in questa lingua.
languages Le lingue del sito, indicizzate per codice. Ogni voce: code, name, native_name, short_code, menu_label, flag.
current_language, current_language_code, default_language_code La lingua in corso di rendering (stessa forma di una voce di languages), il suo codice e la lingua principale del sito.
language_switch_urls Indicizzato per codice lingua: l’URL di questa stessa pagina in quella lingua, slug tradotto compreso.
home_url, url_language_code La pagina iniziale nella lingua corrente (/ o /pt-pt) e il prefisso in minuscolo da mettere davanti a qualsiasi URL costruisci tu ('' per la lingua principale).
static_url, media_url Da dove vengono serviti i file di questo tema (/static/<tema>) e dove vivono le immagini caricate.
page_title, meta_description, canonical_url, hreflang_urls, social_image_url, twitter_card_type, json_ld Tutto ciò di cui ha bisogno l’<head>: un titolo di scheda sensato, URL canonico e alternativi per lingua assoluti, un’immagine Open Graph e dati strutturati — tutto precalcolato.
theme_name, theme_info, theme_settings Il nome del tema attivo, il suo theme.yml completo e il suo blocco settings.
t Il traduttore del tema stesso: {{ t('Read more') }} cerca la frase in locales/<lingua>.json (vedi sotto).
request La richiesta FastAPI, per il raro caso in cui ti serva il percorso o un parametro.

L’oggetto page

La stessa forma, che sia una pagina, un articolo o una voce di qualsiasi elenco di articoli:

Campo Note
title, slug, url Nella lingua corrente. url è completo, incluso il prefisso di lingua o il dominio proprio della lingua.
content Già convertito da Markdown in HTML — stampalo con {{ page.content | safe }}.
excerpt, meta_description Le forme brevi, nella lingua corrente.
poster_image_url URL completo dell’immagine di copertina, o None.
gallery, content_gallery Elenchi di immagini: {id, url, title, width, height, original_filename}. Usa image.url direttamente.
tags {slug, title, url} per ogni tag pubblico. I tag privati non sono mai inclusi.
is_post, is_featured, published_at, created_at, updated_at Se è un articolo, se è in evidenza, e le sue date.
id L’id nel database — comodo per ancore e chiavi di cache.

Elenchi di articoli

Template Variabile Contenuto
home.html posts (alias recent_posts), pages, is_home Gli articoli che condividono un tag con la pagina iniziale, in numero e ordine dalla sezione home di theme.yml. Una pagina iniziale senza tag non riceve articoli — assegnale un tag per riempirla.
page.html posts (alias page_posts) Gli articoli che condividono un tag con questa pagina, dalla sezione page. È così che una pagina “Blog” elenca i suoi articoli.
post.html related_posts, is_post Altri articoli che condividono un tag con questo, dalla sezione post.
tag.html tag (title, slug), posts Ogni articolo pubblicato con questo tag, dalla sezione tag.
search.html search_query, search_results, results_count Il termine cercato e le pagine e gli articoli corrispondenti.

Ogni elenco di articoli arriva con una paginazione della stessa forma: has_pagination, current_page, total_pages, has_previous, has_next, previous_page, next_page e pagination_urls con first, previous, next e last — None dove non c’è dove andare. Con `has_pagination` disattivato in `theme.yml`, l’elenco viene semplicemente tagliato a `posts` e gli indicatori restano falsi.

Le lingue e il selettore

Due dei campi lingua vengono calcolati per te. short_code è il solo codice della lingua — EN — a meno che il sito abbia attiva anche un’altra variante della stessa lingua, nel qual caso mantiene la regione: EN-US ed EN-GB. menu_label segue la stessa regola con i nomi: English, oppure English (US) ed English (UK). È esattamente la regola usata dal selettore della lingua dell’amministrazione, così il sito e l’amministrazione non sono mai in disaccordo su un’etichetta. Un selettore completo è quindi breve:

{% if languages | length > 1 %}
<button type="button" aria-label="{{ t('Change language') }}">
  {{ current_language.short_code }}
</button>
<ul>
  {% for lang in languages.values() | sort(attribute='menu_label') %}
  <li>
    {% if lang.code == current_language.code %}
      <span aria-current="true">{{ lang.menu_label }}</span>
    {% else %}
      <a href="{{ language_switch_urls[lang.code] }}" hreflang="{{ lang.code }}">{{ lang.menu_label }}</a>
    {% endif %}
  </li>
  {% endfor %}
</ul>
{% endif %}

language_switch_urls punta già alla stessa pagina in ogni lingua — lo slug tradotto, o la pagina iniziale se quella traduzione non esiste ancora —, quindi un selettore non costruisce mai un URL da solo.

Le parole del tema stesso

Il contenuto lo traduce l’autore nell’amministrazione. Le parole proprie di un tema — “Leggi tutto”, “Cerca”, “Tutti i diritti riservati” — le traduce il tema, in templates/locales/<lingua>.json:

{
  "Read more": "Leggi tutto",
  "Search": "Cerca",
  "Change language": "Cambia lingua"
}

Racchiudi la frase in {{ t('Read more') }} e apparirà nella lingua del visitatore. Una frase assente dal file di quella lingua ripiega sul file della lingua principale, poi sulla frase stessa — così un tema tradotto a metà funziona comunque, semplicemente in inglese dove la traduzione non è ancora stata scritta. I file viaggiano con il tema quando viene esportato.

Una pagina con il proprio layout

Una landing page, un modulo di contatto, una griglia portfolio — a volte una pagina merita un layout di cui nessun’altra ha bisogno. Aggiungi un template chiamato page-<slug>.html, dove <slug> è lo slug di quella pagina nella lingua principale, e FolioPress lo userà per quella pagina in ogni lingua. Cancella il file e la pagina torna silenziosamente a page.html.

{# templates/page-about.html #}
{% extends "base.html" %}
{% block content %}
<section class="about">
  <img src="{{ page.poster_image_url }}" alt="">
  <h1>{{ page.title }}</h1>
  {{ page.content | safe }}
</section>
{% endblock %}

La pagina che un amministratore sceglie come pagina del modulo di contatto riceve un piccolo extra: is_contact_page è vero, contact_form_action è dove il modulo deve inviare, e contact_guard_token / contact_honeypot_field sono due campi nascosti che tengono fuori i bot. Dopo un invio, contact_status ti dice se mostrare un ringraziamento o un errore.

Parti da un tema che funziona

Il modo più rapido per costruire un tema è aprire uno di quelli inclusi — ognuno ha una demo dal vivo — e leggerlo accanto a questa pagina. Duplicalo nell’amministrazione, cambia il CSS, poi i template, e avrai qualcosa di tuo senza mai essere partito da un file vuoto.

Due abitudini che fanno risparmiare tempo. Stampa i valori che il contesto ti dà invece di ricostruirli — page.url, tag.url e language_switch_urls conoscono già i prefissi di lingua e i domini personalizzati. E tieni la struttura in base.html: se ti sorprendi a incollare un’intestazione in un secondo template, il suo posto è nella base.