Téma je priečinok v resources/themes/<slug>/ s React sekciami, JSON schémami nastavení a jedným registrom, ktorý ich spája. Tento článok je pre vývojára, ktorý stavia novú tému alebo upravuje existujúcu — ako obchodník sekcie v hotovej téme nastavuje, opisuje Editor témy — základy a Referencia sekcií editora témy.
1. Štruktúra priečinka
resources/themes/<slug>/
theme.json # meno, verzia, dostupné layouty, CSS/JS assety, settings_schema (globálna paleta a písma)
registry.ts # jediný súbor, ktorý mapuje section type → React komponent
layouts/*.json # zoznam sekcií pre danú stránku (home, product, cart, checkout, ...)
sections/*.json # schéma nastavení KAŽDÉHO typu sekcie (settings + volby)
sections/<Name>/ # React implementácia (index.tsx = komponent, žiadny samostatný "VariantA.tsx" wrapper)
templates/ # celostránkové šablóny (napr. ProductsListing)
pages/ # stránky bez sekcií (Cart, Checkout, Account) — často znovupoužité z @shop/core
menus.json # predvolené menu
assets/ # theme.css a ďalšie statické súborylayouts/*.json určuje poradie a nastavenia sekcií na danej stránke; sections/*.json určuje, aké nastavenia sekcia vôbec ponúka (typ poľa, predvolená hodnota, možnosti výberu). Editor témy v administrácii oba súbory spája — layout ukladá hodnoty, schéma kreslí formulár.
2. Registrácia cez createRegistry
registry.ts sa musí stavať cez createRegistry() z @shop/core — nie ako obyčajný exportovaný objekt:
import { createRegistry } from '@shop/core/createRegistry';
import Header from './sections/Header/index';
import Hero from './sections/Hero/index';
// …
export default createRegistry({
sections: { header: Header, hero: Hero /* … */ },
templates: { /* … */ },
pages: { /* … */ },
});createRegistry() doplní každú povinnú šablónu/slot/stránku z @shop/core, ktorú téma sama nedefinuje — takže chýbajúca položka nezostane nepovšimnutá. Bez neho by chýbajúci záznam zostal len detegovateľný (ThemeRenderer vykreslí prázdnu sekciu namiesto chyby), nie nemožný. Kontrakt kontroluje test resources/js/__tests__/themeRegistryContract.test.ts, spúšťaný v npm test — nie build (vite build len odstraňuje typy, typová chyba mu neprekáža).
Pozor: Import komponentu vždy so/indexna konci (./sections/Header/index, nie./sections/Header). Na macOS (case-insensitive súborový systém) sa bez neho môže cesta zhodnúť ssections/header.jsona naimportuje sa schéma namiesto komponentu.
3. Preklady v sekciách
Textové pole, ktoré má byť preložiteľné, nesie vedľa seba pole <pole>_translations s kľúčmi podľa locale:
{
"search_placeholder": "Vyhľadať produkt...",
"search_placeholder_translations": {
"cs": "Vyhledat produkt...",
"en": "Search products..."
}
}SectionLocalizer na storefronte vymení hodnotu podľa aktívneho jazyka zákazníka. Ak pole prekladateľné pole má, ale niektorá jeho inštancia sibling _translations nemá, alebo pokrýva iný súbor jazykov než ostatné preklady na stránke, čitateľ v danom jazyku dostane pôvodný (zle prepnutý) text — ticho, bez chyby.
4. Guard skripty (musia prejsť pred zaradením zmeny)
| Skript | Čo kontroluje |
|---|---|
php scripts/theme-variant-guard.php | jednodizajnové témy (nutrition-pro, power-supplements, velur) nesmú mať v žiadnej sekcii pole variant — pozri §5 |
php scripts/theme-i18n-guard.php | každé prekladateľné pole má _translations všade, kde sa objaví, a všetky pokrývajú rovnakú množinu jazykov |
php scripts/theme-integrity-guard.php | každá sekcia v layouts/*.json, každá schéma v sections/*.json, každý inzerovaný variant a (pri ručných registroch) celý pages kontrakt sa dá naozaj vykresliť |
php artisan theme:validate <slug> | statická kontrola kontraktu sekcia/registry/layout bez potreby tenanta alebo prehliadača |
5. Jednodizajnové témy (Nutrition Pro, Power Supplements, Velur)
Tieto tri témy sú zámerne jednodizajnové — sekcia nemá prepínač variantu, index.tsx JE komponent (žiadny VariantA.tsx wrapper okolo). Pridanie poľa variant do schémy takejto sekcie je regresia, nie funkcia navyše, a theme-variant-guard.php na to spadne.
6. SSR build
Po každej zmene .tsx súboru v téme (sekcia, šablóna, stránka) spustite:
npm run build:ssra reštartujte SSR daemona. Bez toho SSR naďalej vracia starý strom komponentov — na storefronte sa to prejaví ako nesúlad medzi serverovým a klientským vykreslením (hydration mismatch) pri prvom načítaní.
7. Príkazy pre prácu s témami
# Nová téma naklonovaná z existujúcej (predvolene zo `space`)
php artisan make:theme acme --display="Acme Shop" --from=space
# Export témy jedného obchodu (nastavenia, layouty, menu, obrázky) do .zip
php artisan theme:export moj-eshop --output=storage/app/theme-exports/moj-eshop.zip
# Export vrátane katalógu a CMS obsahu (pri odovzdávaní hotového klientskeho buildu)
php artisan theme:export moj-eshop --with-content
# Import bundlu do cieľového obchodu
php artisan theme:import cielovy-eshop storage/app/theme-exports/moj-eshop.ziptheme:import --with-content zmaže existujúci obsah cieľového tenanta a nahradí ho obsahom z bundlu — príkaz odmietne bežať, ak cieľový tenant už má objednávky, práve preto, že ide o nezvratnú operáciu vhodnú len pre obchod, ktorý ešte nezačal predávať.
Časté problémy
- Sekcia sa v administrácii nezobrazí, hoci je v layout JSON. Skontrolujte, či je zaregistrovaná v
registry.tspresne pod rovnakýmtype, aký má vlayouts/*.jsonasections/*.json—ThemeRendererchýbajúcu položku vykreslí ako prázdnu, nie ako chybu. - Zmena vzhľadu sa neprejavila po nasadení. Chýba
npm run build:ssra reštart SSR daemona po zmene.tsxsúboru. - `theme-variant-guard.php` padá na jednodizajnovej téme. Niekto pridal pole
variantdo schémy sekcie tejto témy — odstráňte ho, dizajn je zámerne jeden. - Čeští zákazníci vidia slovenský text uprostred stránky. Prekladateľné pole má chýbajúci alebo neúplný
_translationsblok — doplňte ho pre všetky jazyky, ktoré má obchod aktívne. - `theme:import --with-content` odmietne spustiť sa. Cieľový tenant už má objednávky — operácia je určená len pre obchod, ktorý ešte nezačal predávať.