Randlose Abschnitte, volle Bildschirmhoehe, Vollbild-Hero
Umuts Frage: "Haben wir bilder usw als volles bild startseite? 100% hoehe usw?" Die Teile gab es, die Kombination nicht - und zwei Sachen fehlten wirklich. Abschnittsbreiten: neu "randlos" (ohne Begrenzung, ohne Rand, von Kante zu Kante) und "breit" (1600 px) neben normal und schmal. Vorher war jeder Inhalt auf 1180 px begrenzt; ein Bild von Kante zu Kante war nicht moeglich. Hoeheneinheiten: neu dvh, svh, lvh. 100vh ist auf dem Handy hoeher als der Bildschirm - es meint die Ansicht ohne Adressleiste, und der untere Rand verschwindet darunter. 100dvh ist das, was jemand meint, der "volle Hoehe" sagt. Und eine Vorlage "Vollbild-Hero", die die ganze Kombination auf einen Klick setzt: randlos, 100dvh (mobil 80dvh), Inhalt senkrecht zentriert, Hintergrundbild formatfuellend, dunkle Ueberlagerung damit der Text lesbar bleibt, Ueberschrift, Text, Button. Die Teile waren alle da, aber diese Kombination zu finden war eine Recherche. 328 PHP-Tests / 1329 Assertions, 17 JS-Modelltests. Geprueft: vier Breiten angenommen und eine erfundene abgewiesen, sieben Hoehenangaben mit dvh/svh/lvh und zwei ungueltige, ein randloser Abschnitt mit formatfuellendem Bild, und die Hero-Vorlage Eigenschaft fuer Eigenschaft. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
464f52b147
commit
67b85751cf
11 files changed
+165
-7
No files matched your search
@@ -19,11 +19,12 @@ export const GROUPS = {
|
||||
effect: 'Wirkung',
|
||||
};
|
||||
|
||||
const UNITS = ['px', 'rem', 'em', '%', 'vw', 'vh', 'ch'];
|
||||
// dvh before vh: on a phone 100vh is taller than the screen, 100dvh is what people mean.
|
||||
const UNITS = ['px', 'rem', 'em', '%', 'vw', 'dvh', 'vh', 'svh', 'lvh', 'dvw', 'ch'];
|
||||
|
||||
/** Splits "18.5rem" into its number and its unit; an empty value yields empty parts. */
|
||||
function splitLength(value) {
|
||||
const match = /^(-?\d+(?:\.\d+)?)(px|rem|em|%|vw|vh|ch)$/.exec(value ?? '');
|
||||
const match = /^(-?\d+(?:\.\d+)?)(px|rem|em|%|vw|vh|ch|dvh|svh|lvh|dvw)$/.exec(value ?? '');
|
||||
if (match) return { number: match[1], unit: match[2] };
|
||||
return { number: value === '0' ? '0' : '', unit: 'px' };
|
||||
}
|
||||
|
||||
@@ -178,6 +178,40 @@ export function insertMedia(document, selected, items) {
|
||||
* A row on its own looks like nothing, which is why "add a row" was hard to find: what people
|
||||
* want is a row that already has columns in it.
|
||||
*/
|
||||
/**
|
||||
* A full screen hero: edge to edge, as tall as the screen, content centred, with room for a
|
||||
* background image and a dark overlay so text stays readable on any photo.
|
||||
*
|
||||
* The parts all exist on their own - width "full", min-height in dvh, display flex, overlay - but
|
||||
* finding that combination is a research project. This is the one click.
|
||||
*/
|
||||
export function insertHero(document, selected) {
|
||||
const section = createBlock('section');
|
||||
section.layout = { tone: 'ink', width: 'full' };
|
||||
section.style = {
|
||||
desktop: {
|
||||
// dvh, not vh: on a phone 100vh is taller than the screen.
|
||||
minHeight: '100dvh', display: 'flex', alignItems: 'center',
|
||||
backgroundSize: 'cover', backgroundPosition: 'center', backgroundRepeat: 'no-repeat',
|
||||
overlayColor: '#000000', overlayOpacity: 0.4,
|
||||
padding: { top: '80px', right: '32px', bottom: '80px', left: '32px' },
|
||||
textAlign: 'center',
|
||||
},
|
||||
mobile: { minHeight: '80dvh', padding: { top: '56px', right: '20px', bottom: '56px', left: '20px' } },
|
||||
};
|
||||
const heading = createBlock('heading');
|
||||
heading.props = { text: 'Deine Botschaft, ganzflächig.', level: 1 };
|
||||
const paragraph = createBlock('text');
|
||||
paragraph.props = { text: 'Ein Satz, der sagt, worum es geht. Wähle im Designbereich unter Hintergrund ein Bild und stelle die Deckkraft der Überlagerung so ein, dass der Text lesbar bleibt.' };
|
||||
const button = createBlock('button');
|
||||
section.children = [heading, paragraph, button];
|
||||
upgradeDocument(document, 10);
|
||||
const top = blockPath(document.blocks, selected)[0];
|
||||
const index = top ? document.blocks.findIndex(block => block.id === top.id) + 1 : document.blocks.length;
|
||||
document.blocks.splice(index, 0, section);
|
||||
return section.id;
|
||||
}
|
||||
|
||||
export function insertRow(document, columns, selected) {
|
||||
const count = Math.max(1, Math.min(6, Number(columns) || 2));
|
||||
const names = { 1: 'one', 2: 'two', 3: 'three', 4: 'four' };
|
||||
@@ -193,6 +227,7 @@ export function insertRow(document, columns, selected) {
|
||||
}
|
||||
|
||||
export function insertPreset(document, name, selected) {
|
||||
if (name === 'hero') return insertHero(document, selected);
|
||||
if (!['intro', 'columns', 'cta'].includes(name)) throw new Error('Unbekannte Vorlage.');
|
||||
const section = createBlock('section');
|
||||
const heading = text => { const block = createBlock('heading'); block.props.text = text; return block; };
|
||||
|
||||
@@ -299,7 +299,7 @@ function renderProperties() {
|
||||
}
|
||||
if (block.type === 'section') {
|
||||
field('Farbwelt', 'tone', block.layout?.tone || 'paper', { layout: true, choices: { paper: 'Hell · Papier', mint: 'Hellgrün · Akzent', ink: 'Dunkel · Tinte' } });
|
||||
field('Inhaltsbreite', 'width', block.layout?.width || 'normal', { layout: true, choices: { normal: 'Normal', narrow: 'Schmal' } });
|
||||
field('Inhaltsbreite', 'width', block.layout?.width || 'normal', { layout: true, choices: { normal: 'Normal (1180 px)', narrow: 'Schmal (800 px)', wide: 'Breit (1600 px)', full: 'Randlos (volle Breite)' }, help: 'Randlos heißt ohne Rand und ohne Begrenzung – für ein Bild oder eine Farbfläche von Kante zu Kante.' });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -304,3 +304,14 @@ body.consent-open{padding-bottom:260px}
|
||||
.progress-fill-98{width:98%}
|
||||
.progress-fill-99{width:99%}
|
||||
.progress-fill-100{width:100%}
|
||||
|
||||
/* Section widths. "wide" only widens the content, "full" removes the container entirely: that is
|
||||
what a picture or a colour field from edge to edge needs. A full width section drops its own
|
||||
padding as well, otherwise "edge to edge" would still have 32 pixels of air on each side. */
|
||||
.width-wide>.section-inner{width:min(1600px,calc(100% - 64px))}
|
||||
.width-full>.section-inner{width:100%;max-width:none;margin-inline:0}
|
||||
@media (max-width:650px){.width-wide>.section-inner{width:calc(100% - 36px)}}
|
||||
|
||||
/* A section that is a flex container centres its content box; the inner container must then be
|
||||
allowed to grow, or a full height section would show a strip in the middle. */
|
||||
.block-section>.section-inner{flex:1 1 auto}
|
||||
Reference in new issue
Block a user