The request that swaps the files now ends with a plain page instead of running on through the
framework with old code in memory and new code on disk - that is what answered with a 500 after
a successful update. The page calls the second step itself with a one-time key: migrations,
clean-up, maintenance off, back to the system screen (ADR 112).
Turkish is complete in the fixed template texts and form messages. Two language switches on one
page get distinct landmark names. Kantarci Reisen gets a Turkish version of every page, the
header, footer, menu and enquiry form, with a DE · TR switch; the reset script applies it.
The DM Sans 600 files are needed after all and are committed with fonts.css.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A site can have further languages (Settings → Languages). A page is built once; each language
adds only its texts, keyed by the stable block IDs, plus its own address under /en/… (ADR 111,
document schema v27, migration 20261009000100 for page_translation).
- Editor: DE | EN switch, a list of all texts with original, state (missing, translated,
original changed) and filter; structure stays with the main language. Undo, drafts,
publishing and revisions cover all languages at once.
- Website: /en and translated slugs, lang, canonical, hreflang with x-default, og:locale,
sitemap; links to translated pages follow the language; a language switch block.
- Header, footer and linked sections translate the same way; menu entries carry labels per
language; fixed template texts come from a small dictionary.
- Forms: labels, help, options, button and success message per language; messages and the
step script speak the language, the submission and the email stay in the main language.
- Posts, archives, category names, consent text and the 404 page follow the language.
- Single-language sites see none of it and never touch the new table.
The forms administration becomes an overview with one row per form and its own edit page;
fields are one line each with details collapsed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Seed, reset and asset scripts for the tenth installation, plus dev and prod launch entries
for it and for the multilingual test copy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Check labels spanned the full width, so a click in the empty middle of
"Als Button darstellen" unticked it and folded the menu button settings.
The label now ends after its text.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The menu editor gets "Als Button darstellen" per entry: shape, appearance and
size from the button design, fill and ink with hover and palette, icon colour,
icon from the dialog, motion, icon-only and where the button sits on the phone
(in the opened menu, beside the burger, or there as an icon only). Colours go
into the page stylesheet under a stable per-button id because the CSP forbids
inline styles. ADR 110.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since 1.25.0 the icon set has a truck, so the fleet advantage no longer needs
an own-HTML block with the template's SVG. The seed builds it like its three
neighbours (icon block, 32 px, light green); the local Hüfner database was
patched the same way and republished.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The icon set grows from 33 to 98 drawings in seven categories (arrows,
contact, commerce, files and media, people and security, house and trade,
general), including truck, cart, euro, camera, tools and solar. icons.js is
the single source; npm run build:icons writes the Twig drawings and the
validator list, and a test keeps them equal.
Choosing an icon no longer fills the panel with tiles: the panel shows the
current icon with "Change", which opens a modal with category tabs and named
tiles, keyboard navigable, closing with Escape. Button, icon and icon card
share it.
A new style group "Symbol" targets the icon inside a button per device and
on hover: colour, background, size, padding, radius, rotation, plus the gap
to the text. Schema v26 adds hover motion (slide, reveal) and an icon-only
button whose label stays as the accessible name and keeps the line height.
ADR 109.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Schema v25: a button takes an optional icon (any name from the shared set or
"none") and an icon position (start or end). Without a choice it keeps the
diagonal arrow behind the text, still with the button-arrow class, so stored
pages and their own CSS look exactly as before.
Thirteen everyday icons join the set (arrow down, chevron, calendar, message,
send, download, document, play, search, heart, info, plus, person). Button,
icon and icon card choose from a tile picker with a drawing per icon, grouped,
keyboard navigable, instead of a select with English keys. icons.js holds the
paths for the editor; a test keeps them equal to the Twig drawings and the
validator list. ADR 108.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Website of Garten- und Landschaftsbau Hüfner GmbH rebuilt from the delivered design with
imprint, privacy, 404 and the enquiry form (scripts/reset-huefner.sh, ports 8102/8103),
Barlow and Barlow Condensed served locally with their licence.
- Buttons, icon cards, calls to action, icons and linked images accept tel: and mailto:
links, the same targets menus and rich text already allowed (ADR 107).
- The arrow inside a button takes the button's text colour instead of the accent colour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- /eroeffnung (noindex, unlinked): opening hero, offers embedded from
app.kantarci-markt.de without title and app button, app store buttons,
visit section with route, hours and contact placeholders.
- The height script on the embed asks the app for its height once it runs
(it is deferred and missed the first report) and scrolls to the offer
popup when the app reports it outside the viewport.
- audit.mjs takes CMS_HOST_RULES, because the app allows framing only from
localhost while the server listens on 127.0.0.1.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Coming-soon page "Kantarci Markt – Neueröffnung" rebuilt from the delivered package with
imprint, privacy and 404 (scripts/reset-kantarci-markt.sh, ports 8100/8101).
- Own CSS accepts a short quoted string for `content`, so ::before/::after work.
- cms:fonts:fetch replaces only the rules of the fetched families in fonts.css.
- A variable font is linked and preloaded once with a weight range instead of once per weight.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The kant-it.de header used the hamburger switch at schema 19, so the server refused its
preview and save; the editor now raises a loaded document to the version its blocks and
properties require. The admin, button and navigation checks select blocks through the
structure tab, find the motion button by id and expect what the site's motion setting says.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A button inside a summary is an interactive element in an interactive element; screen
readers announce it unreliably. The handle keeps its look and its mouse, touch and arrow
key behaviour. Also corrects the anchor hints and waits for the menu error in the admin check.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A menu entry picks its anchor from the linked page, link settings of plugins and own menu
links get page and PDF choice next to the address, and the post settings offer existing
categories and tags to click.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The share image and featured image of a page become image fields with the library dialog,
as do image settings of plugins. The editor offers forms, sections, categories and tags as
lists, a page search next to every link target and in text links, the redirect target has a
page search, and the form editor picks the reply field, the condition and the option images.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PDFs go through the same upload field as images, are checked by their content and kept
unchanged in their own table. Every link field can pick one, and a page that links a PDF
opens it in a dialog with the browser's own viewer; touch devices and browsers without a
viewer get a new tab instead.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- System > This installation > Address has a field and a link to the other
spelling (with or without www), like the WordPress address. It writes
SITE_ORIGIN to .env through Environment::write and takes effect on the
next request. With SITE_ORIGIN from the server environment there is no
field, and a POST is refused.
- Before saving, OriginCheck requests /.well-known/kantsites-origin under the
new address; it has to return an HMAC of APP_SECRET that only this
installation knows. Switching to www without DNS or certificate would
otherwise send every page, the administration included, nowhere. "Save
anyway" appears only after a refusal. The probe also answers during
maintenance.
- The message says when the administration changes host and needs a new
sign-in, since the session cookie belongs to one spelling.
Tests: 584 PHPUnit on PHP 8.2 and 8.4, 35 node, test:editor and
test:browser green (test:browser reports only the known editor colour
contrast on /grundlagen).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The file copy still runs the code of the installed version, which does not
know the list; the archive check from 1.21.6 to 1.21.7 left the file behind.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Google kept showing the KantSites K for Elektro Bernhard after the site
icon was changed. The own icon only existed at 32 and 180 px, and Google
takes multiples of 48 only; /favicon.svg was a file in public/, so the web
server always delivered the bundled K.
- The icon is now also cut at 48 and 192 px. The head links /favicon-192.png,
/favicon.ico serves the 48 px PNG, and /favicon.svg is answered by the
application: the bundled mark without an own icon (now in
public/assets/favicon.svg), otherwise a 302 to the 192 px PNG. Sites that
set their icon earlier get the new sizes on first request. A removed icon
no longer counts even though its files remain.
- The archive update only adds files to public/, so it now deletes the old
public/favicon.svg explicitly.
- Seventh local reference site: the KANTARCI Immobilien landing page with a
three-step rental request, own database, seed and reset scripts.
Tests: 582 PHPUnit on PHP 8.2 and 8.4, 35 node, test:editor and
test:browser green (test:browser reports only the known editor colour
contrast on /grundlagen).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- The site icon is no longer a select at the end of the company data. It has
its own section: a button opens the media library (upload included), then
a square is dragged, resized by its corner, a slider or +/-, and previewed
at 180 and 32 px. The crop is stored as fractions (faviconCrop) and the
PNGs are cut from it; faviconVersion changes the link so browsers pick up
a new cut. Icons without a crop keep their old link.
- openMedia() takes needsAlt: false, since the icon never appears as
content. The dialog styles moved from editor.css to media-dialog.css so
the dialog works outside the editor.
- Privacy page and imprint are chosen with the content search, like the 404
page. The picker can store the path instead of the id (valueKey/data-value).
- The page and post lists in the administration sort newest first; the
subpage indentation is gone there, as it would point at the wrong parent.
Tests: 581 PHPUnit, 35 node, test:editor green (the consent step now picks
the privacy page by search). test:admin --admin-only fails only at the known
nested-interactive menu summaries.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Built while setting up the redesign of ebsolar.de as the sixth local
installation (scripts/seed-ebsolar.php, reset-ebsolar.sh, ports 8096/8097).
The rebuild surfaced four things in the core:
- The icon set knows three arrows now: arrow-right, arrow-left,
arrow-up-right. The button block and the post navigation render them as
inline SVG inside the same aria-hidden span as before, so a template that
hides the arrow with "& span{display:none}" keeps working. Until now the
arrow was a text glyph that looked different in every font. Umut: "bei
alle Projekte bzw bei allen möchte ich dieses emoji icon weg haben".
- Own CSS with the same nested selector twice kept only the last rule.
"& .post-card{border:0}" followed by "& .post-card{transform:none}" lost
the border - silently, which brought the core's card frame back on the
project grid. The parser now joins the declarations in source order, as
CSS does; the same for "&{…}" next to the element's own properties.
- The eager image over the fold was not preloaded. The background image of
the first section has had a preload link for a while; a plain <img> was
found only after the stylesheet was parsed, and that round trip went
straight into LCP. PageController::heroImage() now writes the same src,
srcset and sizes the block template writes, and page.html.twig emits the
link - unless a background image is already preloaded.
- Measured on the way: an entrance effect on the LCP element costs about
900 ms of render delay. The EB Solar hero image therefore has none.
Tests: 581 green, the icon test accepts quoted keys, two cases for the CSS
cascade, the preload asserted in the big-picture test.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Umut, after 1.21.3: "fix hat nichts gebracht" - and the fix could not have,
for two reasons. kant-it.de still runs 1.20.1, so nothing since then was ever
live there. And the diagnosis behind 1.21.3 rested on a test that proved
nothing: Safari's folded address bar shows only the domain, never the
fragment, so "nothing in the address bar" said nothing about whether the tap
had arrived.
Reproduced this time in the real engine: Playwright's WebKit with the iPhone 14
profile. The tap arrives, #seitenanfang lands in the URL, and scrollY stays at
1500. Setting location.hash directly does the same. In the desktop profile as
well, so this is Safari on the Mac too. Chromium jumps to 0.
Narrowed down with fourteen variants of the target: WebKit does not scroll to a
fragment target whose box is inline and has no area. The same empty span with
display:block jumps; an empty div with no height jumps; #top, #main and body
jump. It is the inline box, not the size. The section anchors are not affected
- position:absolute already blockifies them and they jump in WebKit.
So the target gets display:block, one rule in site.css. The template stays as
it is.
And the lift from 1.21.3 comes out again: --kant-browser-chrome and the six
places that added it to bottom or padding. It moved the button and the consent
bar up by the folded toolbar's height on every iPhone, for a cause that turned
out not to be the cause. On the desktop it was zero anyway.
WebKit iPhone 14 after the change: tap 0, click 0, hash 0. Chromium unchanged.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Umut: "Beim Smarphone/iphone geht der button scroll to top nicht. hier gehts."
The button was there and did nothing, on every page of every installation, and
tapping it did not even put #seitenanfang in the address bar - so the tap never
reached the page.
Safari on the iPhone folds its address bar into a narrow strip while reading
and lays that over the content. A fixed element measures its "bottom" against
the full viewport, not against the part that is left: the button stood in the
strip the browser had taken for itself. Drawn, visible, and every tap landed on
the bar. Measured at 390x844 the button occupies y 780 to 828 - the bottom 64
pixels, which is where that strip sits.
The difference between the large viewport (bar folded) and the small one (bar
out) is exactly that height, so --kant-browser-chrome carries it and the pieces
that stick to the bottom add it. Where there is no such bar it is zero and
nothing moves. The @supports around it is not caution but necessity: a browser
that does not know lvh/svh would find the calculation invalid at computed-value
time, and bottom would fall back to auto - the button would then stand
anywhere.
The consent bar has the same geometry, so "Annehmen" and "Ablehnen" were just
as hard to hit. It is lifted the same way, except where it is glued to the edge
- raising it there would leave a gap with the page showing through, so its
bottom padding grows instead and the buttons move up with it.
Verified with a test page that sets the height to 50px, the only way to see the
effect without an iPhone: the button moves up by exactly 50px, the glued bar
keeps its edge and its padding goes from 16px to 66px. On the real page in
Chromium bottom stays 16px and the jump still goes 1500 to 0.
Not verified on a real iPhone - that needs the device.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Umut on Elektro Bernhard: "Menü im Mobil Dropdown textfarben sind weiß also
weiß auf weiß und Hero banner texte sind ohne abstand rechts links im mobil".
Both are core faults, not content - every site with a floating header and a
flex section had them.
The opened burger panel paints its own white surface but took its text colour
from the header. A header that hovers over a dark photo writes white, so the
menu was white on white until the page was scrolled and the header turned
light. The design area has settings for the burger button - text, surface,
border, corners, size - but none for the panel, so there was no way to fix
this from the editor. The panel now sets its own colour alongside its own
background.
The gutter was lost to "flex:1 1 auto" on .section-inner, added in 67b8575 so
a full height section could grow. Growing works along the main axis, and that
defaults to the row: the inner box grew sideways and pushed the container
gutter out. On the desktop the theme's maximum width stopped it before anyone
noticed; on a phone there is none, and the text sat hard against the edge
while the header and every other section kept their 18px. It is "flex:0 1
auto" now. The height never needed it - across the main axis a flex item
stretches anyway, and where "align-items: center" is set, the middle is what
was asked for.
Measured at 375px: hero content was x 0 / 375 wide, now x 18 / 339, the same
as the header. At 1440px nothing changes (x 93 / 1240). Checked against the
full screen hero template as well - edge to edge, 100dvh, content centred -
which renders as before.
579 PHP tests / 4798 assertions on 8.4 and 8.2, 35 JavaScript model tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Umut: "Kannst du bitte fuer die Erstseite bzw. nach der Installation eine
schoene Erstseite erstellen. Under Construction oder so ohne Details mit melde
dich an usw. Am besten auch noch Logo von KantSites."
starterPage() shipped two sections of instructions for the owner: "Melde dich
an, oeffne sie im Editor ..." and "Die naechsten Schritte" with design, menus
and settings. That stood on the only page a fresh installation serves
publicly, so the visitor read it, not the owner - who gets the same steps on
the last installer screen and in the admin area anyway.
It is now the page such a site actually needs: the KantSites mark, the label
"In Arbeit" in the accent colour, the site name as the single H1, "Hier
entsteht eine neue Webpraesenz.", a short rule and the line "Erstellt mit
KantSites". The section is a flex container with min-height 70vh and a
gradient from @paper to @mint, so the little that is there sits in the middle
of the screen; the colours are palette references and follow a design change.
Everything is an ordinary block with ordinary style values and can be changed
or removed in the editor. The page description is a real sentence now instead
of "NOCH AUSZUFUELLEN", so a freshly installed site does not carry a
placeholder into search results.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>