Vzhled
Webový frontend
Webový frontend platformy Než zazvoní CMS je postaven na moderním aplikačním rámci Next.js 16 s architekturou App Router, React 19 a jazykem TypeScript. Aplikace se sestavuje do samostatného produkčního balíčku (standalone output) a běží v dockerovém kontejneru.
Jediná běžící instance aplikace dokáže obsluhovat neomezené množství škol. Každý příchozí požadavek dynamicky směruje na odpovídající školu podle domény nebo adresy v hlavičce Host. Vykreslování probíhá na straně serveru (Server-Side Rendering) s využitím datové mezipaměti Strapi REST API.
Kde co najdete ve struktuře kódu
Struktura aplikace ve složce web/ je členěna do přehledných vrstev:
app/: Stránky a šablony App Routeru (layout.tsx,page.tsx,[...path]/page.tsx,aktuality/page.tsx,hledat/page.tsx) a globální kaskádové stylyglobals.cssanz.css.proxy.ts: Vstupní vrstva požadavků zajišťující bezpečnostní hlavičky, generování nonce pro Content Security Policy, omezení počtu požadavků i přesměrování starých adres.lib/: Aplikační logika a pomocné funkce:tenant.tsaschool.ts: Rozpoznání a načtení školy podle hlavičky Host.cms.tsacontent.ts: Klientské funkce pro volání veřejného REST API a dotazy na stránky, články i kategorie.theme.tsafonts.ts: Výpočet CSS proměnných pro barvy a konfigurace písem.types.ts: TypeScript rozhraní pro datový model Strapi a komponenty sekcí.
components/: Vizuální komponenty webu rozdělené na základní prvky, sekce klasického designu (sections/) a komponenty moderního designového jazyka Než zazvoní (nz/).
Výběr školy podle domény
Mechanismus rozpoznání školy v modulu lib/tenant.ts (funkce findSchool) vyhodnocuje hlavičku Host každého příchozího požadavku podle následujících pravidel:
- Vlastní doména školy: Nejprve se ověřuje přesná shoda se záznamem
domainu některé ze škol v CMS (napříkladoapv.czi variantawww.oapv.cz). - Subdoména platformy: Pokud vlastní doména neodpovídá, systém zkontroluje, zda hostitel odpovídá formátu
<slug>.<SITE_BASE_DOMAIN>(napříkladoapv.lab.navit.cz). Tento tvar slouží pro testovací provoz, náhledy a školy bez vlastní domény. - Vývojové prostředí a místní provoz: Pro požadavky z adres
localhost, holé IP adresy nebo interní jednoslovné názvy kontejnerů se použije výchozí škola definovaná v proměnné prostředíDEFAULT_SCHOOL_SLUG.
Z bezpečnostních důvodů systém odmítá jakékoli subdomény třetího řádu na cizích doménách (například oapv.cizidomena.cz). Tím je vyloučeno, aby kdokoli zobrazil web školy pod cizí doménou bez schválení v konfiguraci platformy.
Bezpečnostní vrstva a přesměrování adres
Vstupní brána proxy.ts zpracovává každý požadavek ještě před spuštěním samotného vykreslování:
- Ochrana Content Security Policy (CSP): Pro každý požadavek vygeneruje unikátní kryptografický řetězec (nonce). Tento nonce předává inline skriptům frameworku Next.js. Politika přísně vymezuje zdroje médií na adresu CMS a povoluje vkládání externích rámců výhradně z Google Maps a domény YouTube bez cookies (
youtube-nocookie.com). - Omezení počtu požadavků (Rate Limit): Chrání webový server před přetížením. Výchozí limit je 1200 požadavků za minutu na jednu klientskou IP adresu.
- Trvalá přesměrování (HTTP 301) ze starého webu:
- Odkazy na články ze starého WordPressu ve tvaru
/slug-clanku/automaticky přesměruje na novou adresu/aktuality/slug-clanku(za předpokladu, že článek v CMS existuje a neexistuje stránka se stejnou adresou). - Odkazy na kategorie
/category/<slug>/přesměruje na výpis aktualit s filtrem/aktuality?kategorie=<slug>. - Adresy zakončené lomítkem přesměruje na tvar bez lomítka v jediném kroku.
- Odkazy na články ze starého WordPressu ve tvaru
- Zákaz indexace (Noindex): Při nastavení systémové proměnné
SITE_NOINDEX=1odesílá hlavičkuX-Robots-Tag: noindex, nofollow, což zabraňuje indexaci testovacích a předváděcích webů vyhledávači.
Designové vrstvy a vizuální styl
Platforma podporuje dvě souběžné designové vrstvy, které se volí v administraci v záznamu Škola:
- Klasický design (
klasik): Původní šablona platformy. Využívá základní komponenty záhlaví a zápatí (SiteHeader,SiteFooter) a sekce ze složkycomponents/sections/. - Design Než zazvoní (
nz): Moderní vizuální styl navržený pro školy. Využívá specializované komponenty ze složkycomponents/nz/(NzHeader,NzFooter,NzSections,NzPageHeader,NzAnnouncement) a kaskádové stylynz.css. Umožňuje zvýrazňovat slova v nadpisech sekcí barvou školy pomocí hvězdiček (Škola, která *učí praxí*).
Na následujícím snímku vidíte úvodní stránku školy v moderním designu Než zazvoní:

Pro srovnání si můžete prohlédnout stejnou školu v původním klasickém designu:

Nastavení barev a písem (Theming)
Grafický styl školy se neupravuje v kódu CSS, ale načítá se dynamicky ze záznamu školy v CMS:
- Barvy školy (
colorPrimary,colorAccent,colorDark,colorSurface) se aplikují jako CSS proměnné přímo na elementu<html>. - Skript
lib/theme.tsautomaticky dopočítává kontrastní odstíny, aby byla zajištěna přístupnost podle standardu WCAG 2.1 AA (kontrast textu na barevném pozadí i viditelnost linek tlačítek v poměru alespoň 3 : 1). - Písma nadpisů a textu se volí z 10 přednastavených rodin Google Fonts. Písma jsou uložena přímo na serveru (self-hosted) prostřednictvím balíčku
next/font, takže prohlížeč nestahuje data z externích serverů Google. - Volba zaoblení rohů (
radius:sharp,soft,round) ovlivňuje poloměr tlačítek, karet i vstupních polí.
Rozvržení stránek: běžná vs složená stránka
Aplikace rozlišuje dva typy rozvržení stránek:
- Složená stránka (
composed): Pokud je úplně první sekcí na stránce komponentasection.hero, stránka se vykreslí jako složená. Hero sekce nese hlavní nadpis H1, sekce se roztahují přes celou šířku obrazovky (full-bleed) a standardní záhlaví stránky s drobečkovou navigací se potlačí. Tento styl se používá pro úvodní stránku a klíčové prezentace oborů. - Běžná podstránka (
page): Stránka má standardní záhlaví s drobečkovou navigací a případně bočním menu podstránek. Jednotlivé sekce se řadí do čitelného centrálního sloupce.
Postup krok za krokem: Přidání nového typu sekce
Přidání nového typu sekce do platformy vyžaduje zásah v CMS, schématu i webovém frontendu. Postupujte podle následujících pěti kroků:
1. Definice komponenty ve Strapi
V adresáři cms/src/components/section/ vytvořte nový soubor schématu, například banner.json, s definicí požadovaných polí (texty, odkazy, obrázky). Novou komponentu poté přidejte do seznamu dynamické zóny sections v souboru cms/src/api/page/content-types/page/schema.json.
2. Doplnění českých popisků v administraci
V souboru cms/src/cms-labels.ts přidejte záznam do objektu COMPONENTS:
typescript
'section.banner': {
mainField: 'title',
fields: {
title: ['Nadpis', 'Hlavní sdělení banneru.'],
text: ['Text', 'Doplňující informace.'],
image: ['Obrázek'],
},
},3. Rozšíření schématu site.json a validátoru
V souboru tools/site.schema.json přidejte definici nové sekce do objektu $defs/sec/banner a zařaďte odkaz na ni do seznamu oneOf v objektu $defs/section. Importér import-site.js sekci zpracuje automaticky díky generickému překladu podle schématu Strapi.
4. Úprava API dotazů a typů v Next.js
V souboru web/lib/content.ts doplňte načítání vnořených dat v objektu PAGE_QUERY:
typescript
'populate[sections][on][section.banner][populate][image]': true,V souboru web/lib/types.ts vytvořte TypeScript rozhraní SectionBanner a přidejte jej do sjednocení typů Section.
5. Vytvoření frontendového rendereru
Ve složce web/components/sections/ vytvořte komponentu Banner.tsx. Zaregistrujte ji v přepínači SectionBody v souboru web/components/Sections.tsx pro klasický vzhled a v souboru web/components/nz/NzSections.tsx pro design Než zazvoní. Případné kaskádové styly doplňte do souborů globals.css a nz.css.
Tipy a doporučení
Dodržujte pravidla přístupnosti
Každá sekce na webu musí být obalena sémantickým tagem <section> a mít jednoznačně přiřazený identifikátor nadpisu pomocí atributu aria-labelledby.
Využívejte CSS proměnné
Při tvorbě nových komponent nikdy nezapisujte barvy natvrdo v HEX kódech. Vždy používejte připravené tokeny motivu, například var(--color-primary) nebo var(--color-accent). Komponenta se tak automaticky přizpůsobí barvám jakékoli školy.
Zabezpečení externích odkazů
Všechny odkazy vedoucí mimo doménu školy musejí v HTML kódu obsahovat bezpečnostní atribut rel="noopener noreferrer".

