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.
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.
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 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.
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.