diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 48ca385..5d74569 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -2,6 +2,17 @@ Stand: 12.10.2026. Arbeitstitel KantSites; Produktname offen. +## ADR 117 – Menü schließt beim Klick daneben, Netzwerk-Symbole, schlankere Symbole (12.10.2026) + +Anlass: Umut: „Bei Mobilmenü, wenn es aufgeklappt ist, schließt es nicht, wenn man irgendwo anders hinklickt.“ Und für Hüfner: Facebook, Instagram und „Angebot anfragen“ als Menü-Buttons statt eigener Knöpfe im Kopf. + +**Entscheidungen.** +1. **Klick daneben schließt – ohne Skript.** Das Menü bleibt ein `details` (es soll auf jeder Seite ohne Skript aufgehen). Solange es offen ist, legt `summary::before` eine unsichtbare, feste Fläche über die Seite (`z-index:29`); sie gehört zum Knopf, ein Klick darauf schließt also. Die Menüfläche liegt darüber (`z-index:30`; außerhalb des Kopfbereichs per `:where()` mit niedriger Spezifität, damit die Platzierung im Kopf gewinnt). Der erste Klick daneben schließt nur und löst nichts darunter aus – wie bei jedem aufgeklappten Menü. Escape schließt weiterhin nicht (das bräuchte ein Skript). Grenze: Hat ein Vorfahr des Menüs `transform`/`filter`, deckt die feste Fläche nur diesen Vorfahren ab. +2. **Facebook, Instagram, WhatsApp im Symbolsatz.** Dieselben Strichformen wie `social-icon.html.twig`, als Pfad in `icon.html.twig`, `icons.js` (Reiter „Soziale Netzwerke“) und `DocumentValidator::ICONS`. Damit sind sie an Menü-Buttons, Buttons, Symbolkarten und Symbolen wählbar. Keine Schemaänderung; ein Dokument mit diesen Symbolen braucht aber einen Kern ab 1.31.0. +3. **Strichattribute ins Stylesheet.** Jedes Symbol trug `fill`, `stroke`, `stroke-width`, `stroke-linecap` und `stroke-linejoin` als Attribute, rund 105 Zeichen, bei etwa 40 Symbolen je Seite gut 4 KB HTML. Jetzt steht das einmal als `svg.block-icon{…}` in `site.css` und `admin.css`; `width`/`height` bleiben am Element, damit nichts ohne Stylesheet riesig wird. Anlass war die Hüfner-Startseite, die mit den Menü-Buttons das HTML-Budget von 150 KB riss. + +**Geprüft:** Eigene Browserprüfung auf iPad (quer) und iPhone: Menü öffnet, Menülinks liegen über der Fläche, Klick daneben schließt, kein waagerechtes Scrollen. `test:menu-buttons` (Menü-Buttons, axe), `test:editor` (Verwaltung mit Symbolen, axe), `npm test` (Symbolsatz in Twig, JS und Validator gleich). `test:responsive-navigation` scheitert im Editorteil am Klick auf den Strukturbaum – ebenso ohne diese Änderungen (Gegenprobe mit zurückgesetzten Dateien), also vorbestehend. + ## ADR 116 – Härtung nach dem Sicherheitscheck (12.10.2026) Anlass: Umut: „Kannst du mal einen Sicherheitscheck für KantSites machen? Das Ding muss bombensicher sein.“ Fünf getrennte Prüfungen (Anmeldung/Rollen, öffentliche Eingaben, XSS/Ausgabe, Uploads/Dateien, Installer/Updater/Betrieb) plus ein Blick von außen auf die Live-Seiten. Keine ausnutzbare Lücke ohne Anmeldung bei korrekt eingerichteter Installation; behoben wird, was übrig blieb. diff --git a/docs/STATUS.md b/docs/STATUS.md index 2001568..722a977 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -1,5 +1,15 @@ # Projektstatus +## Fassung 1.31.0: Menü schließt beim Klick daneben, Netzwerk-Symbole; Hüfner-Kopf (12.10.2026) + +**Anlass:** Umut: aufgeklapptes Mobilmenü schließt nicht beim Klick daneben; bei Hüfner wirkt der Kopf auf dem iPad verloren (Menü-Knopf mittig), und Facebook, Instagram und „Angebot anfragen“ sollen ins Menü. + +**Neu im Kern (ADR 117):** Klick neben das aufgeklappte Menü schließt es – reines CSS (unsichtbare Fläche am Menü-Knopf), kein Skript. Facebook, Instagram und WhatsApp im Symbolsatz (Menü-Buttons, Buttons, Symbole). Die Strichattribute der Symbole stehen jetzt als `svg.block-icon` in `site.css`/`admin.css` statt an jedem Symbol (rund 4 KB weniger HTML je Seite). + +**Hüfner (nur Inhalt):** Kopf aus Logo und rechtsbündigem Menü; „Angebot anfragen“, Facebook und Instagram als Menü-Buttons. Desktop zeigt in der Zeile nur „Angebot anfragen“ (F/I stehen in der Leiste darüber), im aufgeklappten Menü stehen alle drei, die Symbole nebeneinander. Menü-Knopf auf iPad und Telefon rechts; Abstand und Schrift schrumpfen knapp über 1024 px mit, damit die Zeile nicht umbricht. Upload-Paket neu (`var/huefner-upload.zip`, braucht 1.31.0). + +**Geprüft:** PHPUnit vollständig 657 grün; `npm test` 41; `lint:twig`; `test:menu-buttons` und `test:editor` grün inklusive axe; `test:browser` auf Hüfner grün (HTML Startseite rund 148 KB). Eigene Browserprüfung auf iPad quer und iPhone: öffnen, Menülinks bedienbar, Klick daneben schließt, kein waagerechtes Scrollen; Kopf bei 810, 1025, 1080, 1100, 1280 und 1366 px im Bild. **Bekannt:** `test:responsive-navigation` scheitert im Editorteil am Klick auf den Strukturbaum – auch ohne diese Änderungen (Gegenprobe), vorbestehend. + ## Fassung 1.30.0: Härtung nach dem Sicherheitscheck (12.10.2026) **Anlass:** Umut: „Sicherheitscheck für KantSites … das Ding muss bombensicher sein.“ Prüfung in fünf Bereichen plus Blick von außen auf huefner-gartenbau.de und kant-it.de; Ergebnis im Chat vom 12.10.2026, Umsetzung „1.30.0, Signatur später“. diff --git a/public/assets/admin.css b/public/assets/admin.css index 9b8f41c..f0d61cf 100644 --- a/public/assets/admin.css +++ b/public/assets/admin.css @@ -1,3 +1,5 @@ +/* Der Strich aller Symbole aus icon.html.twig, wie auf der Website (site.css). */ +svg.block-icon{fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round} /* Administration: shared foundation and the management screens. The editor has its own stylesheet (editor.css) that only the editor and the media page load. */ diff --git a/public/assets/icons.js b/public/assets/icons.js index f73823f..64080b8 100644 --- a/public/assets/icons.js +++ b/public/assets/icons.js @@ -107,6 +107,10 @@ export const ICON_PATHS = { hardware: 'M4 5h16v11H4Zm4 15h8m-4-4v4', network: 'M5 3h14v5H5Zm0 13h14v5H5Zm7-8v8M8 8v3h8V8', design: 'M4 20h16M6 16 18 4l3 3L9 19l-4 1Z', + // Soziale Netzwerke + facebook: 'M15.5 4h-2.2A3.8 3.8 0 0 0 9.5 7.8V10H7v3.2h2.5V20h3.3v-6.8h2.4l.5-3.2h-2.9V8.2c0-.6.3-.9.9-.9h2Z', + instagram: 'M8.1 3.5h7.8a4.6 4.6 0 0 1 4.6 4.6v7.8a4.6 4.6 0 0 1-4.6 4.6H8.1a4.6 4.6 0 0 1-4.6-4.6V8.1a4.6 4.6 0 0 1 4.6-4.6ZM12 8.1a3.9 3.9 0 1 0 0 7.8 3.9 3.9 0 0 0 0-7.8ZM17 7h.01', + whatsapp: 'M4 20l1.4-4A8.5 8.5 0 1 1 8.6 19.1ZM9.3 9.2c0 3 2.5 5.5 5.5 5.5l1.3-1.4-2-.9-.9.8a4.1 4.1 0 0 1-2.3-2.3l.8-.9-.9-2Z', }; export const ICON_LABELS = { @@ -130,6 +134,7 @@ export const ICON_LABELS = { check: 'Häkchen', 'check-circle': 'Häkchen im Kreis', info: 'Info', help: 'Frage', alert: 'Warnung', search: 'Suche', settings: 'Einstellungen', filter: 'Filter', grid: 'Raster', list: 'Liste', sparkle: 'Funkeln', coffee: 'Kaffee', utensils: 'Besteck', laptop: 'Laptop', hardware: 'Bildschirm', network: 'Netzwerk', design: 'Stift', + facebook: 'Facebook', instagram: 'Instagram', whatsapp: 'WhatsApp', }; /** Die Kategorien des Auswahlfensters, in der Reihenfolge der Reiter. Jedes Symbol steht genau einmal. */ @@ -141,6 +146,7 @@ export const ICON_GROUPS = [ ['Personen & Sicherheit', ['user', 'people', 'login', 'lock', 'key', 'shield', 'eye', 'thumbs-up', 'award', 'graduation', 'heart', 'star']], ['Haus & Handwerk', ['home', 'building', 'wrench', 'hammer', 'brush', 'ruler', 'leaf', 'tree', 'sun', 'droplet', 'flame', 'bolt', 'solar', 'thermometer', 'snowflake', 'car']], ['Allgemein', ['check', 'check-circle', 'info', 'help', 'alert', 'search', 'settings', 'filter', 'grid', 'list', 'sparkle', 'coffee', 'utensils', 'laptop', 'hardware', 'network', 'design']], + ['Soziale Netzwerke', ['facebook', 'instagram', 'whatsapp']], ]; const svgNS = 'http://www.w3.org/2000/svg'; diff --git a/public/assets/site.css b/public/assets/site.css index 8464218..d6779c9 100644 --- a/public/assets/site.css +++ b/public/assets/site.css @@ -264,6 +264,8 @@ body.consent-open{padding-bottom:320px} /* A single icon, sized by class because inline styles are forbidden by the policy. */ .block-icon{display:inline-flex;align-items:center;justify-content:center;color:var(--accent);line-height:0} .block-icon svg{width:100%;height:100%;display:block} +/* Der Strich aller Symbole (icon.html.twig) - einmal hier statt als Attribute an jedem einzelnen. */ +svg.block-icon{fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round} /* Das eingebettete SVG traegt dieselbe Klasse wie seine Huelle und setzte deshalb die Akzentfarbe erneut - eine am Symbolblock eingestellte Farbe kam nie an. */ .block-icon .block-icon{color:inherit} @@ -475,6 +477,12 @@ body.consent-open{padding-bottom:320px} .menu-collapse-mobile>.menu-list{display:none} } .menu-burger>summary{min-height:44px;min-width:44px} +/* Ein Klick neben das aufgeklappte Menue schliesst es - ohne Skript: Solange es offen ist, liegt + eine unsichtbare Flaeche ueber der Seite, die zum Menue-Knopf gehoert. Ein Klick darauf ist ein + Klick auf den Knopf, und der klappt zu. Die Flaeche des Menues liegt darueber und bleibt + bedienbar; :where() haelt die Regel schwaecher als die Platzierung im Kopfbereich. */ +.menu-burger[open]>summary::before{content:'';position:fixed;top:0;right:0;bottom:0;left:0;z-index:29;cursor:default} +:where(.menu-burger[open])>.menu-burger-body{position:relative;z-index:30} .menu-burger-body{overflow-wrap:anywhere} /* Im Kopfbereich klappt das Menue als Flaeche unter dem Kopf auf, ueber die volle Breite. Inline geoeffnet stand es sonst in der schmalen Spalte neben der Marke und schob den diff --git a/scripts/seed-huefner.php b/scripts/seed-huefner.php index 3d7436d..0d744c2 100644 --- a/scripts/seed-huefner.php +++ b/scripts/seed-huefner.php @@ -499,31 +499,43 @@ $kopf = [ 'desktop' => ['width' => '153px', 'maxWidth' => '153px', 'flexShrink' => 0, 'margin' => $seiten('0px')], 'mobile' => ['width' => '121px', 'maxWidth' => '121px'], ], '/', '& img{display:block;width:100%;height:auto;border-radius:0}'), + // Umut, 12.10.2026: Facebook, Instagram und „Angebot anfragen“ gehören ins Menü + // (Menü-Buttons, ADR 110). Das Menü steht rechtsbündig - auch auf dem iPad quer, wo + // vorher der Menü-Knopf mitten im Kopf stand. Auf dem Desktop zeigt die Zeile nur den + // ersten Button (Angebot anfragen); Facebook und Instagram stehen dort schon in der + // Leiste darüber. Im aufgeklappten Menü stehen alle drei, die Symbole nebeneinander. $column('kopf-navigation', [ ['id' => 'kopf-menue', 'type' => 'menu', 'props' => ['slot' => 'header', 'layout' => 'horizontal', 'collapse' => 'tablet', 'burgerLabel' => 'icon'], 'style' => ['desktop' => [ - 'fontFamily' => 'heading', 'fontSize' => '18px', 'fontWeight' => '700', 'letterSpacing' => '0.06em', 'textTransform' => 'uppercase', 'color' => '@tinte', + 'fontFamily' => 'heading', 'fontSize' => '18px', 'fontWeight' => '700', 'letterSpacing' => '0.04em', 'textTransform' => 'uppercase', 'color' => '@tinte', 'burgerColor' => '@papier', 'burgerBackgroundColor' => '@tinte', 'burgerBorderColor' => '@tinte', 'burgerBorderRadius' => '999px', ]], // Unterstrich, der beim Überfahren von links einläuft. Das Klappmenü auf // Tablet und Telefon liegt als Fläche unter dem ganzen Kopf. - 'css' => "& .menu-list{gap:4px 34px;justify-content:center;align-items:center}\n" - ."& .menu-list a{position:relative;padding:6px 0;text-decoration:none;color:#17221b}\n" - ."& .menu-list a::after{content:'';position:absolute;left:0;right:100%;bottom:0;height:2px;background:#067f30;transition:right .25s ease}\n" - ."& .menu-list a:hover::after{right:0}\n" - ."& .menu-burger summary{width:46px;height:46px;display:flex;align-items:center;justify-content:center;padding:0}\n" - ."& .menu-burger-body{position:absolute;left:0;right:0;top:100%;background:#fff;padding:4px 20px 20px;border-top:1px solid #d4e8db;box-shadow:0 18px 30px rgba(20,32,26,.12);font-size:22px}\n" - ."& .menu-burger-body .menu-list{flex-direction:column;align-items:stretch;gap:0}\n" - ."& .menu-burger-body .menu-list a{display:block;padding:14px 0;border-bottom:1px solid #e0efe5}\n" - .'& .menu-burger-body .menu-list a::after{display:none}'], - ], ['desktop' => ['flexGrow' => 1, 'display' => 'flex', 'justifyContent' => 'center'], 'tablet' => ['justifyContent' => 'flex-end', 'order' => 3, 'flexGrow' => 0], 'mobile' => ['justifyContent' => 'flex-end', 'order' => 3, 'flexGrow' => 0]]), - $zeile('kopf-aktionen', [ - ['id' => 'kopf-profile', 'type' => 'social', 'props' => ['size' => 20, 'shape' => 'plain', 'layout' => 'horizontal', 'labels' => false, 'networks' => ['instagram', 'facebook']], - 'style' => ['desktop' => ['color' => '@tinte', 'gap' => '10px']], - 'css' => "& a{width:46px;height:46px;justify-content:center;border-radius:50%;box-shadow:inset 0 0 0 1px #cae3d3}\n& a:hover{background:#e9f5ed;color:#067f30}", - 'layout' => ['visibility' => 'hide-mobile']], - $knopf('kopf-knopf', 'Angebot anfragen', '/#kontakt', 'rot', true, [], 'desktop'), - ], '10px', ['flexShrink' => 0], ['margin' => ['left' => 'auto']], ['margin' => ['left' => 'auto']]), + // Abstand und Schrift schrumpfen knapp über der Tablet-Grenze fließend mit, damit + // die Zeile auch bei 1025 px nicht umbricht (eigenes CSS kennt kein @media). + // justify-content am Menü selbst: zusammengeklappt steht nur der Knopf darin, und + // der soll rechts stehen, nicht direkt neben dem Logo. + // Knapp gehalten, die Startseite steht am HTML-Budget. Buttons im Menü (a.cms-button) + // bekommen keine Linkregeln; die Symbol-Buttons sind Ringe wie früher im Kopf. + 'css' => "justify-content:flex-end\n& .menu-list{gap:4px clamp(12px,1.9vw,26px);justify-content:flex-end;align-items:center}\n" + ."& a{position:relative;padding:6px 0;text-decoration:none;color:#17221b;font-size:clamp(15px,1.45vw,18px)}\n" + ."& a::after{content:'';position:absolute;left:0;right:100%;bottom:0;height:2px;background:#067f30;transition:right .25s ease}\n" + ."& a:hover::after{right:0}\n" + ."& a.cms-button{padding:13px 24px;color:#fff;font-size:17px;white-space:nowrap;margin-left:6px}\n" + ."& a.cms-button::after{display:none}\n" + ."& a.icon-only{width:46px;height:46px;padding:0;justify-content:center;margin:0;color:#17221b;background:transparent;box-shadow:inset 0 0 0 1px #cae3d3}\n" + ."& a.icon-only:hover{background:#e9f5ed;color:#067f30}\n" + ."& > .menu-list > .menu-item-button ~ .menu-item-button{display:none}\n" + ."& summary{width:46px;height:46px;display:flex;align-items:center;justify-content:center;padding:0;margin-left:auto}\n" + ."& .menu-burger-body{background:#fff;padding:4px 20px 20px;border-top:1px solid #d4e8db;box-shadow:0 18px 30px rgba(20,32,26,.12);font-size:22px}\n" + ."& .menu-burger-body ul{flex-direction:row;flex-wrap:wrap;justify-content:flex-start;gap:0}\n" + ."& .menu-burger-body li{flex:0 0 100%}\n" + ."& .menu-burger-body a{display:block;padding:14px 0;border-bottom:1px solid #e0efe5}\n" + ."& .menu-burger-body a.cms-button{display:flex;justify-content:center;border:0;margin:16px 0 0;padding:15px 24px}\n" + ."& .menu-burger-body .menu-item-button ~ .menu-item-button{flex:0 0 auto;margin:16px 10px 0 0}\n" + ."& .menu-burger-body a.icon-only{margin:0;padding:0}"], + ], ['desktop' => ['flexGrow' => 1, 'display' => 'flex', 'justifyContent' => 'flex-end', 'minWidth' => '0']]), ], '24px', ['justifyContent' => 'space-between', 'minHeight' => '82px'], ['minHeight' => '66px', 'gap' => '12px']), ], [ 'desktop' => ['backgroundColor' => '#fffffff5', 'padding' => ['top' => '0px', 'bottom' => '0px'], 'borderWidth' => $nurUnten('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie'], @@ -533,7 +545,6 @@ $kopf = [ // Der rote Knopf im Kopf ist kleiner als die im Inhalt. $kleinerKnopf = static fn (array $b): array => [...$b, 'css' => str_replace('min-height:58px;padding:0 30px', 'min-height:50px;padding:0 22px;font-size:16px;white-space:nowrap', $b['css'])]; -$kopf['blocks'][1]['children'][0]['children'][2]['children'][1]['css'] = str_replace('min-height:58px;padding:0 30px', 'min-height:46px;padding:0 24px;font-size:17px', $kopf['blocks'][1]['children'][0]['children'][2]['children'][1]['css']); // --------------------------------------------------------------------------------------------- // Fußbereich @@ -1588,6 +1599,14 @@ $menus->save('header', 'Hauptnavigation', [ $sprung('Karriere', 'karriere'), ['label' => 'Alpakas', 'type' => 'page', 'pageId' => $alpakaId, 'anchor' => '', 'children' => []], $sprung('Kontakt', 'kontakt'), + // Menü-Buttons (ADR 110). Feste Kennungen, damit die Regeln im Kopf sie finden. Facebook und + // Instagram nur als Symbol, wie vorher die runden Knöpfe im Kopf. + ['label' => 'Angebot anfragen', 'type' => 'page', 'pageId' => $homeId, 'anchor' => 'kontakt', 'icon' => 'arrow-right', 'iconPosition' => 'after', + 'button' => ['id' => 'mc7a0000001', 'mobile' => 'menu'], 'children' => []], + ['label' => 'Facebook', 'type' => 'url', 'url' => $facebook, 'newTab' => true, 'icon' => 'facebook', + 'button' => ['id' => 'mfb00000001', 'mobile' => 'menu', 'iconOnly' => true], 'children' => []], + ['label' => 'Instagram', 'type' => 'url', 'url' => $instagram, 'newTab' => true, 'icon' => 'instagram', + 'button' => ['id' => 'm1600000001', 'mobile' => 'menu', 'iconOnly' => true], 'children' => []], ], 0); echo "Menü angelegt\n"; diff --git a/src/Content/DocumentValidator.php b/src/Content/DocumentValidator.php index efe2e90..c1229a5 100644 --- a/src/Content/DocumentValidator.php +++ b/src/Content/DocumentValidator.php @@ -16,7 +16,7 @@ final class DocumentValidator /** Blocks introduced with schema v6 for the sections a marketing page actually needs. */ public const V6_TYPES = ['iconcard', 'stat', 'quote', 'logos', 'cta', 'contact', 'divider', 'spacer', 'accordion', 'accordionitem']; /** Icons ship as inline SVG from this fixed list; no icon font and no external request. */ - public const ICONS = ['arrow-up-right', 'arrow-right', 'arrow-left', 'arrow-up', 'arrow-down', 'chevron-right', 'chevron-left', 'chevron-up', 'chevron-down', 'arrow-circle', 'external', 'refresh', 'menu', 'close', 'plus', 'minus', 'phone', 'app', 'mail', 'chat', 'send', 'calendar', 'clock', 'pin', 'map', 'navigation', 'web', 'wifi', 'support', 'shop', 'cart', 'tag', 'euro', 'credit-card', 'gift', 'percent', 'truck', 'package', 'briefcase', 'document', 'download', 'upload', 'print', 'link', 'share', 'image', 'camera', 'video', 'play', 'pause', 'music', 'mic', 'book', 'user', 'people', 'login', 'lock', 'key', 'shield', 'eye', 'thumbs-up', 'award', 'graduation', 'heart', 'star', 'home', 'building', 'wrench', 'hammer', 'brush', 'ruler', 'leaf', 'tree', 'sun', 'droplet', 'flame', 'bolt', 'solar', 'thermometer', 'snowflake', 'car', 'check', 'check-circle', 'info', 'help', 'alert', 'search', 'settings', 'filter', 'grid', 'list', 'sparkle', 'coffee', 'utensils', 'laptop', 'hardware', 'network', 'design']; + public const ICONS = ['arrow-up-right', 'arrow-right', 'arrow-left', 'arrow-up', 'arrow-down', 'chevron-right', 'chevron-left', 'chevron-up', 'chevron-down', 'arrow-circle', 'external', 'refresh', 'menu', 'close', 'plus', 'minus', 'phone', 'app', 'mail', 'chat', 'send', 'calendar', 'clock', 'pin', 'map', 'navigation', 'web', 'wifi', 'support', 'shop', 'cart', 'tag', 'euro', 'credit-card', 'gift', 'percent', 'truck', 'package', 'briefcase', 'document', 'download', 'upload', 'print', 'link', 'share', 'image', 'camera', 'video', 'play', 'pause', 'music', 'mic', 'book', 'user', 'people', 'login', 'lock', 'key', 'shield', 'eye', 'thumbs-up', 'award', 'graduation', 'heart', 'star', 'home', 'building', 'wrench', 'hammer', 'brush', 'ruler', 'leaf', 'tree', 'sun', 'droplet', 'flame', 'bolt', 'solar', 'thermometer', 'snowflake', 'car', 'check', 'check-circle', 'info', 'help', 'alert', 'search', 'settings', 'filter', 'grid', 'list', 'sparkle', 'coffee', 'utensils', 'laptop', 'hardware', 'network', 'design', 'facebook', 'instagram', 'whatsapp']; /** Das Symbol eines Buttons, wenn keins gewählt ist: der Pfeil, den jeder Button vor v25 hatte. */ public const BUTTON_DEFAULT_ICON = 'arrow-up-right'; public const SPACER_SIZES = ['small', 'medium', 'large', 'huge']; diff --git a/templates/blocks/icon.html.twig b/templates/blocks/icon.html.twig index 33cdb09..f6c707b 100644 --- a/templates/blocks/icon.html.twig +++ b/templates/blocks/icon.html.twig @@ -1,4 +1,6 @@ -{# Inline SVG from a fixed allowlist. No icon font, no external request, no extra stylesheet. #} +{# Inline SVG from a fixed allowlist. No icon font, no external request, no extra stylesheet. + Facebook, Instagram und WhatsApp am Ende: dieselben Formen wie social-icon.html.twig, damit sie + auch als Symbol eines Menü-Buttons oder Bausteins wählbar sind. #} {% set paths = { 'arrow-up-right': 'M6.5 17.5 17.5 6.5M8.5 6.5h9v9', 'arrow-right': 'M4 12h15.5m-6.5-6.5 6.5 6.5-6.5 6.5', @@ -97,6 +99,9 @@ laptop: 'M5 5h14v10H5Zm-2.5 10h19l-1 3.5h-17Z', hardware: 'M4 5h16v11H4Zm4 15h8m-4-4v4', network: 'M5 3h14v5H5Zm0 13h14v5H5Zm7-8v8M8 8v3h8V8', - design: 'M4 20h16M6 16 18 4l3 3L9 19l-4 1Z' + design: 'M4 20h16M6 16 18 4l3 3L9 19l-4 1Z', + facebook: 'M15.5 4h-2.2A3.8 3.8 0 0 0 9.5 7.8V10H7v3.2h2.5V20h3.3v-6.8h2.4l.5-3.2h-2.9V8.2c0-.6.3-.9.9-.9h2Z', + instagram: 'M8.1 3.5h7.8a4.6 4.6 0 0 1 4.6 4.6v7.8a4.6 4.6 0 0 1-4.6 4.6H8.1a4.6 4.6 0 0 1-4.6-4.6V8.1a4.6 4.6 0 0 1 4.6-4.6ZM12 8.1a3.9 3.9 0 1 0 0 7.8 3.9 3.9 0 0 0 0-7.8ZM17 7h.01', + whatsapp: 'M4 20l1.4-4A8.5 8.5 0 1 1 8.6 19.1ZM9.3 9.2c0 3 2.5 5.5 5.5 5.5l1.3-1.4-2-.9-.9.8a4.1 4.1 0 0 1-2.3-2.3l.8-.9-.9-2Z' } %} - +{# Strich, Farbe und Enden stehen als Regel für svg.block-icon in site.css und admin.css, nicht an jedem Symbol: eine Seite trägt um die 40 davon. #} diff --git a/tests/CmsTest.php b/tests/CmsTest.php index 17ececa..781c961 100644 --- a/tests/CmsTest.php +++ b/tests/CmsTest.php @@ -311,7 +311,7 @@ final class CmsTest extends TestCase public static function rejectedButtonIcons(): iterable { yield 'Symbol vor v25' => ['icon', 'phone', 24]; - yield 'unbekanntes Symbol' => ['icon', 'whatsapp', 25]; + yield 'unbekanntes Symbol' => ['icon', 'telegram', 25]; yield 'Symbol ist kein Text' => ['icon', ['phone'], 25]; yield 'Stellung vor v25' => ['iconPosition', 'start', 24]; yield 'unbekannte Stellung' => ['iconPosition', 'middle', 25];