Binding rules of the canon, rendered from RULES.md (authored in Czech).

Pravidla projektu — Datamart & CRM

0. Jednoduchost především — „containment budget" (META-PRAVIDLO)

Tohle pravidlo je nad všemi ostatními. Cílem není mít hodně prvků — cílem je mít právě jeden, a ten perfektně vyladěný (Apple/Attio). Méně rámečků = víc přehlednosti a prostoru.

Containment budget — maximálně 1 úroveň rámování. Rám (okraj / pozadí / dlaždice) si MUSÍ zasloužit místo jako skutečná hranice: krok onboardingu, input, pravý preview panel, opravdový overlay. Jinak ne.

  • Nikdy rámeček kvůli „uspořádání" seznamu. Položky seskupuj prostorem + hierarchií (váha / barva / velikost), ne krabičkami. Seznam = ikona + text oddělený mezerou — žádný okraj kolem každého řádku.
  • Ikona je ikona, ne dlaždice. Žádné pozadí/okraj kolem ikony jen aby „seděla". Barevná ikona, hotovo.
  • Zákaz „rámeček v rámečku v rámečku". Když je obsah už uvnitř karty/zóny, jeho položky jsou ploché.

Proč to píšu (a proč se to opakovalo): instinkt „seskup → zabal do boxu; ikona → na dlaždici" se vrací u každé nové obrazovky. DS dosud popisoval jen slovník (tokeny + jak nakreslit okraj), ne gramatiku zdrženlivosti (kdy okraj NEpřidat). Bez viditelného anti-patternu a bez kontroly před publikací ten habit vždycky vyhrál. Tahle sekce + DO/DON'T karta + checklist to mění z prózy na vynutitelnou bránu.

Povinná self-review PŘED publikací KAŽDÉ obrazovky:

  1. Spočítej úrovně zanoření (karta → zóna → položka → …). >1 dekorativní rám = STOP a zploštit.
  2. U každého okraje/pozadí/dlaždice řekni jednou větou „jakou skutečnou hranici tvoří". Když to nejde → pryč.
  3. „Šlo by to jen prostorem a hierarchií?" Pokud ano → udělej to tak.

Referenční anti-pattern: karta Layout · Containment (DO / DON'T). Referenční flat implementace: Registrace · Propojení Google + _ds/auth.css.

0.1 Portuj z reálné appky — NEimprovizuj (META-PRAVIDLO)

Zdroj pravdy je reálná appka apps/web/src/, ne obrázek před tebou. Než postavíš nebo přestyluješ JAKÝKOLIV prvek (dialog, picker, ovládací prvek, karta, seznam, tabulka), nejdřív prohledej appku:

  • apps/web/src/components/*.tsx + balíček @acrm/ui (komponenty), apps/web/src/ds.css (sdílené .acrm-* třídy) a apps/web/src/ds/*.css (screen vrstvy), tokeny v packages/ui/src/tokens/tokens.css. Grepuj podle názvu funkce (SaveSegment, IconPicker, seg-ic, dlg-, segpick…).

Když kanonická implementace existuje → portuj ji 1:1 do bundle: stejné názvy tříd, stejné velikosti, stejná paleta (tokeny / tone-třídy, NIKDY natvrdo hex), stejná struktura i chování. CSS zkopíruj doslova ze zdrojové vrstvy (ds.css / ds/*.css).

Zakázáno: paralelní třídní systém (.ssg-*, ad-hoc hex, vlastní velikosti) pro něco, co appka už řeší; natvrdo barvy tam, kde jsou --ic-* / pill tóny / tone-třídy; velikost dlaždice „od oka" (segment tile = 20 px v navigaci/Home, 40 px editovatelný picker trigger v save dialogu).

Teprve když v appce NIC není, navrhuj nově — i tak s DS tokeny a nejbližším existujícím vzorem. Krok „prohledej appku jako první" je povinný a jde PŘED prvním řádkem kódu. (Doloženo: Save dialog jsem improvizoval místo portu save-segment-dialog.tsx + segment-icon-picker.tsx → muselo se zahodit.)

1. App Shell je MASTER pro všechny obrazovky (bez výjimky)

Pravidlo. App Shell (_ds/app-shell.js + _ds/app-shell.css, komponenta DS.AppShell) je jediná master šablona aplikačního rámu. Každá obrazovka, kterou aplikace obsahuje, se vykresluje skrz DS.AppShell — topbar (logo · přepínač Datamart/CRM · hledání · notifikace · účet) a levou navigaci nesmí žádná obrazovka kreslit sama. Strukturální změna v App Shell se musí promítnout do všech obrazovek automaticky.

Jak to dodržet:

  • Obrazovka renderuje <DS.AppShell mode="datamart|crm" active="<nav-key>" …>{obsah}</DS.AppShell> a do DS.AppShell vkládá pouze svůj obsah (centrální plocha) a případně drawer (pravý náhledový panel).
  • Sdílené obrazovkové enginy jdou taky přes shell:
  • DS.SegmentScreen (tabulky Lidé/Firmy, Průvodci) vykresluje obsah do DS.AppShell (viz _ds/segment-screen.js).
  • DS.PersonDetail (detail osoby) vykresluje do DS.AppShell (viz _ds/person-detail.js).
  • Žádné lokální gbar / sidebar / pd-gbar rámy v jednotlivých souborech. Každá obrazovka musí načíst _ds/app-shell.css + _ds/app-shell.js.
  • Drawery (record / preview) se dokují do slotu .as-drawer shellu — elevaci (stín) určuje jen slot, jednotlivé drawery vlastní stín nemají.

Hranice pojmu „obrazovka". Obrazovka = navigovatelný pohled aplikace (Domů, Pipeline, Lidé, Firmy, Company/Deal/Person drawer, Person detail, Průvodci). Referenční katalogy stavů a komponent (Prázdné stavy, Chybové stavy, Loading, Notifikace, Toasty) nejsou obrazovky — dokumentují stavební prvky v izolovaných rámech; samotné stavy (loading/empty/error) se v reálných obrazovkách už dnes zobrazují uvnitř shellu.

Výjimka — plnoplošné brány mimo shell. Registrace / login / onboarding a podobné fokusní brány (před vstupem do aplikace) shell NEpoužívají; řídí se pravidlem 4 (čisté plochy). Vše ostatní jde přes shell.

2. Bonita — barvy odvozuj z ratingu (DS tokeny)

Barva bonity se odvozuje z písmena ratingu, nikdy se nezapisuje natvrdo u dat. Kanonická mapa (zdroj pravdy: DS.SegData.BONITA_BANDS):

Tón (DS token)Ratingy
green (--bonita-green)AAA, AA
lime (--bonita-lime)A, BBB
amber (--bonita-amber)BB, B
rose (--bonita-rose)CCC, CC, C
grey (--bonita-grey)D

Helper: DS.gradeTone(grade) (v _ds/segment-screen.js). Vizuál = tlumená „pill" .grade <tón> (světlé pozadí + barevný text z DS tokenů) — stejně v tabulkách, scale i detailech.

3. Avatary — DS styl (hashHue)

Avatary používají DS treatment: světlé pozadí --surface-75, 1.5px barevný okraj a barevný iniciál z hsl(var(--av-hue) …), kde --av-hue = hashHue(jméno). Kruh pro osoby, zaoblený čtverec pro firmy. Žádné plné syté pozadí s bílým iniciálem.

4. Čisté plochy — bílé pozadí, tenký rámeček, vzdušné (Attio)

Fokusní plochy mimo aplikační shell (přihlášení, registrace, onboarding kroky, dialogy) musí působit čistě a vzdušně jako Attio:

  • Pozadí = bílá (--surface-100). Šedý --shell-bg je vyhrazen JEN pro aplikační rám (topbar + sidebar), nikdy pod fokusní obsah.
  • Každý krok ohranič tenkým jemným rámečkem — 1px solid var(--border), --radius-xl, velký a vzdušný (štědrý padding). Přesně jako kroky onboardingu v Attio. (Tohle Štěpán chtěl — rámeček ano, jen tenký a bez stínu.)
  • Žádný stín — vůbec. Rámeček = jen tenká linka (box-shadow: none). Drop-shadow (--shadow-pop) patří jen skutečným overlayům (pop-upy, menu, drawery), nikdy fokusním kartám.
  • Vzdušnost přes DS spacing (--sp-*): štědré mezery, méně prvků, restraint (Apple/Attio). Hierarchie váhou + barvou.
  • Vnitřní prvky (inputy, řádky zdrojů, preview panely) mají jemný hairline --border nebo tlumené pozadí --surface-75.

Referenční implementace: _ds/auth.css (.au-card) + karty Registrace · *.

5. Tlačítka — vždy DS tlačítko v jeho definované velikosti

Tlačítka jsou jediná DS komponenta (Components · Buttons): kanonická třída .acrm-btn v apps/web/src/ds.css (.btn je alias), v Reactu ji emituje @acrm/ui <Button>. Žádná obrazovka si nevyrábí vlastní (větší) tlačítka ani nepředefinovává .btn per-screen.

  • Velikost = --control-h (≈28–30px), label 12–13px, --radius-sm. Nikdy nevymýšlej „velká" 40–44px tlačítka pro login/marketing. Plnou šířku řeš width:100%, ne zvětšováním výšky.
  • Varianty: primary = --accent + okraj --accent-press (label 500 12px Inter); default/sekundární = bílé + okraj --line-300 + --shadow-sm; ghost = transparentní (hover --hover-surface). Google = default look + G.
  • Tvar = rounded-square (--radius-sm/md); nikdy pill/kruh (ty jsou jen pro avatary, status-dots a pill-badge).

6. Titulky a podtitulky — JEDEN zdroj jsou type tokeny

⛔ Verzálky (ALL CAPS) jsou globálně zakázané. Nikdy nepoužívej uppercase „eyebrow"/overline styl sám od sebe — pouze když si o to uživatel explicitně řekne. Platí pro titulky, labely, tlačítka, menu, názvy sekcí v navigaci i mikro-eyebrow. text-transform: uppercase se v UI nepoužívá. (Zdroj: DS „Psaní titulků a popisů" — „Uppercase overline nepoužíváme".)

Pravidlo. Každý titulek a podtitulek obrazovky se odvozuje z type tokenů — nikdy se nezapisuje natvrdo (font: 600 15px …). Změna měřítka v jednom místě (token) se promítne na všechny obrazovky.

Zdroj tokenů = kód: packages/ui/src/tokens/tokens.css na origin/main. legacy/colors_and_type.css je export, ne kánon — čti ho jen jako historii.

RoleTokenPoužití
Page title / dashboard hero--h1 (Geist 500 16/1.2)největší, který ship­uje — pozdrav na Domů, hlavní nadpis stránky
Primární titulek--h2 (Geist 500 15/1.25)titulek segmentu, jméno v draweru, titulek sekce, view bar
Podnadpis--h3 (Geist 500 13/1.35)menší nadpis uvnitř sekce
Eyebrow / overline--section · --overlinedrobný štítek nad titulkem — sentence case, NIKDY verzálky
Popisek pole--label (Geist 500 12/22)popisek nad/vedle vstupu
Nápověda pod polem--hint (Geist 400 12/1.45)vysvětlení, ne hodnota — proto 400 (AD 26. 8. 2026)
Podtitulek--subtitle (Geist 400 13/1.5)doplňková řádka pod titulkem, vždy --fg-secondary

Rodina je jedna: Geist (--font-sans i --font-display), plus Geist Mono výhradně v roli níže. Manrope ani Inter už v systému nejsou — od 29. 8. 2026 (!2571). --tracking-tight je 0: Geist se záporný tracking nedopisuje.

Sdílené třídy (v colors_and_type.css): .ds-page-title, .ds-section-title, .ds-subtitle, .ds-eyebrow. Použij je (nebo tokeny) na každém novém titulku.

Pravidla obsahu (DS „CONTENT FUNDAMENTALS"):

  • Sentence case — nikdy Title Case pro titulky/sekce/tlačítka/menu.
  • Bez tečky na konci titulku (titulek je štítek, ne věta).
  • Max 16px — hierarchii tvoří váha + barva, ne velikost. Žádné 21/26px display nadpisy uvnitř aplikace.
  • Váhy = 400 / 500 / 600, nic víc (ruling 6. 7. 2026, karta Type · Weights): 600 je strop, váhy 700+ se nepoužívají; native <b>/<strong> je přemapováno na 600. Headingy samotné jedou 500 (--h1/--h2/--h3) — 600 zbylo na --section a na zvýraznění uvnitř textu. Hierarchii dělá váha a barva, ne velikost.
  • Nadpis sekce BEZ ikony (AD 18. 9. 2026, ACRM-359) — .sb-h uvnitř detailu (schůzka, projekt, obchod, osoba…) nese jen text + počet + akce. Ikona před nadpisem sekce („✓ Rozhodnutí“, „⚠ Námitky“) je dekorace, která se sčítá přes celou obrazovku; ikony patří řádkům a akcím, hierarchii nadpisů dělá váha a barva.
  • Podtitulky ÚSPORNĚ — jen když nesou informaci navíc. Žádné dekorativní „výplňové" podtitulky pod každým titulkem.
  • Eyebrow/overline = sentence case, NIKDY verzálky. ALL CAPS, kapitálky ani Title Case nepoužíváme nikde — ani na mikro-eyebrow, ani na názvech sekcí v navigaci. Hierarchii dělá váha a barva, ne velikost ani velikost písmen.

6.1 Monospace — jen finanční výkazy, kód a klávesy

Rozhodnutí Štěpána 31. 8. 2026 (ACRM-34), potvrzené 2. 9. 2026: „Jediné místo, kde monospace používáme, jsou finanční výkazy." — „souhlasím s AD, odstraň to mono, kde to jde."

Mono ANOúčetní výkazy (kódy účtů, částky; v appce scoped .acc-page/.acc-inv), doslovný kód, klávesové zkratky (kbd), identifikátory ke zkopírování (ID workflow, kódy majetku, QR štítky)
Mono NEdatové tabulky, meta a termíny v railu, datumy a časy, počty a odznaky, popisky grafů, názvy

Kritérium, když je to hraniční: mono má smysl tam, kde mřížka glyfů něco znamená — sloupec, který se má zarovnat, nebo doslovný kód. Jednociferné číslo v pilulce nemá s čím lícovat, takže je to dekorace. Sloupce čísel se zarovnávají přes font-variant-numeric: tabular-nums na sans, ne přes rodinu písma (a tabulkové číslice nikdy u dat a časů — datum je text, ne číslo; AD 22. 7. 2026).

Čas, který se nesmí posouvat, bez tabular-nums (zadavatel 26. 9. 2026, přehrávač přepisu schůzky — apps/web/src/components/meetings/TranscriptPlayer.tsx, apps/web/src/ds/meeting-record.css .player .tm): proporcionální text má u každé číslice jinou šířku, takže odpočet vedle sebe „poskakuje" a stopa přehrávače se s ním hýbe. Řešení NENÍ tabular-nums (ten je u času zakázaný výše) — buňka dostane pevnou šířku přes neviditelné vzory nejširších číslic: display: inline-grid, viditelná hodnota a dva aria-hidden vzory se stejným textem nahrazeným 0 a 8 (nejširší číslice písma) sdílejí grid-area: 1 / 1, takže šířku buňky určuje nejširší z nich a viditelný text zůstává kanonický proporcionální text, jen s pevnou kotvou.

Proč se to rozlezlo (a proč to hlídat): do 29. 8. 2026 mapoval --font-mono na Inter, takže monospace fakticky neexistovalo a psát ho kamkoli bylo bez následku — zažilo se to jako „zvýrazni číslo". !2571 (Geist) ho přemapoval na skutečný Geist Mono a tím se mono tiše obnovilo na ~100 místech naráz. Není to sto rozhodnutí, je to jedno rozhodnutí stokrát zopakované. Odklizeno v appce 9 MR (!2770–!2805), v zrcadle !2724.

Výjimka — plnoplošné brány mimo shell. Auth/onboarding (_ds/auth.css, .au-title / .au-sub) mají vlastní vzdušnější měřítko — řídí se §4, ne 16px capem. Je to dokumentovaná výjimka, ne licence pro velké nadpisy v appce.

Referenční anti-pattern (vše už přepojeno na tokeny): lokální .home-greet 26px, .sgb-title 14px, .vtitle / .mtitle / .pipe-nm / .pdr-top-name 700 15px.

7. Navigace — IA žije v App Shell (jeden zdroj)

Pravidlo. Informační architektura levé navigace je jediná konstanta NAV v _ds/app-shell.js (+ ROUTES, PARENT). Žádná obrazovka nekreslí ani nedefinuje navigaci sama — vše jde přes DS.AppShell (viz §1). Změna IA v app-shell.js se promítne do všech obrazovek automaticky.

  • Typy uzlů: row (řádek nejvyšší úrovně) a section (rozbalovací sekce); v sekci jsou item (záznam / segment / průvodce) a action („+ nová věc", přerušovaný okraj). Vzor 1:1 = templates/sidebar-nav/SidebarNav.dc.html.
  • Sekce jsou sentence case (Záznamy · Segmenty · Pohledy) — NIKDY verzálky. Hlavička je klikací (sbalí/rozbalí): chevron vlevo + jemná modrá count kapsle (--accent-tint / --accent-ink) vpravo.
  • Badge: počet na hlavičce sekce = jemná modrá kapsle (--accent-tint / --accent-ink, tabular-nums). Notifikační badge na řádku = plný --accent, bílé číslo. Záznamové řádky badge nemají.
  • Klíče active jsou kontrakt. home · lide · firmy · sro · praha · kraje · brno · lide-pruvodce · firmy-pruvodce · pipeline · moje · potermin · riziko — při úpravě IA je zachovej, jinak se rozbije zvýraznění aktivní položky i routing.

8. Section top bar — uzavřený slovník prvků (jen povolené)

Pravidlo. Horní lišta sekce (content toolbar pod App Shellem, na každé obrazovce) smí obsahovat POUZE prvky definované v kartě Components · Section top bar. Žádné jiné. Slovník je uzavřený — co tam není, na lištu nepatří.

Povolené prvky (a nic víc):

  1. Identita vlevo — ikona + název sekce. Buď plochá ikona (--fg-secondary) + název, přepínač (.pipe-btn: dlaždice/ikona + název + chevron, otevírá menu), nebo kruhový avatar (.nav-pav) u detailu záznamu. Jen jedna identita, vlevo.
  2. Oddělovač .tdiv — svislá linka 1 px / 20 px mezi skupinami ovládání.
  3. Ovládací chip .tchip — rounded-rect (radius-md), v něm: ikona + volitelně .k (tlumený štítek) + .v (hodnota, bold) + .chev (chevron, otevírá menu). Tohle je jediný prvek pro zobrazení / řazení / filtr / rozsah. Aktivní stav = modrý okraj (.tchip.on), ne výplň.
  4. Výplň .vspace — pružná mezera (flex:1), odtlačí akci doprava.
  5. Tlačítko .btn / .btn.primary — vpravo; maximálně jedna primární (plná modrá) akce na obrazovku.

Zakázáno (časté prohřešky):

  • Pilulky (.chip, border-radius: pill) jako filtry/taby v liště. Pill se v Section top baru nevyskytuje — filtr/přepínač je vždy .tchip s chevronem a rozbalovacím menu (vzor = přepínač zobrazení v kartě).
  • Jiná značka identity, než jakou má táž položka v navigaci. Rozhodnutí zadavatele 27. 9. 2026: identita v liště = TATÁŽ značka jako položka v menu (nav-catalogue.ts) — řádek s ikonou → plochá ikona (--fg-secondary); podpoložka s barevnou dlaždicí (sg) → barevná dlaždice se stejným glyfem a tónem. Detail záznamu dědí značku své sekce (projekt = dlaždice Projekty, úkol = ikona Úkoly, schůzka = ikona Kalendář). Výjimka — přepínač (pipeline): spouštěč ukazuje značku VYBRANÉ položky + chevron, jako každý výběr (zadavatel 27. 9. 2026, 7b). Implementace (acrm !4017, vlna H): značku NIKDY nepiš ručně — ber ji z apps/web/src/components/section-mark.ts: useSectionMark() (značka + název aktuální položky menu), sectionMarkFor(path, search, mode), sectionMarkForKey(key) (značka jiné sekce, např. drobečky detailu úkolu → Projekty), adaptéry markProps (→ props SectionTopBarTitle) a segmentMark (dynamické segmenty / procesy — značka z dat, shodná s menu). Výjimky, které helper záměrně neřeší: přepínač pipeline (značka vybrané položky), identita osoby/firmy/obchodu (kind, 32 px), detail úkolu jako drobečky (s projektem = Projekty, bez = Úkoly).
  • Dvě+ primární tlačítka, sekundární akce naházené do lišty, vlastní jednorázové „chipy" mimo .tchip.
  • Verzálky v názvu i v chipech (sentence case, §6).

Escalation (když prvek opravdu chybí). Slovník nerozšiřuj potichu. Designer napíše krátké zdůvodnění Art Directorovi: (a) jakou potřebu prvek řeší, (b) proč to nejde stávajícími pěti prvky, (c) návrh tokenů/chování. Teprve po schválení se prvek přidá do karty Section top bar (zdroj pravdy) a až pak na obrazovky. Bez schválení v kartě = na lištu nepatří.

Self-review před publikací lišty: projdi prvky zleva doprava — je každý z povolené pětky? Je identita právě jedna? Je primární tlačítko nanejvýš jedno? Pokud něco neprojde → nahraď .tchipem nebo eskaluj.

Zdroj pravdy + anatomie + DO/DON'T: karta Components · Section top bar.

9. Allow-list — oprávněný keep označ ds-ok

Validátor (pnpm ds:validate, od 7.7.26 STRICT v pre-commitu i CI appky) shazuje každou natvrdo napsanou hodnotu: px ve spacing/gap/radius místo --sp-* / --radius-*, hex místo barevného tokenu / tone-třídy.

Oprávněný keep existuje — a MUSÍ nést marker s důvodem, jinak je to porušení:

background: #16b8c4; /* ds-ok: brand tile CRM workspace */
height: '100vh', // ds-ok: viewport výška, ne spacing token

Oprávněné důvody (úplný výčet):

  • brand barvy (workspace tile; kanálové glyfy ale preferuj přes --channel-*),
  • gradient stops (mapový placeholder, color wheel),
  • true-overlay scrim/stín definovaný overlay systémem (--ov-scrim / --overlay-shadow),
  • layoutové rozměry (šířka sloupce/panelu, viewport výška — nejsou spacing škála),
  • auth/onboarding vzdušné měřítko (dokumentovaná výjimka §4/§6),
  • native control seed (barva nativního prvku, kterou token neřídí).

Marker patří na řádek nálezu (nebo na řádek nad něj). Pozor na prettier: dlouhé deklarace (gradienty) láme na víc řádků a marker odsune z řádku nálezu — takové řádky chraň /* prettier-ignore */. Marker potlačí TOKEN/COLOR nálezy, nikdy HARD (verzálky výjimku nemají).

10. Dialog system — jeden plášť pro všechny overlaye

Pravidlo. Každý dialog v aplikaci je instance JEDNOHO systému (karta Components · Dialog system, app: .acrm-dialog + <Popup> / useConfirm()). Žádné bespoke modaly, žádný window.confirm.

  • Scrim = jednotný závoj --ov-scrim; oddělení pláště dělá jen podpisový stín --overlay-shadow (jen skutečné overlaye — nikdy fokusní karty, §4).
  • Tvar = --radius-xl, bílý povrch, obsah clipnutý; max 1 rám (§0).
  • Kotva: form dokuje ~11vh od horní hrany; confirm centruje.
  • Šířky = TŘI TŘÍDY, ne řada čísel (ACRM-320/321, <Dialog size>): s 400 confirm a picker · m 750 formulář (default dialogů se vstupy) · l flexi dokument, e-mail, náhled souboru a buildery — bez pevné míry, šířku bere z okna se stropem 1200, výšku z obsahu. Číslo v width je deprecated: nový dialog si vybírá příčku, ne pixely. Měřený vzor: Linear 750, Attio 800. Head+foot pinned, scrolluje jen tělo (.tall = 88vh).
  • Patička: vlevo kontextové ghost afordance (.lead), vpravo sekundár a max jeden primár; destrukce = .btn.danger (--critical).
  • Klávesy / fokus / motion: Esc zavře; fokus po otevření na panel (autofocus input vyhraje) a po zavření zpět na trigger; scrim fade 120 ms, plášť rise+scale 160 ms, reduced-motion okamžitě.
  • Confirm (nevratné/destruktivní rozhodnutí): titulek + jedna věta důsledku + Zrušit + destruktivní primár — v Reactu useConfirm() (promise), nikdy nativní window.confirm.

11. Přechod mezi stavy prvku — jeden prvek, který se přetváří

Pravidlo (AD 17. 9. 2026). Když prvek mění stav (chip nahrávání, tlačítko s průběhem, lišta, dlaždice), nevyměňuje se za jiný prvek — zůstává týž a jen se přetvaruje. Nic nepřilétá ani neodlétá.

  1. Jeden trvalý prvek. Rámeček (u chipu kanonický .tk-chip) je v DOM po celou dobu; stav nese atribut (data-phase), ne jiná komponenta.
  2. Mění se jen rozměr — šířka (u svislých prvků výška) z předchozí na novou kanonickou PRUŽINOU (zadavatel 26. 9. 2026, packages/ui/src/lib/spring.ts: tlumený oscilátor, tuhost 190, tlumení 17, ~750 ms, přestřelení ~6 %, WAAPI klíčové snímky). NAHRAZUJE dřívější 0,29 s cubic-bezier(0.32, 0.72, 0, 1) (ACRM-152) — ten kánon skončil, nedoplňuje se. <Dialog morphKey> (okno) a MorphText (šířka textu, @acrm/ui) jedou na pružině; oba mají kanonický záznam v DS registru (spring, morph-text). Žádná vlastní délka ani křivka.
  3. Obsah se prolne NA MÍSTĚ. Odcházející vrstva se vyjme z toku (position: absolute na téže pozici, šířku určuje jen příchozí) a rozplyne se (opacity, ~0,14 s ease-out); příchozí najede opacity 0 → 1 za 0,2 s se zpožděním 0,06 s. Žádný posun ani zvětšení — transform zůstává none.
  4. Tvar se nemění: výška, rámeček, radius i písmo jsou ve všech stavech tytéž a obsah je dědí. Stav „probíhá přepis" nesmí být vyšší než stav klidový — skok rozměru rozbije řadu chipů.
  5. Glyf uvnitř (pauza ↔ přehrát) se mění prolnutím ikony (opacity + scale 0,6 → 1, 0,2 s), ne výměnou celého obsahu.
  6. Animuje se jen ZMĚNA STAVU, ne každý render (tikající časomíra, dotažená data) — jinak se přechod ruší uprostřed. Změna během běhu začíná z právě viditelného rozměru.
  7. Výchozí rozměr měř v okamžiku akce (klik), ne z pozorovatele velikosti: ten drží hodnotu z doby před načtením písma (změřeno 83,7 místo 85 px → poskok zpět).
  8. prefers-reduced-motion = výměna bez animace (springAnimate vrátí null, odcházející vrstva display: none). Pro celou appku to navíc drží globální pojistka apps/web/src/ds/reduced-motion.css (acrm !4008, 27. 9. 2026): poslední vrstva CSS (ve všech třech seznamech — main.tsx, .ladle/components.tsx, ds-proto-build.mjs), která při omezeném pohybu zkrátí každý transition a animation na 0.01ms, vynuluje delay, pustí animaci jen jednou a vypne plynulé rolování. Proč 0.01ms, ne 0s: nulová délka nevyvolá transitionend/animationend a kód, který na ně čeká (zavření šuplíku, odmontování), by uvízl. Pojistka pokrývá i portované soubory, na které se nesahá. Lokální přepis (např. „ruka zůstane stát“) je přesnější a zůstává. Nový přechod proto nemusí psát vlastní blok, ale nesmí spoléhat na průběh animace (výsledný stav musí být správný i bez ní).
  9. Ověřuje se po snímcích, ne okem: rozměr jede po křivce pružiny (viz spring.ts), výška konstantní, transform: none, obě vrstvy během prolnutí na téže levé hraně.

Proč: výměna prvků čte jako dva různé objekty — oko sleduje „něco přiletělo" místo „tohle se změnilo". První kolo návrhu nahrávacího chipu takhle vypadalo a AD ho zamítl; přijaté chování je jeden objekt, který se roztahuje a zužuje.

Referenční implementace (vše na pružině, acrm !4016, 27. 9. 2026): <Dialog morphKey> (packages/ui/src/components/Dialog.tsx), MorphText (packages/ui/src/components/MorphText.tsx), ChipMorph (apps/web/src/components/notes/ChipMorph.tsx — šířka chipu; sdílí ho i NoteRecChip v NoteAudio.tsx), kurzorový pruh výpisu .il-cursor-bar (ItemList.tsx) a formless okno: use-avail-spring.ts (šířka okna + posun, posun drží, dokud šířka nedojede — SPRING_ARRIVE_MS) a use-formless-height-spring.ts (výška .fl-body, obsah se nehýbe) v apps/web/src/components/meetings/. Dřívější @keyframes flaSettle a CSS přechody šířky/výšky formless okna zanikly. Stará křivka cubic-bezier(0.32, 0.72, 0, 1) smí zůstat jen u prolnutí obsahu (bod 3), vstupních animací a barvy — nikdy u změny rozměru.

Past — vlastní proměnná čtená z JS: proměnnou s clamp() / 100vw (např. --fla-shift) Chromium v getComputedStyle(el).getPropertyValue() nevyhodnotí — vrátí nevyhodnocený výraz, ne pixely. Když JS potřebuje hodnotu v px (cíl pružiny), čte ji přes skrytou sondu (prvek s width: var(--x) a měří jeho getBoundingClientRect().width), ne ze stylu proměnné.

12. Žádné dvě úrovně záložek nad sebou

Pravidlo (zadavatel 27. 9. 2026 zamítl TabStrip pod taby v detailu uživatele). TabStrip / Tabs řádek se nikdy nekreslí pod jiným taby řádkem — jedna obrazovka nese nejvýš jednu vodorovnou řadu záložek. Když obsah potřebuje druhou úroveň volby:

  • Je to nová stránka, ne záložka. Druhá úroveň navigace = vlastní položky menu + vlastní stránky (vzor Settings page, karta Section top bar · Settings page) — ne vnořený TabStrip pod existujícím record-tabs. Osobní nastavení a preference nejsou záložka detailu záznamu (uživatele, firmy, …); patří do Nastavení jako vlastní položka a vlastní stránka.
  • Nebo je to jeden Segmented control uvnitř stránky (např. E-mailové šablony „Moje | Firemní"), ne druhý řádek tabů. Segmented je přepínač obsahu JEDNÉ stránky, ne navigace mezi stránkami — nesmí se řadit pod .tbar/record-tabs jako by to byl další tab strip.

Zakázáno: TabStrip/Tabs bezprostředně pod record-tabs (nebo pod jiným TabStrip), a to i když je vizuálně odlišený (menší, jinak podbarvený) — dvě úrovně tabů nad sebou zamítá i vizuálně upravená varianta. Řešení je vždy buď samostatná stránka (Settings pattern), nebo Segmented control v obsahu, nikdy druhý tab řádek.

Zdroj pravdy + anatomie: karty Components · Tab strip, Components · Record tabs a Components · Settings page (docs/section-top-bar/settings-page).

13. Rozměr = token podle VÝZNAMU

Pravidlo (zadavatel 27. 9. 2026, vlny F + F4). Rozměr v CSS se nepíše číslem, ale tokenem, který říká, co to je — ne jakou má hodnotu. Stejné číslo s jiným významem = jiný token (--pill-h a --tile-sm mají obě 22 px, ale můžou se rozejít). Zdroj: packages/ui/src/tokens/tokens.css.

Co to jeTokenHodnota
glyf / ikona--ico-2xs · -xs · -sm · -md · -lg · -xl12 · 14 · 16 · 18 · 20 · 24
mezera, odsazení--sp-*škála mezer
výška ovládání (tlačítko, přepínač)--control-h30
kompaktní ovládání (Button compact, IconButton sm, tlačítko v řádku)--control-h-sm24
vstupní pole, select, hledání + ovládání topbaru, které s nimi lícuje--field-h32
řádek výpisu / navigace--row-h · --row-h-nav37 · 28
avatar v textu · výchozí · velký--avatar-xs · -sm · -lg16 · 24 · 32
tečka indikátoru · tečka stavu--dot-sm · --dot6 · 8
pilulka malá · pilulka--pill-h-sm · --pill-h20 · 22
dlaždice přepínače (.pipe-tile)--tile-sm22
silná čára (indikátor záložky, spoje)--line-thick2
  1. Nový token jen s významem, ne pro jedno číslo. Když hodnota nemá význam na škále (komponentní rozměr, kresba 1:1 z předlohy), zůstává viditelným dluhem v ratchetu — ne ds-ok jen kvůli čistému výpisu. ds-ok patří tam, kde by byl nutný i v novém kódu (glyf = výška sousedního textu, iniciály avataru) a vždy s důvodem na řádku.
  2. Baseline ratchetu (dluh, suppressed) se nikdy ručně nezvyšuje — mr-bot to hlásí jako obcházení brány. Ratchet jen klesá.
  3. Glyf v boxu má paritu s boxem: box − glyf = sudé číslo (18 → 12 / 14, 32 → 14 / 16); lichý rozdíl = kresba na půlpixelu, vypadá rozmazaně.
  4. Tokenizace nemění hodnotu. Změna hodnoty (např. pole 32 → 30) je vizuální změna → návrh.

14. Žádné popisné texty v základním UI — vysvětlení do (i)

Zadavatel 3. 10. 2026: „přesně takový přístup musíme mít, žádné dlouhé popisné texty v základním UI, vše schovávat do (i)“ (návrh v2 na Nastavení, :3777/preview/vysvetlivky-info-tooltip.html). Navazuje na AD 9. 8. 2026: nikdo je nečte.

  1. Nejdřív škrtat. Věta, která nemění rozhodnutí uživatele, zmizí. Význam nese struktura, hierarchie, glyf a chování.
  2. Co zbyde a mění rozhodnutí (důsledek, nevratnost, odkud se berou data) jde do InfoTip (i) za nadpisem sekce nebo popiskem pole: klik / tap otevře, tap mimo nebo Esc zavře, max 2 věty / 280 px, na dotyku terč --tap-min. Tooltip zůstává jen pro název akce nebo důvod, ne pro vysvětlení.
  3. Viditelné zůstávají: stav („Na účtu zatím není žádné číslo“), chyba, varování, které se musí přečíst DŘÍV (např. automatizace odešle reálné e-maily), data a jediný návod k akci.
  4. Příklad nebo formát hodnoty („např. věcně, zdvořile…“) patří do placeholderu pole, ne pod pole.
  5. Sekce, po které zbyde jen nadpis + (i), se přeskládá. Sekce bez obsahu nestojí sama.
  6. Doplňkový světlý text (--fg-muted) zůstává světlý (zadavatel 3. 10. 2026); co má být čitelné, je --fg-secondary.
  7. Hostitel (i) je nejbližší nadpis nebo popisek: SectionBlock info, Field labelExtra (stojí VEDLE <label> v .acrm-field-head), .flabel, .dlg-label, SegCol.info (hlavička sloupce tabulky). (i) nikdy uvnitř <label> obalujícího ovládací prvek a nikdy v tlačítku; vysvětlení přepínače nebo zaškrtávátka jde do (i) jeho pole. Kde hostitel chybí (prázdný stav), zůstane jedna viditelná věta.
  8. Krátké varování před nevratnou akcí stojí u tlačítka („Odejde přesně tento text.“), ne v (i).

Výsledek akce 3. 10. 2026 (10 MR: !4504, !4507, !4511, !4515, !4517, !4519, !4520, !4522, !4525, !4529): ~130 textů do (i), ~40 smazáno, 11 do placeholderu, ~85 zůstalo viditelných (stavy, chyby, varování, jediné návody). Po cestě opraveno: popisek pole otevíral (i) (labelExtra uvnitř <label>), Avatar bez fallbacku na iniciály, přetékání Průvodce na 390, potomkovský selektor .sgb-flabel .ti.

15. Upozornění = červená a původní ikona

Zadavatel 3. 10. 2026 (zamítl amber u duplicit: „fuj, hnědá, škaredej trojúhelník… pokud to někde takto používáme, změň to všude“):

  1. Upozornění (ikona + text u řádku, v dialogu, u duplicity, varování v okamžiku rozhodnutí) má glyf alert-square-rounded a barvu --critical (glyf) / --critical-ink (text).
  2. alert-triangle a text v --warning-ink se u upozornění nepoužívají.
  3. Datové barvy v grafech a ukazatelích (pásmo Koučinku --ic-amber, reporty, stat tile) nejsou upozornění a zůstávají.
  4. Hnědá se v aplikaci nepoužívá vůbec, nikdy (zadavatel 3. 10. 2026: „vůbec nepoužívej hnědou… nikdy“). Hnědé jsou tokeny --warning-ink a --pill-amber-ink ve světlém režimu (#a35200) a --warning-subtle / --pill-amber-bg v tmavém (#331b00). Žádný text, glyf ani plocha v hnědém tónu. Jasná oranžová (--ic-amber #ff990a, --warning #ffb224) hnědá není.
  5. Upozornění a chyba vypadají stejně (glyf alert-square-rounded, --critical) — i u toastu (ToastProvider: warning a error). Rozliší je text, ne barva ani jiný glyf. Návrh „varování jinou barvou / jantarovou“ (i z review bota) se zamítá s odkazem na tento bod (AD 4. 10. 2026, acrm !4576).
  6. Barva nese prioritu, ne kategorii (zadavatel 4. 10. 2026 k souhrnu importu: „vše červené, není vidět priorita… vrcholem je červená ikona na zeleném pozadí“). Červená jen u toho, co vyžaduje rozhodnutí hned (a jen glyf + titulek / počet); kontrola = neutrální nebo accent; hotovo / informace = tichý řádek bez rámečku. Glyf má vždy tón svého řádku / pruhu — nikdy červený glyf na zeleném nebo jiném tónu. Bloky na jedné stránce mají jednu šířku. Pořadí shora: rozhodni → zkontroluj → hotovo.

16. Názvy tříd bez segmentu „ad“ (blokátory reklam)

Incident 6. 10. 2026 (ATSL, FA256): blokátor reklam schoval popisek .cwk-adlabel v liště „celý den“ týdne kalendáře, grid poskočil o sloupec a celodenní schůzky visely o den dřív. Kosmetické filtry (EasyList, AdGuard, uBlock) skrývají prvky podle tříd a id s „ad“ segmentem.

  1. Žádný segment třídy ani id (část mezi - / _) v kánonu, kartách, screens ani v appce nesmí být ad, ads, adv, advert…, sponsor… ani ad + slovo (adlabel, adcell, adev, adbox, adbanner, adslot…). Běžná slova (add, addr, admin, adresa) jsou v pořádku.
  2. Zkratky piš celým slovem: celodenní = allday (.cwk-allday-label/-cell/-ev/-more/-pop), nikdy ad.
  3. Appku hlídá kontraktní test apps/web/src/ds/__tests__/tridy-bez-reklamy.contract.test.ts (acrm !4780); název ze starého kánonu by při portu neprošel.

17. Myš a dotyk mají přednost před klávesnicí; přetahování vždy s kopií pod kurzorem

Zadavatel 6. 10. 2026 (FA255, přes CTO: rozbité přetahování obchodů po refaktoru desky z 3. 10. — tažená karta „se nechytne myši", jen ztlumení a čára):

  1. Vzhled, chování a funkční prvky UI mají přednost před přístupností z klávesnice. Klávesové ovládání se přidává jen tam, kde nic nezhorší pro myš a dotyk; kvůli němu nesmí z toho, co uživatel vidí a používá, nic ubýt. Návrh, který kvůli klávesnici ubírá viditelné chování, se zamítá s odkazem na tento bod.
  2. Každé přetahování v aplikaci má kopii taženého prvku, která jede s kurzorem / prstem — .acrm-drag-ghost (apps/web/src/ds/reorder.css: podklad --bg-canvas, stín --shadow-pop, --radius-md) přes sdílené liftDragGhost / useReorder z @acrm/ui (acrm !4774, !4776). Místo dopadu ukazuje rozestup cíle a zvýrazněné místo .acrm-drop-slot (čárkovaný --accent, podklad --accent-tint, pod kopií) přes dropGap z @acrm/ui (zadavatel 6. 10. 2026, acrm !4796 — čára „sem“ nestačila a zanikla). Ztlumení zdroje a místo dopadu jsou doplněk, kopii nenahrazují. Z návrhů ji neodstraňuj.

18. Tlumený text --fg-muted se nemění

Zadavatel 7. 10. 2026 (R15, podnět FA257 z Datamart e2e): --fg-muted (--ink-400, světle #98999c, tmavě #717479) zůstává, i když jako běžný text nesplňuje WCAG AA (2,85 : 1 / 3,63 : 1). Je to vědomé rozhodnutí o vzhledu (RULES §17.1 — vzhled má přednost). Návrh ztmavit token nebo zavést druhý „textový" tlumený token se zamítá s odkazem na tento bod; kde text musí být čitelný naplno, patří do --fg-secondary / --fg podle významu.

19. Dotykový cíl na telefonu ≥ 44 px, vzhled se nemění

Zadavatel 8. 10. 2026 (bod 16, FA257, acrm !4882, prototyp proto/touch-targets-mobile@522540199):

  1. Pod 720 px má každé .acrm-icobtn (i sm a framed) a .acrm-btn neviditelnou plochu ::after aspoň 44 × 44 px (apps/web/src/ds/responsive.css, selektor v :where). Glyf, rozměr tlačítka ani hustota řádků se nemění; desktop beze změny. FAB je vyňatý.
  2. Plocha nesmí krást klepnutí sousedovi: roste od levého horního rohu doprava a dolů, v liště sekce (.tbar) svisle na střed; řádky výpisů (.il-li) a pole formulářů se kreslí nad ní; ⋮ sloupce tabulky má plochu jen do výšky. Ověřeno 18 tras × 430 klikatelných prvků, 0 % cizí plochy.
  3. Nové tlačítko vlastní ::after, min-height ani „velkou" variantu pro telefon nedostává — cíl řeší toto pravidlo. Kde předek plochu ořízne (overflow), je to chyba k opravě, ne důvod ke zvětšení vzhledu.

20. Štítky výpisu vždy ve slotu tags

Zadavatel 8. 10. 2026 (zadání komunikace na kartě R13, acrm !4915, review AD FA258 / FA259 / FA264):

  1. Pilulky, které řádek výpisu štítkují (štítek pošty, tag úkolu, štítek projektu), patří do slotu tags komponenty ItemList — nikdy do cols, meta ani do vlastního sloupce. Slot kreslí pilulky doprava těsně před datum, bez pevné šířky; nepřeroste polovinu řádku, název má přednost. Stavová pilulka ve sloupci tabulky štítek NENÍ a zůstává.
  2. Výpis se štítky nastaví na obalu --il-meta-w (jedna šířka data pro všechny řádky), aby pravá hrana štítků lícovala.
  3. Na telefonu (hustota lg) komponenta sama zalomí štítky na vlastní linku pod název i datum přes celou šířku; volající nic nevětví. lg má padding-block: var(--sp-3).
  4. Hlavička skupiny .tl-grp nad vnořeným výpisem (width="embedded") nese .is-embedded a jde přes celou šířku boxu; žádné lokální dorovnání v obrazovce.
  5. Pilulka s vedoucí ikonou nemá tečku a ikona nese tón (.acrm-pill > .ti { color: var(--pill-dot) }) — obecné pravidlo pilulky, ne výjimka jedné komponenty. Štítek projektu (Project label) je .acrm-pill sm + ikona projektu, bez vlastního CSS.
  6. Platí pro všechny výpisy (pošta, komunikace, úkoly, TaskList v @acrm/ui); nový výpis se štítky bez slotu tags se nevydává.

21. Přiřazení k záznamu = rovnou našeptávač, nikdy dialog s tlačítkem „Vybrat…"

Zadavatel 13. 10. 2026 (dialog „Přiřadit ke kontaktu" s vnořeným „+ Vybrat kontakt"): „tenhle úplně debilní dialog, který místo toho, aby rovnou člověk vybral osobu, má v sobě vnořený další dialog, musíš předělat, nikdy nikde už ho nechci vidět, u prolinkování osob ani firem … musí to fungovat stejně jako když přidávám nový obchod — rovnou se otevře našeptávač institucí nebo lidí nebo obojího dle toho, co se přidává — musí to fungovat všude."

  1. Akce „Přiřadit / Propojit / Vybrat osobu · firmu · obojí" otevře rovnou velkou paletu hledání jako u Nového obchodu (DealSubjectStep: šířka paletteWidth = ⌘K, PaletteSkeleton, sekce, patička ↑↓), s kurzorem v hledání; na telefonu šuplík na plnou výšku. Ne malý našeptávač v Popoveru u spouštěče — zadavatel 14. 10. 2026: „vybírám si v pohodlném UI, s kým chci obchod otevřít; v malém inline dialogu se blbě hledá, když mám třeba časté jméno". Typ výsledků podle toho, co se přiřazuje: lidé, instituce, nebo obojí.
  2. Zakázáno: dialog, jehož jediný obsah je spouštěč výběru („+ Vybrat kontakt" / „Vybrat firmu…") a tlačítko „Přiřadit". Výběr položky v našeptávači je potvrzení — žádný druhý krok.
  3. Dialog smí obalit výběr jen tehdy, když vedle něj sbírá další údaj (např. typ vztahu, role); i pak je našeptávač pole uvnitř dialogu otevřené hned, ne vnořený dialog.
  4. Vzor: výběr firmy/osoby u Nového obchodu — paleta je tam záměrně (zadavatel 14. 10. 2026). Malý RecordSelect u spouštěče zůstává jen pro výběr hodnoty z krátkého seznamu (stav, typ), ne pro hledání záznamu.

22. Nic navíc proti prototypu a zadání

Zadavatel 13. 10. 2026 (bubliny chatu): „tam žádný zelený proužek v návrhu nebyl … vyhalucinoval jsi ho a ještě jsi domrvil tvar bubliny."

  1. Implementace i návrh obsahují jen prvky ze schváleného prototypu / zadání nebo z kánonu. Nový prvek (proužek, ikona, štítek, text, barva, tvar) se nepřidává „pro jistotu" — buď ho zadavatel viděl a schválil, nebo tam není.
  2. Změna tvaru nebo vzhledu existující komponenty, o které zadání nemluví, je chyba, ne vylepšení.
  3. Kontrola před odevzdáním: snímek DNES × PO × prototyp a seznam rozdílů; každý rozdíl buď vysvětlený zadáním, nebo odstraněný.

23. Dlouhý text se zkracuje „…", celý na hover / klik

Zadavatel 13. 10. 2026: „proč přestalo fungovat zkracování dlouhého textu na …? Teď to celé rozbíjí UI … musí to fungovat tak, jak to fungovalo, a dlouhý text zobrazit po hover nebo klik."

  1. Titulek, jméno, předmět a buňka ve výpisu, kartě, liště i pilulce mají jeden řádek s text-overflow: ellipsis (min-width: 0 na flex/grid potomkovi). Dlouhý text nikdy nerozšíří řádek, sloupec ani dialog.
  2. Celé znění: title / Tooltip na hover, na dotyku klik (detail nebo popover). Víceřádkový clamp jen tam, kde to kánon komponenty říká (popis v railu, náhled zprávy previewLines).
  3. Regrese zkracování je chyba s nejvyšší prioritou — vizuální testy mají mít aspoň jeden řádek s extrémně dlouhým textem v každé rodině výpisů.

24. Méně textu v UI — rozšíření §14

Zadavatel 13. 10. 2026: „nebuď tak ukecaný v UI!!!! Zákaz si to vypisovat všude viditelně hromadu textu, NIKDO TO NECHCE ČÍST!!!!"

  1. Viditelný text v UI = popisky, hodnoty, akce. Vysvětlení, podklady, zdůvodnění, „co se stane", nápověda a AI odůvodnění patří do (i), do sbaleného „Podklady" nebo do detailu — nikdy jako odstavec v základním zobrazení.
  2. Měřítko: v řádku, kartě i dialogu nanejvýš jedna věta stavu; cokoli delšího je chyba podle §14/§24.

25. Jedna věc = jeden ovladač, jedna cesta

Zadavatel 13. 10. 2026 (podpis v psaní e-mailu: ikona Podpis vedle „Podpis: plný ▾"): „Nemůžou být 2 stejné věci vedle sebe 2 různými cestami." V acrm docs/UI-RULES.md §2.2 (!5017), oprava !5019.

  1. Vedle sebe nikdy dva ovladače téže věci. Novou schopnost (varianta, volba) přidej do nabídky existujícího prvku.
  2. Zdvojení se neřeší odlišením vzhledu (sebrat ikonu, přebarvit); jeden z ovladačů zmizí.
  3. Vzor: psaní e-mailu má pro podpis jen ikonu signature → Picker menu: osobní varianty (druhý řádek = náhled textu podpisu, je to hodnota, ne vysvětlení) — oddělovač — firemní podpisy — oddělovač — × Bez podpisu; ✓ u podpisu, který leží v těle. Na telefonu ⋯ → Podpis = spodní šuplík s „Hotovo".
  4. Plovoucí ovladač (FAB „+", plovoucí přepínač rozložení) obsah překrývá záměrně — to není zdvojení ani chyba překryvu (zadavatel 14. 10. 2026).
  5. Dvě ikony s téměř stejnou nabídkou = jedna ikona. Vzor: „Později" (LaterMenu, AD 24. 10. 2026, a2a FA269) nahrazuje dvojici Odložit + Připomenout — jedna ikona v liště čtečky pošty, v nabídce přepínač „Připomenout" jako řádek Picker menu. Čip odloženého stavu je Button ghost s tónem, ne vlastní třída.
  6. Ikona a ovladač před textem. Kde stačí ikona nebo ovládací prvek (šipka posunu, ⋮, chevron, čip), nepíše se text. Zadavatel 14. 10. 2026 (záložky detailu firmy, „+ 8 další"): „preferuj ikony a jine ovladaci prvky misto textu". Funkce se ale neztrácí: „+ N další" otevírá vypnuté záložky, takže ji nahradí ikona, ne nic (návrh 24g). Ikonový ovladač má vždy aria-label a tooltip.

26. Viditelnost akce podle frekvence a kontextu

Zadavatel 14. 10. 2026 (prototyp jednotné hlavičky s modrým „Nový obchod" u osoby): „Vůbec takhle nechci … to tlačítko chci jenom někdy, když to má opravdu význam. U osoby nebo kontaktu nezakládám každý den obchod … Chci, aby ten systém byl pěkný a kontextový … obchod na tom místě, kde nad ním přemýšlím … komunikace je velmi častá, ale obchody jsou zřídkavé (u 200 000 kontaktů 20 obchodů)."

  1. Viditelná akce v hlavičce / liště (a zvlášť modrá primární) jen pro činnost, kterou uživatel v tom kontextu dělá opakovaně (Komunikace → Napsat; výpis úkolů → Nový úkol).
  2. Vzácná akce patří do kontextu, kde o ní uživatel přemýšlí — sekce nebo záložka té věci (Nový obchod v „Nejbližší aktivitě" vedle „… nemá otevřený obchod", v sekci / záložce Obchody osoby, v procesu), případně do ⋮. Nikdy trvale svítící v hlavičce. Zadavatel 14. 10. 2026: „zmizelo to CTA v Nejbližší aktivita!" — kontextové CTA se neodstraňuje.
  3. Akce, která už nemá smysl (obchod existuje), se nenabízí výrazně.
  4. „Jedna primární akce" (UX základy bod 4) je strop, ne povinnost: hlavička detailu záznamu primár mít nemusí.

22. Stav patří do řádku štítků jako pilulka

Zadavatel 15. 10. 2026 (prototyp čtení e-mailu proto/mail-cteni-kontext, ACRM-523 / ACRM-525): „udělej to jako štítek … nemělo by to být výjimka, zaveď to jako pravidlo". Nahrazuje dřívější výklad z review !4390 („pilulka = jen kategorie, stav tečkou s textem").

  1. Stav záznamu nebo vlákna (Čeká na vaši odpověď, Čeká na ně, Po termínu, Delegováno…) se kreslí jako pilulka .acrm-pill sm <tón> ve stejném řádku jako štítky (obchod, štítek pošty, příloha) — všechny „štítky" jedné věci drží pohromadě, nikdy rozházené do hlavičky, meta řádku a těla.
  2. Stavová pilulka nemá křížek (stav se mění akcí, ne odebráním) a nese významový tón (amber čeká, rose po termínu, green vyřízeno, grey neutrální); štítek pošty má barvu štítku. Tím se stav a kategorie liší, aniž by stav musel být jiný prvek.
  3. Ve výpisu jde stavová pilulka do slotu tags (§20) před štítky; ve sloupci tabulky zůstává stavová pilulka sloupce.
  4. Tečka + text (.il-dot) zůstává jen tam, kde pilulky nejsou (hustý řádek bez slotu tags, legenda grafu).

27. Prázdný stav stojí uprostřed plochy — i svisle

Zadavatel 21. 10. 2026 (nepropojené kanály Komunikace, proto/kanaly-nepropojene): „vertikálně vystřeď na stránce — zapiš to do DS — všechny prázdné stavy vystřeď".

  1. Prázdný stav se kreslí uprostřed plochy, kterou nahrazuje — vodorovně i svisle. Platí pro EmptyState (.acrm-empty, tón prázdno i chyba), „nic nenalezeno" i nepropojený / nenastavený modul (např. ChannelConnectPane). Plocha = to, co by jinak zaplnil obsah: stránka pod lištou, výpis pod FilterBarem, panel, záložka.
  2. Hostitel vyplní zbytek plochy (flex: 1; min-height: 0 ve sloupcovém flexu) a stav v něm stojí na středu (margin: auto nebo justify-content: center). Lišta, záložky a FilterBar zůstávají nahoře — vystředí se jen obsah pod nimi.
  3. Je-li obsah vyšší než plocha (telefon), začíná nahoře a plocha roluje; margin: auto se sám srazí na 0, nic se neořízne. Žádná výjimka „na mobilu nahoru".
  4. Sekce, jejíž výška se řídí obsahem (sekce railu, karta, popover), nemá co středit svisle — tam jen vodorovně a varianta .sm.

Source: RULES.md · GitLab