Decyzje projektowe — SEO
- Gate jest pierwszy, bo blokuje build. Tabela GateTable zajmuje lewą kolumnę nad zgięciem; Surface preview po prawej jako kontekst „jak to wygląda w Google”. Sitemap i llms.txt pod zgięciem — to dowody, nie decyzje.
- Blocker = chip krytyczny z ikoną „×” i tekstem z kodu (
missing description, dosłownie jak w seo-gate.ts). Nie tłumaczymy nazw blockerów — operator grepuje je w logu builda.
- Rekord bez blockerów nie ma badge'a. Cisza = muted check + „bez blockerów”. Zielony pojawia się tylko na StatusTile z dowodem (
seo-gate, sitemap-urls, generateLlmsTxt) i znacznikiem LIVE · hh:mm.
- llms.txt niesie notkę „nie jest ranking gate” w treści karty, nie w tooltipie — żeby klient nie traktował go jako KPI. Przycisk „Kopiuj” zamiast edycji: plik jest generowany.
- Hreflang = karta z przerywanym obrysem i statusem „deferred (PL-only)” ze źródłem
globals.locales. Nie znika z IA (ta sama IA w obu trybach), ale nie udaje aktywnej funkcji.
- Licznik znaków w SERP-card (title 61/60 → warn tylko na liczniku). Kolor statusu wyłącznie na liczniku, tytuł zostaje w
--blue jak w wyniku wyszukiwania.
Co jest badge'em, co ciszą
| Sygnał | Forma | Dowód |
| Rekord z blockerami | chip .blk per blocker, --critical | runSeoGate().blockers[] |
| Rekord OK | cisza: muted check „bez blockerów” | ok: true |
| Gate ogółem | StatusTile critical + badge „3 z blockerami · LIVE” | agregat gate |
| Sitemap / llms.txt | StatusTile ok + LIVE · 03:07 | dist/sitemap.xml, dist/llms.txt |
| Hreflang | StatusTile unknown (przerywana krawędź) „deferred (PL-only)” | globals.locales |
| Gate nieudany | error-panel + log + retry; poprzedni wynik jako STALE | exit code ≠ 0 |
| Brak tokenu | blocked-banner „BLOCKED · UNVERIFIED”, kafle w trybie dim | DIRECTUS_TOKEN unset |
Dane LIVE / UNVERIFIED na planszy
| Element | Źródło | Etykieta |
| tenant, sites, mode, pluginy | modules.json, clients.json (BRIEF §6) | LIVE |
| slugi artykułów i autorów | repo fixtures/ · BRIEF §6 | LIVE |
| nazwy blockerów | packages/seo/src/seo-gate.ts | LIVE |
| tytuły, description, daty publikacji | przykład redakcyjny | UNVERIFIED |
| liczba URL w sitemap (11), czasy 03:07 | snapshot planszy | UNVERIFIED |
| format llms.txt (nagłówki, sekcje) | packages/seo/src/llms-txt.ts | LIVE |
Tokeny — decyzja koloru akcentu
--accent-op = ciemny miód (jasny motyw) / jasne złoto (operator). Różni się od --warn (ochra) jasnością, nie tylko odcieniem — po desaturacji primary CTA i badge warn nie zlewają się. --accent-cl = głęboki granat (jasny) / jasny błękit (operator), różny od --teal. --purple zadeklarowany, nieużyty.
--accent-op
--warn
--accent-cl
--teal
--ok
--critical
--blue
--muted
Użycie --accent na planszy: aktywna pozycja menu systemu, primary CTA, focus ring. Nic więcej. Aktywna zakładka i toggle „on” idą przez --text, żeby akcent nie konkurował ze statusem.
Shell — AIONLINE-CONTRACT
Menu systemu 244 px po lewej (brand „AIOS Admin”, tenant switcher tylko dla operatora, 12 pozycji IA §1, stopka: ModeBadge + build). AionLine 56 px po prawej: systemy Hub · Discovery · Panel · Create, inbox ekosystemu, obecność, profil, wyloguj, rozsuń. Topbar produktu bez dzwonka i profilu. Commerce w menu z kłódką i tooltipem „kill-gate Discovery”.
Board 01 = rola client/owner (jasny, tenant statyczny, brak akcji operatorskich). Board 02 = operator (data-theme="operator", switcher tenantów, pełne akcje). Ta sama IA w obu — różnią się tylko uprawnieniami.
Komponenty użyte (BRIEF §5) i hierarchia
.status-tile ×4 (pasek) · .gate-table (Gate) · .tabs (filtry gate, SERP/JSON-LD/OG) · .skeleton (loading) · .empty-checklist (empty) · .blocked-banner (blocked) · .mode-badge (stopka menu) · .stat-card = alias .status-tile. Nieużyte celowo: .conceal (brak sekretów w SEO), .kill-lock (tylko menu Commerce), .chart-bars, .event-stream, .diff-panel, .drawer.
Hierarchia: h1 26 px bold → StatusTile (mono 28 px) → tytuły sekcji 15 px/650 → treść 13 px → meta 11 px. Dwa poziomy zagnieżdżenia: karta na tle, tabela/lista w karcie. Obrys 1 px tylko na wierszach, przyciskach, polach, pickerze. Odstęp między sekcjami 20 px w kolumnie, 36 px między blokami logicznymi (pasek → grid). Operator (02) ma identyczny układ — różnica to switcher tenantów i akcja „Otwórz Directus”.