Files
KantSites/templates/page.html.twig
T
kantarciandClaude Opus 5 e21dff91fe
CMS quality / php (8.2) (push) Canceled after 0s
CMS quality / php (8.4) (push) Canceled after 0s
CMS quality / browser (push) Canceled after 0s
CMS quality / mysql (push) Canceled after 0s
Slider mit Punkten, Karten als Karten, hervorgehobenes Wort
Umut hat den Nachbau Detail fuer Detail mit kant-it.de verglichen. Vier
Stellen, an denen das CMS etwas nicht konnte oder etwas Falsches tat.

Der Slider zeigte eine Rollleiste statt Punkte. Punkte zeichnet jetzt der
Browser selbst - scroll-marker-group und ::scroll-marker, ohne Skript, mit
erkennbarem aktivem Punkt. Wo ein Browser das noch nicht kennt, bleibt das
wischbare Schiebefeld. Pfeile gibt es bewusst nicht: Chrome legt
::scroll-button an den Seitenrand statt in den Slider. Und er rotiert
nicht von selbst, weil eine oeffentliche Seite kein JavaScript laedt.

Eine Beitragskarte war keine Karte, sondern eine lose Spalte. Jetzt weiss,
18px Ecken, Bild buendig oben im Verhaeltnis 16:10, Text darunter.

Die Theme-Schriftskala ueberschrieb die Groessen der Bausteine: jede
Kartenueberschrift war so gross wie eine Abschnittsueberschrift.

Eine Ueberschrift darf ab v17 ein Wort hervorheben. Dabei waere beinahe
eine Luecke entstanden: die Dokumentfassung war in der Schliessung des
Filters nicht gebunden, der Filter lief nie, die Ausgabe war schon
unescaped. Ein Test mit <script> in einer Ueberschrift hat es gezeigt.

Geprueft: 390 PHP-Tests unter 8.2 und 8.4, 18 JS-Tests, Editortest,
Browserpruefung, axe ohne Verstoss auf dem Nachbau.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 00:23:02 +02:00

78 lines
6.2 KiB
Twig
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="{{ page.locale }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ page.document.title }}</title>
<meta name="description" content="{{ page.document.description }}">
<link rel="canonical" href="{{ canonical }}">
{% if page.robots|default('index') != 'index' %}<meta name="robots" content="noindex, follow">{% endif %}
<meta property="og:title" content="{{ page.document.title }}">
<meta property="og:description" content="{{ page.document.description }}">
<meta property="og:type" content="{{ page.type|default('page') == 'post' ? 'article' : 'website' }}">
<meta property="og:url" content="{{ canonical }}">
{% if socialImage|default(null) %}<meta property="og:image" content="{{ socialImage }}"><meta name="twitter:card" content="summary_large_image">{% endif %}
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="/assets/site.css">
{% if googleFonts|default(null) %}<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link rel="stylesheet" href="{{ googleFonts }}">{% endif %}
<link rel="alternate" type="application/rss+xml" title="Beiträge" href="/feed.xml">
{% if page.document.schemaVersion >= 2 %}<link rel="stylesheet" href="/assets/design.css">{% endif %}
{% if page.document.schemaVersion >= 3 %}<link rel="stylesheet" href="/assets/enhancements.css">{% endif %}
{% if needsTabs|default(false) %}<script src="/assets/tabs.js" defer></script>{% endif %}
{# Own scripts of this page, as a file. Never rendered in the editor preview: the preview must
show the layout, not run code that could change the page under the editor's hands. #}
{% if pageScript|default(null) and not editing|default(false) %}<script src="{{ pageScript }}" defer></script>{% endif %}
{# A data block, not a script: CSP script-src does not apply to application/ld+json. #}
{% for block in structuredData|default([]) %}<script type="application/ld+json">{{ block|json_encode(constant('JSON_UNESCAPED_SLASHES') b-or constant('JSON_UNESCAPED_UNICODE'))|raw }}</script>
{% endfor %}
{% if pageStyles|default('') %}
{# In the editor the preview inherits the administration's policy, which forbids inline
styles. The stylesheet therefore travels as inert template text and the editor applies
it as a constructed stylesheet – same result, no policy violation and a clean console. #}
{% if editing|default(false) %}<template id="kant-styles">{{ pageStyles|raw }}</template>
{% else %}<style>{{ pageStyles|raw }}</style>{% endif %}
{% endif %}
</head>
{% set consentOpen = not editing|default(false) and consentGate.needsBanner(app.request) %}
<body class="kant{{ consentOpen ? ' consent-open' : '' }}">
<a class="skip-link" href="#main">Zum Inhalt springen</a>
{# Header and footer are block documents like any page, so they are built from elements
instead of being written into this template. #}
{% if areas.header|default(null) %}
<header class="site-header{{ siteSettings.load().stickyHeader ? ' is-sticky' : '' }}">
{% for block in areas.header.blocks %}{% include 'blocks/block.html.twig' with {block: block, editing: editing|default(false), postLists: {}, forms: {}, breadcrumbs: [], navigation: navigation|default({}), sections: sections|default({}), richText: areas.header.schemaVersion|default(1) >= 12, richHeadings: areas.header.schemaVersion|default(1) >= 17, eagerImage: eagerImage|default(null)} only %}{% endfor %}
</header>
{% endif %}
<main id="main" tabindex="-1">
{% if postMeta is defined and postMeta is not null %}
<p class="post-header shell">
{% if page.published_at %}<time datetime="{{ page.published_at|date('Y-m-d') }}">{{ page.published_at|date('d.m.Y') }}</time>{% endif %}
{% if page.author %}<span>von {{ page.author }}</span>{% endif %}
{% for term in postMeta.category %}<a href="{{ term.href }}">{{ term.name }}</a>{% endfor %}
{% for term in postMeta.tag %}<a class="post-tag" href="{{ term.href }}">#{{ term.name }}</a>{% endfor %}
</p>
{% endif %}
{% for block in page.document.blocks %}{% include 'blocks/block.html.twig' with {block: block, editing: editing|default(false), postLists: postLists|default({}), forms: forms|default({}), breadcrumbs: breadcrumbs|default([]), navigation: navigation|default({}), sections: sections|default({}), richText: page.document.schemaVersion|default(1) >= 12, richHeadings: page.document.schemaVersion|default(1) >= 17, eagerImage: eagerImage|default(null)} only %}{% endfor %}
</main>
{% if areas.footer|default(null) %}
<footer class="site-footer">
{% for block in areas.footer.blocks %}{% include 'blocks/block.html.twig' with {block: block, editing: editing|default(false), postLists: {}, forms: {}, breadcrumbs: [], navigation: navigation|default({}), sections: sections|default({}), richText: areas.footer.schemaVersion|default(1) >= 12, richHeadings: areas.footer.schemaVersion|default(1) >= 17, eagerImage: null} only %}{% endfor %}
</footer>
{% endif %}
{# Privacy statement, imprint and the way back to the consent decision. All three have to be
reachable from every page, so they are part of the template and not of the footer area,
which someone could delete. One labelled landmark, so they are not loose text either. #}
{% set legal = consentGate.legalLinks() %}
{% set revocable = not editing|default(false) and consentGate.askedGroups is not empty and not consentGate.needsBanner(app.request) %}
{% if legal is not empty or revocable %}
<nav class="legal-bar" aria-label="Rechtliches">
{% for path, label in legal %}{% if not loop.first %} · {% endif %}<a href="/{{ path }}">{{ label }}</a>{% endfor %}
{% if revocable %}{% if legal is not empty %} · {% endif %}<a href="/einwilligung">Einwilligung ändern</a>{% endif %}
</nav>
{% endif %}
{% if not editing|default(false) and consentGate.needsBanner(app.request) %}
{% include 'consent-banner.html.twig' with {consent: consentGate.state(app.request), back: app.request.pathInfo} only %}
{% endif %}
</body>
</html>