news.js: Variablen-Defaults ohne Spezifitaet, Combo-Umbruch, Bildmaße absichern

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Opus 5.5 committed 2026-09-30 17:19:40 +00:00
1 parent 035d1d5517
commit 0e0776b406
3 files changed
+43 -15

No files matched your search

+2 -2
View File
@@ -11,7 +11,7 @@ Anleitung für das WEBklar-Team. Die Pinnwand zeigt die Beiträge, die der Kunde
```
- `data-webklar-news`: der siteKey des Projekts (Pflicht).
- `data-api`: Herkunft des Portals. Ohne Angabe nimmt das Skript die Herkunft aus der Adresse des Skripts.
- `data-api`: Herkunft des Portals. Ohne Angabe nimmt das Skript die Herkunft aus der Adresse des Skripts. Das klappt nur, wenn es als normales `<script src>` geladen wird. Bei Modul-Import, Tag-Manager oder nachgeladenem Skript ist `data-api` Pflicht.
- `data-signature` (optional): Unterschrift unter jedem Zettel, z. B. `data-signature="Opa Martin"`. Ohne Attribut gibt es keine Unterschrift.
## 2. Das Skript (einmal pro Seite, vor `</body>`)
@@ -52,7 +52,7 @@ Setze sie auf dem `div` oder in der Seiten-CSS, z. B. `[data-webklar-news]{ --wk
Die Schriften lädt das Skript nicht selbst. Sind sie auf der Kundenseite nicht eingebunden, greift die Systemschrift. Setze dann die Variablen auf die Schriften der Kundenseite.
Das Layout richtet sich nach der Breite des `div` (Container Query), nicht nach dem Bildschirm: ab 701 px Breite ein Korkbrett mit drei Spalten, darunter eine Wischreihe mit Punkten. Sechs Beiträge pro Seite, „Ältere Neuigkeiten ansehen“ lädt die nächsten nach. Alle Klassen beginnen mit `wkn-`, die Styles stehen einmalig in `<style id="wkn-style">`.
Das Layout richtet sich nach der Breite des `div` (Container Query, braucht aktuelle Browser), nicht nach dem Bildschirm: ab 701 px Breite ein Korkbrett mit drei Spalten, darunter eine Wischreihe mit Punkten. Sechs Beiträge pro Seite, „Ältere Neuigkeiten ansehen“ lädt die nächsten nach. Alle Klassen beginnen mit `wkn-`, die Styles stehen einmalig in `<style id="wkn-style">`.
## 5. Datenschutzerklärung des Kunden
+20 -13
View File
@@ -10,7 +10,8 @@
var PAPERS = { sun: 1, sky: 1, coral: 1, white: 1 }
var CSS = [
'.wkn-root{container-type:inline-size;display:block;--wkn-board:#C08A52;--wkn-frame:#A8733F;--wkn-font-display:"Fredoka",system-ui,sans-serif;--wkn-font-body:"Nunito",system-ui,sans-serif;--wkn-paper-sun:#FFF6D6;--wkn-paper-sky:#DDEEFB;--wkn-paper-coral:#FFE1DA;--wkn-paper-white:#fff;--wkn-ink:#232A33;--wkn-ink-soft:#5B6673;font-family:var(--wkn-font-body)}',
'.wkn-root{container:wkn-root/inline-size;display:block}',
':where(.wkn-root){--wkn-board:#C08A52;--wkn-frame:#A8733F;--wkn-font-display:"Fredoka",system-ui,sans-serif;--wkn-font-body:"Nunito",system-ui,sans-serif;--wkn-paper-sun:#FFF6D6;--wkn-paper-sky:#DDEEFB;--wkn-paper-coral:#FFE1DA;--wkn-paper-white:#fff;--wkn-ink:#232A33;--wkn-ink-soft:#5B6673;font-family:var(--wkn-font-body)}',
'.wkn-root *,.wkn-root *::before,.wkn-root *::after{box-sizing:border-box}',
'.wkn-board{position:relative;isolation:isolate;overflow:hidden;border-radius:28px;padding:40px 32px 48px;background-color:var(--wkn-board);',
'background-image:url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%27260%27 height=%27260%27%3E%3Cfilter id=%27a%27%3E%3CfeTurbulence type=%27fractalNoise%27 baseFrequency=%271.1%27 numOctaves=%272%27 seed=%273%27/%3E%3CfeColorMatrix values=%270 0 0 0 .29 0 0 0 0 .16 0 0 0 0 .06 0 0 0 -2.4 1.25%27/%3E%3C/filter%3E%3Crect width=%27100%25%27 height=%27100%25%27 filter=%27url(%23a)%27/%3E%3C/svg%3E"),url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%27220%27 height=%27220%27%3E%3Cfilter id=%27b%27%3E%3CfeTurbulence type=%27fractalNoise%27 baseFrequency=%27.85%27 numOctaves=%272%27 seed=%2711%27/%3E%3CfeColorMatrix values=%270 0 0 0 .95 0 0 0 0 .8 0 0 0 0 .58 0 0 0 2.6 -1.55%27/%3E%3C/filter%3E%3Crect width=%27100%25%27 height=%27100%25%27 filter=%27url(%23b)%27/%3E%3C/svg%3E"),url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%27600%27 height=%27600%27%3E%3Cfilter id=%27c%27%3E%3CfeTurbulence type=%27fractalNoise%27 baseFrequency=%27.012%27 numOctaves=%273%27 seed=%277%27/%3E%3CfeColorMatrix values=%270 0 0 0 .45 0 0 0 0 .27 0 0 0 0 .12 0 0 0 1.4 -.45%27/%3E%3C/filter%3E%3Crect width=%27100%25%27 height=%27100%25%27 filter=%27url(%23c)%27/%3E%3C/svg%3E");',
@@ -25,14 +26,15 @@
'.wkn-new{position:absolute;top:-14px;right:-10px;z-index:3;transform:rotate(8deg);background:#E4573D;color:#fff;font:700 12px/1 var(--wkn-font-body);letter-spacing:.04em;padding:7px 12px;border-radius:999px;box-shadow:0 3px 8px rgba(35,52,74,.25)}',
'.wkn-note{padding:28px 24px 22px;border-radius:6px 6px 18px 6px;box-shadow:0 8px 18px rgba(35,52,74,.14);background:var(--wkn-paper,#fff)}',
'.wkn-note h3{font:600 20px/1.25 var(--wkn-font-display);color:var(--wkn-ink);margin:8px 0}',
'.wkn-note p{font:400 16px/1.5 var(--wkn-font-body);color:var(--wkn-ink);margin:0;white-space:pre-line;overflow-wrap:anywhere}',
'.wkn-lead .wkn-note h3{font-size:28px}',
'.wkn-note p{font:400 16px/1.5 var(--wkn-font-body);color:var(--wkn-ink);margin:0;white-space:pre-line;overflow-wrap:break-word;hyphens:auto}',
'.wkn-lead .wkn-note h3,.wkn-lead.wkn-note h3{font-size:28px}',
'.wkn-paper-sun{--wkn-paper:var(--wkn-paper-sun)}.wkn-paper-sky{--wkn-paper:var(--wkn-paper-sky)}.wkn-paper-coral{--wkn-paper:var(--wkn-paper-coral)}.wkn-paper-white{--wkn-paper:var(--wkn-paper-white)}',
'.wkn-polaroid{margin:0;background:#fff;padding:12px 12px 16px;border-radius:4px;box-shadow:0 10px 22px rgba(35,52,74,.16)}',
'.wkn-polaroid img{display:block;width:100%;height:auto;aspect-ratio:4/3;object-fit:cover;border-radius:2px;background:#eef0f3}',
'.wkn-polaroid figcaption{display:flex;justify-content:space-between;gap:12px;align-items:baseline;margin-top:12px}',
'.wkn-polaroid figcaption b{font:600 17px/1.25 var(--wkn-font-display);color:var(--wkn-ink);overflow-wrap:anywhere}',
'.wkn-combo{display:grid;grid-template-columns:1.25fr 1fr;align-items:center}',
'.wkn-polaroid figcaption b{font:600 17px/1.25 var(--wkn-font-display);color:var(--wkn-ink);overflow-wrap:break-word;hyphens:auto}',
'.wkn-combo{container:wkn-card/inline-size}',
'.wkn-combo-in{display:grid;grid-template-columns:1.25fr 1fr;align-items:center}',
'.wkn-combo .wkn-polaroid{transform:rotate(-2deg)}',
'.wkn-combo .wkn-note{margin-left:-28px;transform:rotate(2.5deg);z-index:1}',
'.wkn-combo::before{left:28%}',
@@ -44,18 +46,18 @@
'.wkn-dots{display:flex;gap:6px;margin-right:8px}',
'.wkn-dots i{width:7px;height:7px;border-radius:50%;background:rgba(35,42,51,.2);transition:width .2s}',
'.wkn-dots i.wkn-on{background:var(--wkn-ink);width:18px;border-radius:999px}',
'@container (max-width:700px){',
'@container wkn-root (max-width:700px){',
'.wkn-board{padding:32px 0;border-radius:20px}',
'.wkn-grid{display:flex;overflow-x:auto;scroll-snap-type:x mandatory;gap:18px;padding:28px 20px 12px;scrollbar-width:none}',
'.wkn-grid::-webkit-scrollbar{display:none}',
'.wkn-grid>*{flex:0 0 76%;scroll-snap-align:center}',
'.wkn-grid>:first-child{grid-column:auto}',
'.wkn-combo{grid-template-columns:1fr}',
'.wkn-combo .wkn-note{margin:-26px 12px 0 26px}',
'.wkn-lead .wkn-note h3{font-size:20px}',
'.wkn-lead .wkn-note h3,.wkn-lead.wkn-note h3{font-size:20px}',
'.wkn-more{margin-top:24px;padding-inline:20px}.wkn-more .wkn-btn{width:100%}',
'.wkn-swipehint{display:flex}',
'}',
'@container wkn-card (max-width:340px){.wkn-combo-in{grid-template-columns:1fr}.wkn-combo .wkn-note{margin:-26px 12px 0 26px}}',
'@media (prefers-reduced-motion:reduce){.wkn-pin,.wkn-btn,.wkn-dots i{transition:none}}'
].join('\n')
@@ -99,14 +101,17 @@
var fig = el('figure', 'wkn-polaroid')
if (p.image) {
var img = document.createElement('img')
var iw = Number.isFinite(+p.image.w) && +p.image.w > 0 ? Math.round(+p.image.w) : 1600
var ih = Number.isFinite(+p.image.h) && +p.image.h > 0 ? Math.round(+p.image.h) : 1200
var small = absUrl(api, p.image.srcSmall)
var full = absUrl(api, p.image.src)
img.src = small || full
if (small && full) {
img.srcset = small + ' 480w, ' + full + ' ' + (p.image.w || 1600) + 'w'
img.srcset = small + ' 480w, ' + full + ' ' + iw + 'w'
img.sizes = '(max-width:700px) 80vw, 480px'
}
if (p.image.w && p.image.h) { img.width = p.image.w; img.height = p.image.h }
img.width = iw
img.height = ih
img.alt = p.title || ''
img.loading = 'lazy'
img.decoding = 'async'
@@ -133,8 +138,10 @@
} else if (p.type === 'combo' && p.image) {
item = el('article', cls + ' wkn-combo')
item.style.setProperty('--wkn-r', '0deg')
item.appendChild(buildPhoto(p, api, false))
item.appendChild(buildNote(p))
var inner = el('div', 'wkn-combo-in')
inner.appendChild(buildPhoto(p, api, false))
inner.appendChild(buildNote(p))
item.appendChild(inner)
} else {
item = el('article', cls)
item.style.setProperty('--wkn-r', r + 'deg')
+21
View File
@@ -183,6 +183,27 @@ await check('ohne_titel_keine_ueberschrift', async (page) => {
assert((await page.locator('.wkn-sig').count()) === 0, 'Signatur ohne Attribut')
})
await check('seiten_css_ueberschreibt_variablen', async (page) => {
await mockApi(page, { total: 1 })
await page.route('**/embed/news-demo.html', async (route) => {
const r = await route.fetch()
await route.fulfill({ response: r, body: (await r.text()).replace('</head>', '<style>[data-webklar-news]{--wkn-frame:rgb(10, 20, 30)}</style></head>') })
})
await page.goto(DEMO)
await page.locator('.wkn-pin').first().waitFor()
const c = await page.evaluate(() => getComputedStyle(document.querySelector('.wkn-board')).borderTopColor)
assert(c === 'rgb(10, 20, 30)', 'Rahmenfarbe nicht ueberschrieben: ' + c)
})
await check('combo_notiz_nie_schmal', async (page) => {
await mockApi(page, { total: 6 })
await page.goto(DEMO)
await page.locator('.wkn-pin').first().waitFor()
const w = await page.evaluate(() => [...document.querySelectorAll('.wkn-combo')].map((c) => ({ card: c.clientWidth, note: c.querySelector('.wkn-note').clientWidth, stacked: getComputedStyle(c.querySelector('.wkn-combo-in')).gridTemplateColumns.split(' ').length === 1 })))
assert(w.length > 0, 'kein Combo')
for (const x of w) assert(x.stacked ? true : x.note >= 140, 'Notiz zu schmal: ' + JSON.stringify(x))
})
// Screenshots
for (const [label, viewport] of [['desktop', { width: 1280, height: 900 }], ['phone', { width: 390, height: 844 }]]) {
for (const n of [1, 8]) {