Přeskočit obsah
V
Pro vývojáře
Architektura, konvence, jádro systému a bezpečnost
Vzhled a témata / Témata frontendu

Témata frontendu#

Téma je celý vizuální celek webu — rozvržení, šablony, SCSS a JavaScript pohromadě. www/themes/, jádro výběru app/Core/Theme/.

Dvě navazující kapitoly rozvádějí jeho části: Struktura SCSS a Design systém a tokeny.

Struktura#

www/themes/
  admin/default/desktop/          administrace, výchozí šablona
  admin/template1/desktop/        administrace, druhá šablona (viz Témata administrace)
  frontend/template1/desktop/     jedna z 18 front šablon (template1–template18)
      layouts/                    rozvržení a jejich části
      assets/{scss,css,js,dist,imgs}
      assets/skins/<název>/       barevné varianty (skiny), viz níž
      ui-demo/                    katalog komponent
      webpack.config.js
  frontend/template2/desktop/     další šablona, přepíná se z administrace

Téma má varianty podle zařízení: DefaultThemeResolver vybere mobile, tablet nebo desktop podle DeviceDetector — a když varianta v tématu neexistuje, spadne zpátky na desktop.

Které téma je aktivní#

Téma se nevybírá v kódu, ale nastavením v databázi: theme.frontend (název) a theme.frontend.path (relativní cesta, dopočítaná). Ručně se dnes needituje — administrátor je vybírá naráz se skinem v Nastavení → Vzhled webu (ThemeSettingPresenter), viz Vzhled webu. Šablon je dnes 18, template1–template18 (spočítáno ls www/themes/frontend/*/theme.json | wc -l).

Téma v nastavení platí pro každý web nad touž databází

Tabulka nastavení nemá sloupec pro prostředí. Běží-li nad stejnou databází víc instalací (třeba vývojová a ostrá), změna theme.frontend.path by na ostrém webu nezměnila jen barvy — nemá-li tamní instalace soubory nové šablony, takže by jí přestaly fungovat všechny frontové stránky kvůli chybějícím layoutům. Tohle riziko platí i pro obrazovku „Vzhled webu“ — je to jen jiné rozhraní nad týmiž klíči nastavení.

Lokálně se téma přepíná proměnnou prostředí, ne nastavením

CMS_THEME_OVERRIDE=template2 php -S 127.0.0.1:8099 -t www
Bez proměnné je to no-op — produkce ji nikdy nenastaví. Neexistující adresář se ignoruje, aby překlep neshodil web.

Přepnutí vzhledu pro jednoho návštěvníka#

Vedle nastavení v databázi umí DefaultThemeResolver přebít téma i skin jen pro aktuální požadavek — databázi nemění. Volba se hledá v tomhle pořadí, vyhrává první nalezená:

Pořadí Téma Skin Poznámka
1 ?_theme=<adresář> ?_skin=<název> uloží se do session cookie, takže přežije další prokliky
2 cookie cms_theme_override cookie cms_skin_override zmizí se zavřením prohlížeče
3 env CMS_THEME_OVERRIDE env CMS_SKIN_OVERRIDE výchozí volba celého běhu serveru
— nastavení theme.frontend nastavení theme.frontend.skin když nic z toho výš neplatí

Hodnota off, reset, default, none (nebo prázdná) cookie smaže a vrátí volbu z env, a teprve když env není nastavená, z databáze. URL stojí nad env schválně: server spuštěný s CMS_THEME_OVERRIDE by jinak ?_theme= tiše ignoroval.

Přepínání přes URL a cookie je zapnuté pro každého návštěvníka, protože je součástí ukázky CMS (přepínač vzhledu ThemeSwitcher). Vypíná se proměnnými prostředí, každá zvlášť:

Proměnná Vypne Nevypne
CMS_THEME_SWITCH=0 ?_theme= a cookie tématu skiny
CMS_SKIN_SWITCH=0 ?_skin= a cookie skinu šablony

Env CMS_*_OVERRIDE platí i s vypnutým přepínáním. Kdo chce vzhled zamknout úplně, nastaví obě *_SWITCH na 0.

Přepnout jde jen na šablonu, která má desktop/

Nabídka (availableThemes()) bere jen adresáře s variantou desktop/, protože bez ní by se nenašel layout. Název mimo [a-z0-9_-] nebo neexistující adresář se ignoruje a zůstane téma z nastavení.

Přepínání platí jen pro frontové téma

Resolver je jedna služba i pro administraci. Kdyby se přepnulo i admin téma, hledala by administrace layouty ve frontové šabloně, findLayoutTemplateFile() by tiše vrátil null a celý admin by se vykreslil jako holý obsah bez <html>, CSS a JS, bez záznamu v logu. Kontrola větve proto stojí v applyLocalOverride() ještě před čtením ?_theme=. Administrace má vlastní parametry a cookie, viz Témata administrace.

Přepínač vzhledu na webu#

Komponenta ThemeSwitcher nabízí návštěvníkovi šablony a jejich skiny jako barevná kolečka. Kolečko vybírá dvojici šablona + skin; první kolečko šablony je vždy základní vzhled bez skinu. Volba se uloží jen do cookie toho, kdo klikl, nastavení webu se nemění.

Situace Co komponenta nabídne
CMS_THEME_SWITCH=0 i CMS_SKIN_SWITCH=0 nic, nevykreslí se
jen CMS_THEME_SWITCH=0 jen aktivní šablonu a její skiny; cizí šablony by byly mrtvé odkazy, protože resolver ?_theme= ignoruje
celkem méně než dvě kolečka nic: prázdný panel by jen zabíral roh obrazovky
barva s duplicateColor kolečko se vynechá, kromě právě zapnutého skinu (jinak by fajfka neměla u čeho být)

Odkaz kolečka je aktuální URL s ?_theme=…&_skin=…, takže návštěvník zůstane na téže stránce. Není to signál (handle…): ten by potřeboval CSRF token a měnil by se s každým překreslením, takže by odkaz nešel sdílet ani cachovat.

Volba shodná s nastavením webu musí posílat off

Kolečko výchozí šablony posílá _theme=off a kolečko výchozího skinu _skin=off, tedy zrušení volby. Kdyby posílalo název, návštěvník by si cookie uložil natvrdo a pozdější změna vzhledu v administraci by se k němu nedostala. Skin se s nastavením porovnává jen u šablony z nastavení; stejně pojmenovaný skin jiné šablony off neposílá.

_theme a _skin jdou vždy spolu

Kolečko základního vzhledu musí skin zrušit (_skin=off). Bez toho by po přepnutí šablony zůstal skin z předchozí volby, který do nové šablony nepatří.

Mount patří mimo {snippet page}

{control frontBaseThemeSwitcher} je v layouts/parts/shared/bottom.latte každé šablony, vedle cookie lišty. Uvnitř snippetu by ho AJAX překreslení stránky sestřelilo.

Témata administrace#

Administrace má dvě šablony v www/themes/admin/: default a template1. Obě mají stejné layouty a shodný JavaScript (desktop/assets/js je v obou stejný strom), liší se jen SCSS:

Šablona Kde je její vzhled Sestavení
default společný základ; styly funkčních prvků administrace (drobečky, akce řádku, panel hromadných akcí, stav formuláře, seznam a detail) v assets/scss/modules/system/_admin_features.scss npm run build:admin
template1 společný základ a odchylky šablony jen v assets/scss/custom/ (_admin_data_grid, _admin_edit_layout, _admin_form_facelift, …) npm run build:admin:t1

Oba příkazy se pouštějí z kořene projektu (package.json) a sestaví JS (webpack) i CSS (sass) dané šablony.

Změnu skriptu administrace udělejte v obou šablonách

bin/theme-js-sync.php hlídá jen frontové šablony. Skript upravený jen v admin/default v template1 chybí a prvek tam tiše nefunguje, i když markup je stejný.

Odchylka template1 patří do custom/

Ostatní složky SCSS jsou společný základ. Pravidlo jen pro template1 zapsané jinam změní vzhled i šablony default.

Šablona komponenty pro admin téma#

Komponenta si šablonu pro konkrétní admin téma hledá stejně jako na frontu: soubor v Templates/Template1/default/… má přednost před Templates/Default/…. Platí to i pro jednotlivé části přes resolveThemeTemplatePath(), viz Komponenty. Obě admin témata dnes sdílejí šablony z Templates/Default/ (akce řádku, patička gridu, lišta akcí formuláře); liší se jen styly.

Vlastní admin téma uživatele#

Výchozí admin téma a skin jsou v nastavení theme.admin a theme.admin.skin (upravuje se v Nastavení administrace). Přihlášený uživatel si vedle toho vybere vlastní téma přepínačem v hlavičce administrace (ThemeSwitcherComponent, {control adminSystemThemeSwitcher} v layouts/parts/shared/_header.latte). Volbu zpracuje DefaultThemeResolver::applyAdminOverride():

Parametr Co udělá
?_admin_theme=<adresář> uloží téma do cookie cms_admin_theme (rok) a smaže cookie skinu
?_admin_skin=<název> uloží skin do cookie cms_admin_skin
?_admin_theme=reset (off, none, prázdné) smaže obě cookie, platí zase nastavení
?_admin_skin=reset smaže jen cookie skinu

Po uložení resolver přesměruje (u GET) na tutéž adresu bez parametrů. Téma se přijme, jen když ThemeCatalog::exists() najde v www/themes/admin/ adresář s theme.json, skin jen když ho téma má; cokoli jiného se ignoruje, takže ručně podstrčená cookie nikam jinam nevede. Odkazy přepínače jsou prosté GET bez signálu, není tedy co zfalšovat.

Existující téma má přednost před slovem pro reset

Základní admin šablona se jmenuje default, což je zároveň slovo pro reset. Resolver proto nejdřív zkusí, jestli téma tohoto jména existuje: ?_admin_theme=default přepne na šablonu default a uloží ji do cookie, nastavení nevrátí. Návrat k nastavení je ?_admin_theme=reset.

Cesty do tématu v šablonách#

Šablona se nesmí vázat na jméno tématu. K dispozici jsou dvě funkce (fungují i jako filtry) a jeden test:

Zápis Co udělá
{=themeAsset('assets/dist/bundle.js')} cesta k souboru uvnitř aktivního tématu
{=themeUrl('…')} absolutní URL do aktivního tématu
{if $x is isTheme('template2')} větvení podle tématu

Registruje je app/Core/Latte/Extensions/ThemeLatteExtension.php.

Cesta napsaná natvrdo přestane fungovat při přepnutí tématu

A nikdo to nezjistí, dokud se téma nepřepne — tedy typicky až u zákazníka s vlastní šablonou. V template1 je themeAsset() použitý na 30 místech.

Za asset patří ?v= s časovým razítkem

Prohlížeč jinak drží starý soubor v mezipaměti a změna se „neprojeví“.

Postup: upravuji vzhled běžícího frontendu#

  1. Ověř, kterou větev sestavení používá. V themes/ je hodně mrtvých větví — desktop_bck, scss-backup, scss_2, js2, ui-demo_old. Editace mrtvé větve je nejčastější ztráta času v téhle části systému.
  2. SCSS patří do template1/desktop/assets/scss/, sestavení:
sass assets/scss/style.scss assets/css/style.css --style=compressed
  1. JavaScript do assets/js/{components,behaviors,pages}/, sestavení:
npx webpack --config webpack.config.js
  1. Prohlédni výsledek v katalogu komponent (ui-demo/) — je to jediné místo, kde je vidět většina prvků najednou.
  2. Prokliknij i mobilní variantu, pokud ji téma má.

npx sass NENÍ totéž co sass

Je to jiná verze překladače a vyrobí jiný výstup. Používej sass.

Bez sestavení se změna SCSS neprojeví — ani po tvrdém obnovení

Aplikace čte css/style.css, ne scss/. K tomu ještě prohlížeč drží starou verzi v mezipaměti.

Postup: zakládám nové téma#

  1. Zkopíruj celý základ existujícího tématu; rozdíly drž ve vlastní vrstvě (custom/), ne rozsypané po celém stromu.
  2. Ověř to diff -r proti zdrojovému tématu — mělo by ukázat jen tvoji vrstvu, katalog komponent a výstupy sestavení. Cokoli navíc je nechtěná odchylka, která se při další opravě zdrojového tématu rozejde.
  3. Nezapínej ho v nastavení, dokud nejsou soubory nasazené všude (viz varování výš). Na vývoj používej CMS_THEME_OVERRIDE.
  4. Projdi varianty zařízení — chybějící mobile spadne na desktop, což je v pořádku jen tehdy, když je desktopové rozvržení responzivní.

Jak přidat šablonu#

Nová šablona je adresář www/themes/frontend/<název>/ se stejnou strukturou jako ostatní (desktop/, případně mobile//tablet/). Vedle něj patří manifest theme.json:

{
    "label": "Přírodní kosmetika",
    "description": "Jemný e-shop s přírodní kosmetikou",
    "color": "#c9785a",
    "group": "eshop",
    "subpage": "/eshop"
}
Klíč Význam
label název v nabídce šablon
description krátký popis pod názvem
color barva kolečka / náhradní plochy, když chybí printscreen
group slug skupiny, viz níž
subpage adresa podstránky pro galerii šablony; čte ji jen bin/theme-previews.php

Pořadí v nabídce určuje název adresáře, přirozeně (template2 před template10), ne manifest — ThemeCatalog klíč order nečte. Texty manifestu nejsou překladové klíče schválně: chybějící překlad shodí stránku, chybějící manifest jen ubere hezký název.

Bez theme.json šablona v administraci chybí

Návštěvnický přepínač nabídne každý adresář s desktop/, bez manifestu pod názvem adresáře. Obrazovka Vzhled webu i generátor náhledů ale berou jen adresáře, ve kterých theme.json je — šablona bez něj se v administraci nedá vybrat.

group je slug ze slovníku App\Core\Theme\ThemeCatalog::GROUPS (eshop, classifieds, company, magazine, other), ne volný text. Název skupiny, který se zobrazí v administraci, je text CMS a musí být přeložený do cs/sk/en — volný text z manifestu by se do překladů nedostal. Neznámý nebo chybějící slug spadne do other, takže překlep v manifestu nikdy nezpůsobí pád obrazovky.

Náhledy pro administraci#

Obrazovka Vzhled webu potřebuje ke každé šabloně a jejímu skinu printscreen hlavní strany. Generuje je:

php bin/theme-previews.php              # všechny šablony
php bin/theme-previews.php template5    # jen jedna šablona

Skript vyfotí headless Chromem hlavní stranu lokálně běžícího webu na pevné adrese 127.0.0.1:8000 (třeba php -S 127.0.0.1:8000 -t www) a uloží:

  • <šablona>/preview.webp — základní barva,
  • <šablona>/previews/<skin>.webp — každá další barevná varianta.

Formát je WebP, ne PNG — táž sada náhledů měla v PNG 9,2 MB, ve WebP 1,4 MB.

Generátor nespouštěj, když se zrovna na webu pracuje

Vyfotí web přesně v tom stavu, v jakém zrovna je — rozpracované úpravy souborů nebo přepnuté téma v administraci se tak dostanou přímo do náhledu. Chyba se přitom nepozná automaticky: skript hlídá jen to, že Chrome doběhl a soubor vznikl, ne že snímek nezachytil chybovou stránku. Stalo se to a poznalo se to až okem.

Po předělání šablony náhledy přegeneruj

Negenerují se samy při buildu ani při deploy. Bez ručního spuštění skriptu administrace ukazuje starý vzhled.

Chybějící náhled není chyba — karta šablony nebo skinu se v administraci vykreslí i bez obrázku, místo něj barevná plocha z color v manifestu.

Synchronizace skriptů mezi tématy#

Frontové skripty (www/themes/frontend/templateN/desktop/assets/js) jsou v každé šabloně vlastní kopie téhož stromu. Oprava komponenty v jedné šabloně se proto do ostatních sama nedostane. Rozjeté kopie hlídá a sjednocuje bin/theme-js-sync.php:

php bin/theme-js-sync.php                              # jen ohlásí rozdíly (exit 1 = rozjeté)
php bin/theme-js-sync.php --canonical=template1        # totéž s jinou kanonickou šablonou
php bin/theme-js-sync.php --canonical=template1 --apply  # přepíše rozjeté kopie kanonickou

Kanonická šablona je ta, jejíž kopie se považuje za správnou. Bez --canonical je to template2 (výchozí hodnota ve zdroji skriptu), proto změněný skript komponenty rozešli vždy s výslovným --canonical= na šablonu, kde jsi ho opravil. Soubory, které se mají lišit záměrně (dnes pages/homepage.js), jsou v konstantě EXPECTED_DIFFERENT a --apply je nepřepíše. Skript neřeší SCSS ani obrázky.

Skiny#

Skin je barevná varianta jedné šablony: adresář <šablona>/desktop/assets/skins/<název>/ se souborem skin.css, který obsahuje jen přepis CSS proměnných (:root { --color-primary: …; }). Linkuje se za style.css, takže jeho tokeny vyhrají; layouty ani komponenty nemění. Cestu do šablony dává ThemeHelper::getSkinPath().

Vedle skin.css může ležet skin.json:

{ "label": "Cihlová", "description": "Teplá cihlová červeň", "color": "#ec3013", "order": 10 }
Klíč Význam Když chybí
label, description název a popis v nabídce název adresáře, bez popisu
color barva kolečka v přepínači poslední --color-primary ze skin.css
order pořadí skinů v nabídce, při shodě rozhoduje label 1000, tedy na konec
hidden true skin vynechá z nabídek skin se nabízí

Seznam skinů skládá SkinCatalog a za skin počítá jen adresář se skin.css. Nemá závislost na resolveru, protože ho potřebuje i administrace, kde resolver drží admin téma; cestu k šabloně proto dostává parametrem.

Postup: přidávám skin

  1. Založ desktop/assets/skins/<název>/skin.css a přepiš v něm jen tokeny. Pro tablet a mobil skin nekopíruj — ThemeHelper::getAssetPath() sáhne na desktopový soubor.
  2. Přidej skin.json s label, color a order. Bez color se barva kolečka hledá jen jako hex hodnota --color-primary ve skin.css; jiný zápis (rgb(), var()) nechá kolečko prázdné.
  3. Ověř přes ?_skin=<název> a v administraci v Nastavení → Vzhled webu.
  4. Přegeneruj náhledy (php bin/theme-previews.php <šablona>).

Chybějící skin neshodí web

Na rozdíl od šablony je skin bezpečná volba: resolver ho ověřuje až proti výslednému tématu (po přepnutí šablony) a název, který v té šabloně nemá adresář se skin.css (Theme::skinExists(), stejné pravidlo jako SkinCatalog), vrátí jako NULL, tedy základní barvy, ne odkaz na 404. Rozpracovaný adresář bez skin.css se tak nezapne ani z nastavení, ani přes ?_skin=.

hidden skin dál funguje

Zmizí jen z nabídky. Přímý odkaz ?_skin=<název> ani theme.frontend.skin v nastavení ho dál zapnou. Na zálohu barvy, která nemá být k výběru, je to správně; na skin, který nemá fungovat, ho smaž.

Kolečko se stejnou barvou se jen označí

ThemeCatalog::variants() vrací základní vzhled a za ním skiny; variantě, jejíž barva už v seznamu je, nastaví duplicateColor: true (vyhrává základní vzhled, pak nižší order). Nezahazuje ji: přepínač musí právě zapnutý skin ukázat i s duplicitní barvou, jinak by fajfka neměla u čeho být. O skrytí rozhoduje volající.

Strukturovaná data v hlavičce#

parts/shared/head/schema-org.latte (v každé z 18 šablon) vypisuje do hlavičky JSON-LD typu WebSite: @id a url z $baseUrl, name z $ogData->getSiteName(), description z $metaData->getDescription() a inLanguage z $ogData->getLocale().

Dřív se do stránky vypisoval nevyhodnocený text {latte:raw json_encode(…)}, protože blok měl n:syntax="off" — vyhledávač neviděl platná data. Nově se JSON generuje výrazem {=json_encode([...], JSON_UNESCAPED_UNICODE + JSON_UNESCAPED_SLASHES + JSON_HEX_TAG)|noescape} uvnitř <script type="application/ld+json">.

V tiskové značce Latte | začíná filtr

Příznaky json_encode se proto sčítají (A + B + C), ne spojují |. JSON_HEX_TAG zajistí, že < v názvu webu nebo popisu neuzavře element <script>.

Při přidání další šablony zkopíruj tuhle verzi

Starší tvar s n:syntax="off" do ní nepřenášej. Další typy (Organization, BreadcrumbList) zatím nejsou; v souboru je TODO na dynamické generování.

Postup: ověřit strukturovaná data#

  1. Zobraz zdroj stránky a najdi <script type="application/ld+json">. Uvnitř musí být platný JSON — žádné nevyhodnocené {=…} ani {latte:raw …}.
  2. Z příkazové řádky: curl -s https://vase-domena.cz/ | grep -o '<script type="application/ld+json">.*</script>'. Hlavní strana vrací například graf s jedním uzlem WebSite (@id je …/#website, inLanguage například cs-CZ).
  3. Pro kontrolu vyhledávačem vlož adresu stránky do testu Rich Results Test Googlu.

Prázdný description je v pořádku

Má-li stránka prázdný meta popis, je "description": "". Strukturu to nerozbije; doplň popis stránky v administraci.

Hledání šablony akce#

DefaultThemeResolver::formatTemplateFiles() vrací Nette kandidáty na šablonu akce v tomhle pořadí:

  1. téma, aktivní varianta: <varianta>/layouts/modules/<modul>/presenters/<presenter>/<akce>.latte (části názvu presenteru s malým počátečním písmenem, např. front/system/presenters/home/default.latte),
  2. totéž ve variantě desktop,
  3. Templates/<Presenter>/<view>.latte vedle presenteru v app/.

Seznam kandidátů nesmí být prázdný

Když se nic nenajde, Nette vezme $files[0], aby chybějící šablonu pojmenovalo v hlášce 404. Nad prázdným polem z toho je TypeError v strtr(), tedy HTTP 500 místo 404 u každé chybějící frontové šablony. Metoda proto kandidáty jen řadí podle existence a při úpravě nesmí začít filtrovat.

Hledání layoutu#

ThemeInitTrait::formatLayoutTemplateFiles() bere jméno layoutu ze stránky (cms_system_pages.layout, přihlášenému napřed loggedLayout, bez hodnoty @layout.latte) a hledá ho v <varianta>/layouts/ a pak v desktop/layouts/ aktivního tématu.

Sloupec layoutu je jeden pro všechna témata, takže stránka nastavená na @text_layout_two.latte chce tenhle soubor i v tématu, které ho nemá. Proto:

Situace Co se stane Log
layout v tématu je použije se —
layout v tématu chybí přidá se náhradní @layout_one.latte warning: „Layout '…' není v tématu …“
chybí i @layout_one.latte (nebo ho stránka chce rovnou) stránka se vykreslí bez layoutu, HTTP 200 error: „Náhradní layout … CHYBÍ“

@layout_one.latte (jeden sloupec, sdílená hlavička a patička) mají dnes všech 18 témat (spočítáno ls www/themes/frontend/*/*/layouts/@layout_one.latte). Náhradou není @layout.latte: ten je holá kostra Nette bez hlavičky, patičky a stylů.

Nové téma musí mít @layout_one.latte

Bez něj nemá kam spadnout žádná stránka s layoutem, který téma nezná. Taková stránka vrátí HTTP 200 bez hlavičky, patičky a stylů, takže ji monitoring dostupnosti nepozná; vidět je jen řádek error v logu.

Chybová stránka#

Chybová stránka frontu (404, 403, 410 a ostatní 4xx) se kreslí v aktivním tématu a jazyce požadavku přes Front:System:Error (ErrorPresenter). Přijde se na ni dvěma cestami: forwardem z Error4xxPresenter, když front presenter nebo router skončí BadRequestException, a přímo routou bez whitelistu stránek, kam spadne třeba /?slug=neexistuje.

Stránka v databázi není, layout ale entitu stránky potřebuje. loadPageEntity() proto vrací nepersistovanou Page s layoutem @layout_one.latte. HTTP kód nastavuje renderDefault() sám (přímá routa ho jinak nenastaví a neexistující adresa by vrátila 200), startup() vynuluje persistentní slug a podvržený jazyk v adrese spadne na hlavní jazyk webu.

Layout chybové stránky je vždy @layout_one.latte

Chybí-li v tématu, chybová stránka vyjde bez hlavičky, patičky a stylů. Vlastní kód z administrace se na ní nevykreslí (stránka nemá id, viz níž).

Chyba serveru (500) se v tématu nekreslí — téma ani databáze v tu chvíli nemusí fungovat. Je to samostatná jednoduchá stránka app/UI/Error/Error5xx/500.phtml v češtině, angličtině a slovenštině: jazyk určí předpona adresy (/en/, /sk/), pak hlavička Accept-Language prohlížeče, jinak čeština. Odkazuje na hlavní stránku dané jazykové verze. Vzhled ani texty se nenastavují v administraci; chcete-li je změnit, upravte přímo tento soubor (bez DI a překladače, jen čisté PHP a HTML).

Vlastní kód z administrace#

Knihovna Web → Stránky → Vlastní kód vkládá do stránek CSS, JS nebo HTML (měřicí kódy, chat, ověření domény). Na stránce běží přesně ty kódy, které jsou jí přiřazené (vazba cms_system_custom_asset_page_relations); celowebový rozsah neexistuje, „na všech stránkách“ je v administraci jen akce, která vazby ke všem stránkám vytvoří.

Komponenta frontSystemCustomAssets se v tématu volá jednou pro každé místo:

Volání Kde je ve všech 18 tématech
{control frontSystemCustomAssets 'head'} layouts/parts/shared/head.latte, uvnitř <head>
{control frontSystemCustomAssets 'body_start'} v každém layoutu hned za <body> (všech 26 layoutů tématu)
{control frontSystemCustomAssets 'body_end'} layouts/parts/shared/bottom.latte, před </body>

Všechna tři volání mají stejné jméno, takže dostanou tutéž instanci komponenty: kódy stránky se načtou jedním dotazem (loadAssets(), filtr vazby je v dotazu) a jen se rozdělí podle místa. Změna vazeb bumpuje cache třídy CustomAsset, výsledek tedy nezůstane viset.

Typ Co se vypíše
CSS <style>…</style>, z kódu se odstraní </style v jakékoli podobě
JS <script>…</script>, z kódu se odstraní </script; s vyplněnou kategorií cookies type="text/plain" + data-cookie-category, spustí ho cookie-bar.js až po souhlasu
HTML beze změny

Postup: nový layout tématu

  1. Vlož do layoutu hned za <body> volání {control frontSystemCustomAssets 'body_start'}.
  2. Ověř, že layout vkládá sdílené head.latte a bottom.latte (nesou místa head a body_end).
  3. Komponentu pojmenuj ve všech třech voláních stejně, žádný multicontrol.
  4. Přiřaď v administraci zkušební kód k jedné stránce a ověř ve zdroji stránky všechna tři místa.

Obsah se nesanitizuje

To je smysl knihovny. Ochranou je oprávnění autora (:Admin:System:CustomAsset:edit, přiřazení :Admin:System:CustomAsset:assign), ne kód. Odstranění uzavírací značky u CSS a JS jen brání vyskočit z elementu a dopsat do stránky libovolné HTML; typ HTML žádnou pojistku nemá a mít nemůže. Kdo by komponentu volal pro každé místo jiným jménem, dostane tři dotazy na každý požadavek.

Bez entity stránky se nekreslí nic

Kódy se hledají podle id stránky. Chybová stránka má nepersistovanou stránku bez id, takže vlastní kód na ní neběží.

Přístupnost#

Rozbalovací prvky dělej přes <details>, kde to jde

Funguje to bez JavaScriptu, čtečky si s tím poradí a nepotřebuje to žádné ARIA atributy navíc.

Skills pro práci se šablonami#

Skill Pro co
cs-template-layout rozvržení
cs-template-subpage podstránka
cs-template-from-image šablona podle obrázku
cs-template-from-sample šablona podle ukázky
cs-ui-audit kontrola hotového vzhledu

Kam sáhnout#

Chci Kde
výběr tématu, skinu a varianty app/Core/Theme/DefaultThemeResolver.php
nabídka šablon, theme.json, skupiny app/Core/Theme/ThemeCatalog.php
nabídka skinů, skin.json app/Core/Theme/SkinCatalog.php
cesta ke skin.css app/Core/Utils/Helpers/ThemeHelper.php → getSkinPath()
návštěvnický přepínač app/UI/Front/Base/Components/ThemeSwitcherComponent/
funkce themeAsset / themeUrl app/Core/Latte/Extensions/ThemeLatteExtension.php
aktivní front téma www/themes/frontend/template1/desktop/
katalog komponent www/themes/frontend/template1/desktop/ui-demo/
téma administrace www/themes/admin/default/desktop/, www/themes/admin/template1/desktop/
přepínač admin tématu app/UI/Admin/System/Components/ThemeSwitcherComponent/, DefaultThemeResolver::applyAdminOverride()
šablonu části komponenty podle tématu app/Core/Base/BaseComponent.php → resolveThemeTemplatePath()

Navazující kapitoly: Struktura SCSS · Design systém a tokeny · Šablony Latte · Komponenty

Nastavení cookies