The icon set had no drawing for people, music or a house - the three pictures
the Tagespflege Moldovan template asks for. They join as `people`, `music` and
`home`; a test keeps the list and the drawings together, because a missing path
renders an empty `<path d="">`: invisible, without an error.
An icon block's own colour never reached its drawing: the embedded SVG carries
the same `block-icon` class as its wrapper and set the accent colour again.
`.block-icon .block-icon{color:inherit}` fixes it at the root.
The menu button becomes a thing you can shape. `burgerLabel` chooses symbol and
text, text only or symbol only (which keeps the name for screen readers), and
the new style group `menu` targets the button itself, the way `img` targets an
image and `controls` the slider. Inside the header the opened menu now lies
under the header across the full width instead of pushing the header apart -
on a phone that looked like a missing navigation.
The back-to-top button no longer depends on `animation-timeline: scroll()`. A
browser that parses the property but does not run the timeline left it in the
first frame of the animation: permanently invisible. Two classes from
scrolled.js decide now, and without a script the button simply stays visible.
Also: the Tagespflege Moldovan rebuild as its own installation, built only by
`scripts/seed-tagespflege-moldovan.php`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
263 lines
24 KiB
PHP
263 lines
24 KiB
PHP
<?php
|
||
|
||
namespace App\Content;
|
||
|
||
/**
|
||
* The single source of truth for free block styling.
|
||
*
|
||
* Every property is declared once here with its type, its CSS counterpart and its limits. The
|
||
* validator, the CSS generator and the editor panel all read this definition, so the three can
|
||
* never drift apart: what the editor offers is exactly what the server accepts and renders.
|
||
*
|
||
* Values are stored as short strings ("18px", "#1a2b3c", "flex"), never as CSS text. Each type has
|
||
* an anchored pattern or a fixed list, so no declaration can escape its property.
|
||
*/
|
||
final class StyleSchema
|
||
{
|
||
public const DEVICES = ['desktop' => 'Desktop', 'tablet' => 'Tablet', 'mobile' => 'Mobil'];
|
||
/** A colour is a hex value or "@name", a reference into the site palette. */
|
||
public const COLOR = '/^(#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?|@[a-z][a-z0-9-]{0,31})$/D';
|
||
/** A length is a number with a unit, plus the two keywords that behave like one. */
|
||
/**
|
||
* A length is a number with a unit, plus the two keywords that behave like one.
|
||
*
|
||
* dvh/svh/lvh are in the list because 100vh is wrong on a phone: it means the viewport
|
||
* without the address bar, so a "full height" section is taller than the screen and the
|
||
* bottom edge sits under the browser chrome. 100dvh is what people mean.
|
||
*/
|
||
public const LENGTH = '/^(0|-?\d{1,5}(\.\d{1,3})?(px|rem|em|%|vw|vh|ch|dvh|svh|lvh|dvw))$/D';
|
||
public const LENGTH_AUTO = '/^(auto|none|0|-?\d{1,5}(\.\d{1,3})?(px|rem|em|%|vw|vh|ch|dvh|svh|lvh|dvw))$/D';
|
||
|
||
/**
|
||
* group – where the property appears in the editor
|
||
* type – length | length-auto | color | number | choice | text
|
||
* css – the CSS property it becomes; sides/corners expand to four
|
||
* label – German label for the editor
|
||
* target – 'img' applies the declaration to the images inside the block instead of the block
|
||
* itself. object-fit on a figure does nothing; it belongs on the image.
|
||
* sideCss – how one side is spelled. "padding-top" follows the property name, but a border
|
||
* width is "border-top-width" and not "border-width-top" - written out, because
|
||
* guessing it wrong produces a declaration the browser silently drops.
|
||
*
|
||
* @return array<string, array{group: string, type: string, css: string|array, label: string, values?: array<string, string>, min?: float, max?: float, step?: float, sides?: bool, hover?: bool}>
|
||
*/
|
||
public static function properties(): array
|
||
{
|
||
return [
|
||
// ---- Typografie -------------------------------------------------------------
|
||
'fontFamily' => ['group' => 'text', 'type' => 'font', 'css' => 'font-family', 'label' => 'Schriftart'],
|
||
'fontSize' => ['group' => 'text', 'type' => 'length', 'css' => 'font-size', 'label' => 'Schriftgröße'],
|
||
'fontWeight' => ['group' => 'text', 'type' => 'choice', 'css' => 'font-weight', 'label' => 'Schriftstärke', 'values' => [
|
||
'100' => '100 · Thin', '200' => '200 · Extra Light', '300' => '300 · Light', '400' => '400 · Regular',
|
||
'500' => '500 · Medium', '600' => '600 · Semibold', '700' => '700 · Bold', '800' => '800 · Extra Bold', '900' => '900 · Black',
|
||
]],
|
||
'fontStyle' => ['group' => 'text', 'type' => 'choice', 'css' => 'font-style', 'label' => 'Schriftlage', 'values' => ['normal' => 'Normal', 'italic' => 'Kursiv']],
|
||
'lineHeight' => ['group' => 'text', 'type' => 'number', 'css' => 'line-height', 'label' => 'Zeilenhöhe', 'min' => 0.7, 'max' => 4, 'step' => 0.05],
|
||
'letterSpacing' => ['group' => 'text', 'type' => 'length', 'css' => 'letter-spacing', 'label' => 'Buchstabenabstand'],
|
||
'wordSpacing' => ['group' => 'text', 'type' => 'length', 'css' => 'word-spacing', 'label' => 'Wortabstand'],
|
||
'textAlign' => ['group' => 'text', 'type' => 'choice', 'css' => 'text-align', 'label' => 'Ausrichtung', 'values' => [
|
||
'left' => 'Links', 'center' => 'Mittig', 'right' => 'Rechts', 'justify' => 'Blocksatz',
|
||
]],
|
||
'textTransform' => ['group' => 'text', 'type' => 'choice', 'css' => 'text-transform', 'label' => 'Schreibweise', 'values' => [
|
||
'none' => 'Wie eingegeben', 'uppercase' => 'GROSSBUCHSTABEN', 'lowercase' => 'kleinbuchstaben', 'capitalize' => 'Erster Buchstabe Groß',
|
||
]],
|
||
'textDecoration' => ['group' => 'text', 'type' => 'choice', 'css' => 'text-decoration-line', 'label' => 'Textlinie', 'values' => [
|
||
'none' => 'Keine', 'underline' => 'Unterstrichen', 'line-through' => 'Durchgestrichen',
|
||
]],
|
||
'color' => ['group' => 'text', 'type' => 'color', 'css' => 'color', 'label' => 'Textfarbe', 'hover' => true],
|
||
|
||
// ---- Hintergrund ------------------------------------------------------------
|
||
'backgroundColor' => ['group' => 'background', 'type' => 'color', 'css' => 'background-color', 'label' => 'Hintergrundfarbe', 'hover' => true],
|
||
'gradientFrom' => ['group' => 'background', 'type' => 'color', 'css' => null, 'label' => 'Verlauf von'],
|
||
'gradientTo' => ['group' => 'background', 'type' => 'color', 'css' => null, 'label' => 'Verlauf bis'],
|
||
'gradientAngle' => ['group' => 'background', 'type' => 'number', 'css' => null, 'label' => 'Verlaufswinkel', 'min' => 0, 'max' => 360, 'step' => 1],
|
||
'backgroundImage' => ['group' => 'background', 'type' => 'media', 'css' => null, 'label' => 'Hintergrundbild'],
|
||
'backgroundSize' => ['group' => 'background', 'type' => 'choice', 'css' => 'background-size', 'label' => 'Bildgröße', 'values' => [
|
||
'cover' => 'Füllend', 'contain' => 'Vollständig sichtbar', 'auto' => 'Originalgröße',
|
||
]],
|
||
'backgroundPosition' => ['group' => 'background', 'type' => 'choice', 'css' => 'background-position', 'label' => 'Bildausschnitt', 'values' => [
|
||
'center' => 'Mittig', 'top' => 'Oben', 'bottom' => 'Unten', 'left' => 'Links', 'right' => 'Rechts',
|
||
'top left' => 'Oben links', 'top right' => 'Oben rechts', 'bottom left' => 'Unten links', 'bottom right' => 'Unten rechts',
|
||
]],
|
||
'backgroundRepeat' => ['group' => 'background', 'type' => 'choice', 'css' => 'background-repeat', 'label' => 'Wiederholung', 'values' => [
|
||
'no-repeat' => 'Nicht wiederholen', 'repeat' => 'Kacheln', 'repeat-x' => 'Waagerecht kacheln', 'repeat-y' => 'Senkrecht kacheln',
|
||
]],
|
||
'backgroundAttachment' => ['group' => 'background', 'type' => 'choice', 'css' => 'background-attachment', 'label' => 'Scrollverhalten', 'values' => [
|
||
'scroll' => 'Mitscrollen', 'fixed' => 'Feststehend (Parallax)',
|
||
]],
|
||
'overlayColor' => ['group' => 'background', 'type' => 'color', 'css' => null, 'label' => 'Überlagerung'],
|
||
'overlayOpacity' => ['group' => 'background', 'type' => 'number', 'css' => null, 'label' => 'Deckkraft der Überlagerung', 'min' => 0, 'max' => 1, 'step' => 0.05],
|
||
|
||
// ---- Abstand ----------------------------------------------------------------
|
||
'padding' => ['group' => 'spacing', 'type' => 'length', 'css' => 'padding', 'label' => 'Innenabstand', 'sides' => true, 'sideCss' => 'padding-%s'],
|
||
'margin' => ['group' => 'spacing', 'type' => 'length-auto', 'css' => 'margin', 'label' => 'Außenabstand', 'sides' => true, 'sideCss' => 'margin-%s'],
|
||
|
||
// ---- Größe ------------------------------------------------------------------
|
||
'width' => ['group' => 'size', 'type' => 'length-auto', 'css' => 'width', 'label' => 'Breite'],
|
||
'maxWidth' => ['group' => 'size', 'type' => 'length-auto', 'css' => 'max-width', 'label' => 'Maximale Breite'],
|
||
'minWidth' => ['group' => 'size', 'type' => 'length-auto', 'css' => 'min-width', 'label' => 'Mindestbreite'],
|
||
'height' => ['group' => 'size', 'type' => 'length-auto', 'css' => 'height', 'label' => 'Höhe'],
|
||
'minHeight' => ['group' => 'size', 'type' => 'length-auto', 'css' => 'min-height', 'label' => 'Mindesthöhe'],
|
||
'maxHeight' => ['group' => 'size', 'type' => 'length-auto', 'css' => 'max-height', 'label' => 'Maximale Höhe'],
|
||
'overflow' => ['group' => 'size', 'type' => 'choice', 'css' => 'overflow', 'label' => 'Überlauf', 'values' => [
|
||
'visible' => 'Sichtbar', 'hidden' => 'Abschneiden', 'auto' => 'Scrollen bei Bedarf',
|
||
]],
|
||
|
||
// ---- Rahmen -----------------------------------------------------------------
|
||
'borderWidth' => ['group' => 'border', 'type' => 'length', 'css' => 'border-width', 'label' => 'Rahmenstärke', 'sides' => true, 'sideCss' => 'border-%s-width'],
|
||
'borderStyle' => ['group' => 'border', 'type' => 'choice', 'css' => 'border-style', 'label' => 'Rahmenart', 'values' => [
|
||
'solid' => 'Durchgezogen', 'dashed' => 'Gestrichelt', 'dotted' => 'Gepunktet', 'double' => 'Doppelt', 'none' => 'Keiner',
|
||
]],
|
||
'borderColor' => ['group' => 'border', 'type' => 'color', 'css' => 'border-color', 'label' => 'Rahmenfarbe', 'hover' => true],
|
||
'borderRadius' => ['group' => 'border', 'type' => 'length', 'css' => 'border-radius', 'label' => 'Ecken', 'corners' => true],
|
||
'outlineWidth' => ['group' => 'border', 'type' => 'length', 'css' => 'outline-width', 'label' => 'Zusatzrahmen Stärke'],
|
||
'outlineColor' => ['group' => 'border', 'type' => 'color', 'css' => 'outline-color', 'label' => 'Zusatzrahmen Farbe', 'hover' => true],
|
||
'outlineOffset' => ['group' => 'border', 'type' => 'length', 'css' => 'outline-offset', 'label' => 'Zusatzrahmen Abstand'],
|
||
'outlineStyle' => ['group' => 'border', 'type' => 'choice', 'css' => 'outline-style', 'label' => 'Zusatzrahmen Art', 'values' => [
|
||
'solid' => 'Durchgezogen', 'dashed' => 'Gestrichelt', 'dotted' => 'Gepunktet', 'double' => 'Doppelt', 'none' => 'Keiner',
|
||
]],
|
||
|
||
// ---- Bild: wirkt auf die Bilder im Block, nicht auf den Block ----------------
|
||
'objectFit' => ['group' => 'image', 'type' => 'choice', 'css' => 'object-fit', 'label' => 'Bildausschnitt', 'target' => 'img', 'values' => [
|
||
'cover' => 'Füllen und beschneiden', 'contain' => 'Vollständig zeigen', 'fill' => 'Verzerren', 'none' => 'Originalgröße', 'scale-down' => 'Nur verkleinern',
|
||
]],
|
||
'objectPosition' => ['group' => 'image', 'type' => 'choice', 'css' => 'object-position', 'label' => 'Bildmitte', 'target' => 'img', 'values' => [
|
||
'center' => 'Mitte', 'top' => 'Oben', 'bottom' => 'Unten', 'left' => 'Links', 'right' => 'Rechts',
|
||
'top left' => 'Oben links', 'top right' => 'Oben rechts', 'bottom left' => 'Unten links', 'bottom right' => 'Unten rechts',
|
||
]],
|
||
'imageRatio' => ['group' => 'image', 'type' => 'choice', 'css' => 'aspect-ratio', 'label' => 'Seitenverhältnis', 'target' => 'img', 'values' => [
|
||
'auto' => 'Wie das Bild', '1/1' => 'Quadrat 1:1', '4/3' => 'Klassisch 4:3', '3/2' => 'Foto 3:2', '16/9' => 'Breitbild 16:9',
|
||
'21/9' => 'Panorama 21:9', '3/4' => 'Hochkant 3:4', '2/3' => 'Hochkant 2:3', '9/16' => 'Hochkant 9:16',
|
||
]],
|
||
'imageHeight' => ['group' => 'image', 'type' => 'length-auto', 'css' => 'height', 'label' => 'Bildhöhe', 'target' => 'img'],
|
||
'imageRadius' => ['group' => 'image', 'type' => 'length', 'css' => 'border-radius', 'label' => 'Bildecken', 'target' => 'img', 'corners' => true],
|
||
'imageBorderWidth' => ['group' => 'image', 'type' => 'length', 'css' => 'border-width', 'label' => 'Bildrahmen Stärke', 'target' => 'img', 'sides' => true, 'sideCss' => 'border-%s-width'],
|
||
'imageBorderColor' => ['group' => 'image', 'type' => 'color', 'css' => 'border-color', 'label' => 'Bildrahmen Farbe', 'target' => 'img'],
|
||
'imageBorderStyle' => ['group' => 'image', 'type' => 'choice', 'css' => 'border-style', 'label' => 'Bildrahmen Art', 'target' => 'img', 'values' => [
|
||
'solid' => 'Durchgezogen', 'dashed' => 'Gestrichelt', 'dotted' => 'Gepunktet', 'double' => 'Doppelt', 'none' => 'Keiner',
|
||
]],
|
||
|
||
// ---- Layout -----------------------------------------------------------------
|
||
'display' => ['group' => 'layout', 'type' => 'choice', 'css' => 'display', 'label' => 'Anzeige', 'values' => [
|
||
'block' => 'Block', 'flex' => 'Flex', 'inline-flex' => 'Inline-Flex', 'grid' => 'Raster', 'inline-block' => 'Inline-Block', 'none' => 'Ausgeblendet',
|
||
]],
|
||
'flexDirection' => ['group' => 'layout', 'type' => 'choice', 'css' => 'flex-direction', 'label' => 'Flexrichtung', 'values' => [
|
||
'row' => 'Nebeneinander', 'row-reverse' => 'Nebeneinander, umgekehrt', 'column' => 'Untereinander', 'column-reverse' => 'Untereinander, umgekehrt',
|
||
]],
|
||
'flexWrap' => ['group' => 'layout', 'type' => 'choice', 'css' => 'flex-wrap', 'label' => 'Umbruch', 'values' => ['wrap' => 'Umbrechen', 'nowrap' => 'Nicht umbrechen']],
|
||
'justifyContent' => ['group' => 'layout', 'type' => 'choice', 'css' => 'justify-content', 'label' => 'Verteilung', 'values' => [
|
||
'flex-start' => 'Anfang', 'center' => 'Mitte', 'flex-end' => 'Ende', 'space-between' => 'Außen verteilt', 'space-around' => 'Gleichmäßig', 'space-evenly' => 'Exakt gleich',
|
||
]],
|
||
'alignItems' => ['group' => 'layout', 'type' => 'choice', 'css' => 'align-items', 'label' => 'Ausrichtung quer', 'values' => [
|
||
'stretch' => 'Dehnen', 'flex-start' => 'Anfang', 'center' => 'Mitte', 'flex-end' => 'Ende', 'baseline' => 'Grundlinie',
|
||
]],
|
||
'gridColumns' => ['group' => 'layout', 'type' => 'number', 'css' => null, 'label' => 'Spalten', 'min' => 1, 'max' => 12, 'step' => 1],
|
||
'gap' => ['group' => 'layout', 'type' => 'length', 'css' => 'gap', 'label' => 'Abstand zwischen Elementen'],
|
||
'order' => ['group' => 'layout', 'type' => 'number', 'css' => 'order', 'label' => 'Reihenfolge', 'min' => -20, 'max' => 20, 'step' => 1],
|
||
'rowGap' => ['group' => 'layout', 'type' => 'length', 'css' => 'row-gap', 'label' => 'Abstand zwischen Zeilen'],
|
||
'columnGap' => ['group' => 'layout', 'type' => 'length', 'css' => 'column-gap', 'label' => 'Abstand zwischen Spalten'],
|
||
'alignSelf' => ['group' => 'layout', 'type' => 'choice', 'css' => 'align-self', 'label' => 'Eigene Ausrichtung quer', 'values' => [
|
||
'auto' => 'Wie die Gruppe', 'flex-start' => 'Anfang', 'center' => 'Mitte', 'flex-end' => 'Ende', 'stretch' => 'Strecken', 'baseline' => 'Grundlinie',
|
||
]],
|
||
'flexGrow' => ['group' => 'layout', 'type' => 'number', 'css' => 'flex-grow', 'label' => 'Wachsen', 'min' => 0, 'max' => 12, 'step' => 1],
|
||
'flexShrink' => ['group' => 'layout', 'type' => 'number', 'css' => 'flex-shrink', 'label' => 'Schrumpfen', 'min' => 0, 'max' => 12, 'step' => 1],
|
||
'flexBasis' => ['group' => 'layout', 'type' => 'length-auto', 'css' => 'flex-basis', 'label' => 'Grundbreite'],
|
||
'position' => ['group' => 'layout', 'type' => 'choice', 'css' => 'position', 'label' => 'Positionierung', 'values' => [
|
||
'static' => 'Im Fluss', 'relative' => 'Relativ verschoben', 'absolute' => 'Freigestellt', 'sticky' => 'Klebt beim Scrollen',
|
||
]],
|
||
'top' => ['group' => 'layout', 'type' => 'length-auto', 'css' => 'top', 'label' => 'Abstand von oben'],
|
||
'right' => ['group' => 'layout', 'type' => 'length-auto', 'css' => 'right', 'label' => 'Abstand von rechts'],
|
||
'bottom' => ['group' => 'layout', 'type' => 'length-auto', 'css' => 'bottom', 'label' => 'Abstand von unten'],
|
||
'left' => ['group' => 'layout', 'type' => 'length-auto', 'css' => 'left', 'label' => 'Abstand von links'],
|
||
'zIndex' => ['group' => 'layout', 'type' => 'number', 'css' => 'z-index', 'label' => 'Ebene (vorn/hinten)', 'min' => -10, 'max' => 100, 'step' => 1],
|
||
|
||
// ---- Wirkung ----------------------------------------------------------------
|
||
'opacity' => ['group' => 'effect', 'type' => 'number', 'css' => 'opacity', 'label' => 'Deckkraft', 'min' => 0, 'max' => 1, 'step' => 0.05, 'hover' => true],
|
||
'shadowX' => ['group' => 'effect', 'type' => 'length', 'css' => null, 'label' => 'Schatten waagerecht'],
|
||
'shadowY' => ['group' => 'effect', 'type' => 'length', 'css' => null, 'label' => 'Schatten senkrecht'],
|
||
'shadowBlur' => ['group' => 'effect', 'type' => 'length', 'css' => null, 'label' => 'Schatten weich'],
|
||
'shadowSpread' => ['group' => 'effect', 'type' => 'length', 'css' => null, 'label' => 'Schatten größer'],
|
||
'shadowColor' => ['group' => 'effect', 'type' => 'color', 'css' => null, 'label' => 'Schattenfarbe', 'hover' => true],
|
||
'translateX' => ['group' => 'effect', 'type' => 'length', 'css' => null, 'label' => 'Verschieben waagerecht', 'hover' => true],
|
||
'translateY' => ['group' => 'effect', 'type' => 'length', 'css' => null, 'label' => 'Verschieben senkrecht', 'hover' => true],
|
||
'scale' => ['group' => 'effect', 'type' => 'number', 'css' => null, 'label' => 'Skalierung', 'min' => 0.1, 'max' => 3, 'step' => 0.01, 'hover' => true],
|
||
'rotate' => ['group' => 'effect', 'type' => 'number', 'css' => null, 'label' => 'Drehung', 'min' => -360, 'max' => 360, 'step' => 1, 'hover' => true],
|
||
'shadowInset' => ['group' => 'effect', 'type' => 'choice', 'css' => null, 'label' => 'Schatten innen', 'values' => ['aussen' => 'Außen', 'innen' => 'Innen']],
|
||
'textShadowX' => ['group' => 'effect', 'type' => 'length', 'css' => null, 'label' => 'Textschatten waagerecht'],
|
||
'textShadowY' => ['group' => 'effect', 'type' => 'length', 'css' => null, 'label' => 'Textschatten senkrecht'],
|
||
'textShadowBlur' => ['group' => 'effect', 'type' => 'length', 'css' => null, 'label' => 'Textschatten weich'],
|
||
'textShadowColor' => ['group' => 'effect', 'type' => 'color', 'css' => null, 'label' => 'Textschattenfarbe'],
|
||
'blur' => ['group' => 'effect', 'type' => 'number', 'css' => null, 'label' => 'Weichzeichnen (px)', 'min' => 0, 'max' => 40, 'step' => 0.5, 'hover' => true],
|
||
'brightness' => ['group' => 'effect', 'type' => 'number', 'css' => null, 'label' => 'Helligkeit', 'min' => 0, 'max' => 3, 'step' => 0.05, 'hover' => true],
|
||
'contrast' => ['group' => 'effect', 'type' => 'number', 'css' => null, 'label' => 'Kontrast', 'min' => 0, 'max' => 3, 'step' => 0.05, 'hover' => true],
|
||
'saturate' => ['group' => 'effect', 'type' => 'number', 'css' => null, 'label' => 'Farbsättigung', 'min' => 0, 'max' => 3, 'step' => 0.05, 'hover' => true],
|
||
'grayscale' => ['group' => 'effect', 'type' => 'number', 'css' => null, 'label' => 'Graustufen', 'min' => 0, 'max' => 1, 'step' => 0.05, 'hover' => true],
|
||
'sepia' => ['group' => 'effect', 'type' => 'number', 'css' => null, 'label' => 'Sepia', 'min' => 0, 'max' => 1, 'step' => 0.05, 'hover' => true],
|
||
'hueRotate' => ['group' => 'effect', 'type' => 'number', 'css' => null, 'label' => 'Farbdrehung (Grad)', 'min' => -180, 'max' => 180, 'step' => 1, 'hover' => true],
|
||
'invert' => ['group' => 'effect', 'type' => 'number', 'css' => null, 'label' => 'Umkehren', 'min' => 0, 'max' => 1, 'step' => 0.05],
|
||
'mixBlendMode' => ['group' => 'effect', 'type' => 'choice', 'css' => 'mix-blend-mode', 'label' => 'Mischmodus', 'values' => [
|
||
'normal' => 'Normal', 'multiply' => 'Multiplizieren', 'screen' => 'Negativ multiplizieren', 'overlay' => 'Überlagern',
|
||
'darken' => 'Abdunkeln', 'lighten' => 'Aufhellen', 'color-dodge' => 'Farbig abwedeln', 'difference' => 'Differenz',
|
||
'exclusion' => 'Ausschluss', 'hue' => 'Farbton', 'saturation' => 'Sättigung', 'color' => 'Farbe', 'luminosity' => 'Leuchtkraft',
|
||
]],
|
||
'cursor' => ['group' => 'effect', 'type' => 'choice', 'css' => 'cursor', 'label' => 'Mauszeiger', 'values' => [
|
||
'auto' => 'Automatisch', 'pointer' => 'Hand', 'default' => 'Pfeil', 'not-allowed' => 'Gesperrt', 'grab' => 'Greifen', 'zoom-in' => 'Lupe',
|
||
]],
|
||
'transition' => ['group' => 'effect', 'type' => 'number', 'css' => null, 'label' => 'Übergangsdauer (ms)', 'min' => 0, 'max' => 2000, 'step' => 25],
|
||
// The slider controls are built by the script next to the slider, so their colours
|
||
// travel as custom properties onto the slider and its controls (target "controls").
|
||
// Der Menü-Knopf ist ein eigener Knopf im Menüblock, kein eigener Baustein: seine
|
||
// Farben liegen deshalb auf dem Block und wirken auf das summary-Element darin.
|
||
'burgerColor' => ['group' => 'menu', 'type' => 'color', 'css' => 'color', 'label' => 'Menü-Knopf: Schrift und Striche', 'target' => 'burger'],
|
||
'burgerBackgroundColor' => ['group' => 'menu', 'type' => 'color', 'css' => 'background-color', 'label' => 'Menü-Knopf: Fläche', 'target' => 'burger'],
|
||
'burgerBorderColor' => ['group' => 'menu', 'type' => 'color', 'css' => 'border-color', 'label' => 'Menü-Knopf: Rahmen', 'target' => 'burger'],
|
||
'burgerBorderWidth' => ['group' => 'menu', 'type' => 'length', 'css' => 'border-width', 'label' => 'Menü-Knopf: Rahmenstärke', 'target' => 'burger'],
|
||
'burgerBorderRadius' => ['group' => 'menu', 'type' => 'length', 'css' => 'border-radius', 'label' => 'Menü-Knopf: Ecken', 'target' => 'burger'],
|
||
'burgerFontSize' => ['group' => 'menu', 'type' => 'length', 'css' => 'font-size', 'label' => 'Menü-Knopf: Schriftgröße', 'target' => 'burger'],
|
||
'sliderDotColor' => ['group' => 'slider', 'type' => 'color', 'css' => '--slider-dot', 'label' => 'Punkte', 'target' => 'controls'],
|
||
'sliderDotActiveColor' => ['group' => 'slider', 'type' => 'color', 'css' => '--slider-dot-active', 'label' => 'Aktiver Punkt', 'target' => 'controls'],
|
||
'sliderControlColor' => ['group' => 'slider', 'type' => 'color', 'css' => '--slider-ink', 'label' => 'Pfeile und Zähler', 'target' => 'controls'],
|
||
'sliderControlBackground' => ['group' => 'slider', 'type' => 'color', 'css' => '--slider-bg', 'label' => 'Hintergrund der Knöpfe', 'target' => 'controls'],
|
||
];
|
||
}
|
||
|
||
/** Properties that are written onto the images inside a block instead of the block itself. */
|
||
public static function imageTargets(): array
|
||
{
|
||
return array_keys(array_filter(self::properties(), static fn (array $p): bool => 'img' === ($p['target'] ?? null)));
|
||
}
|
||
|
||
/** Properties that may also be given a hover value. */
|
||
public static function hoverable(): array
|
||
{
|
||
return array_keys(array_filter(self::properties(), static fn (array $p): bool => $p['hover'] ?? false));
|
||
}
|
||
|
||
/**
|
||
* Properties that may also carry a value for the scrolled page - the Flatsome transparent
|
||
* header that turns solid once the reader has scrolled. Colours, shadow and opacity only:
|
||
* a header that changes size while scrolling would push the page around under the finger.
|
||
*/
|
||
public static function scrollable(): array
|
||
{
|
||
return ['backgroundColor', 'color', 'borderColor', 'shadowColor', 'opacity'];
|
||
}
|
||
|
||
public static function sides(): array
|
||
{
|
||
return ['top' => 'Oben', 'right' => 'Rechts', 'bottom' => 'Unten', 'left' => 'Links'];
|
||
}
|
||
|
||
/** Corners round a box; they are not sides, and their CSS property is spelled differently. */
|
||
public static function corners(): array
|
||
{
|
||
return ['topLeft' => 'Oben links', 'topRight' => 'Oben rechts', 'bottomRight' => 'Unten rechts', 'bottomLeft' => 'Unten links'];
|
||
}
|
||
|
||
/** "topLeft" becomes the CSS fragment "top-left". */
|
||
public static function cornerCss(string $corner): string
|
||
{
|
||
return strtolower(preg_replace('/([A-Z])/', '-$1', $corner) ?? $corner);
|
||
}
|
||
}
|