All notable changes to CMS Lite are documented here.
The format follows Keep a Changelog, and this project uses Semantic Versioning.
[0.3.0] - 2026-09-18
The Blog Posts, Pages, Publish Jobs and Site Settings admin screens now show their normal empty/onboarding state on a load hiccup instead of a hard "could not be loaded" error.
Fixed
- Alerts, table rows, icon-only buttons and password fields now pick up the corrected shared styling. The corrections were released earlier in the shared look-and-feel package, but this app was still pinned to an older release, so none of them were reaching the screen.
Changed
- The release notes page now uses the shared platform component, so it renders formatted releases instead of raw text and follows the selected language.
Deployments to staging work again. Since 11 August they had been failing at the database-update step, which meant no change reached the staging site at all.
Changed
- The admin area now uses the shared platform navigation shell instead of its own private sidebar.
The sign-in page's "Continue with app-platform" button now actually starts sign-in instead of doing nothing when clicked.
Fixed
- Buttons and links near the top of a page can be clicked again. The bar across the top of the application floated above the page instead of sitting above it, so it covered roughly the first 85 pixels of every page and swallowed any click that landed there. Page headings in that strip were also partly hidden behind the bar.
Changed
- The application now uses a newer release of the shared application frame. Alongside the fix above it carries the shared-frame work of the past weeks, so some surfaces may look slightly different.
A rejected captcha on the contact form now says so. It used to answer with "please correct the highlighted fields" while every field was valid, which left the visitor looking for a mistake they had not made and gave them no way to find out that the captcha was the problem.
A contact form rejection that arrives without a response body no longer takes the page down with it.
Pressing Send on the contact form can no longer take the page down. If the API cannot be reached, times out, or the browser cannot produce a captcha token, the form now says so and stays usable instead of ending the session and losing what was typed.
This matters most during a deployment, when the API is briefly unreachable and a visitor pressing Send would previously have lost their message.
Cloudflare is no longer contacted on pages that have no captcha. The Turnstile browser API used to load in the root document, so every page -- the home page, the blog, the legal pages and the whole admin area -- opened a connection to challenges.cloudflare.com and transmitted the visitor's IP address and User-Agent to it, for a widget only the contact form can use. The interop now requests that API itself, when a widget is actually being rendered.
The contact form also releases its Turnstile widget again when the page is left. It never did, so each return visit left one more live widget registered inside Cloudflare's own registry for the lifetime of the browser tab.
Fixed
- Signing in with the platform account now completes reliably instead of failing on every attempt.
Pressing Subscribe on either newsletter form can no longer take the page down. If the API cannot be reached or times out, the form now says so and stays usable instead of ending the session and losing the address that was typed.
The newsletter confirmation page gets the same guard and an honest message. It previously reported every failure it did not recognise as "This confirmation link is invalid" — including the API simply being unreachable, which would have sent a visitor to delete the mail carrying their only working link. It now says the confirmation could not be completed right now and that the link is still good.
This matters most during a deployment, when the API is briefly unreachable.
Two fixes that arrive together, both from picking up a corrected version of the shared appearance package that this application had been left behind on.
The colours shown for the fraction of a second before a page finishes loading now match the colours the page actually settles on. They were drawn from a set of names the application had stopped using, so the very first paint could look wrong before correcting itself.
The button that switches between the light and dark appearance now describes itself properly to screen readers and to anyone hovering over it. It had been announcing an internal placeholder instead of a sentence, so people relying on a screen reader were told the button's internal name rather than what it does.
Outside a development machine the authoring service now refuses to start unless it has been told both which sign-in service to trust and which application the sign-ins it accepts were issued for. Previously only the live environment insisted on the first of those, and a missing second value quietly turned off the check that a credential was meant for this application at all — so a credential issued for a neighbouring application would have been accepted.
Nothing changes for people using the site or the authoring area, and no running environment is affected: every deployed environment already supplies both values. The startup message names the exact setting that is missing.
The contact form works again. Sending a message previously did nothing useful: the captcha never appeared, and pressing "Send message" produced an error rather than a sent message.
Two scripts the page needs were never loaded — Cloudflare's captcha service and the small piece of code that places the captcha on the page. Without them the form's first render failed, which cut the page's live connection to the server and left the submit button falling back to a plain form post the server rejected.
The captcha now appears and the form sends.
Release notes now keep a wrapped list item together, so its full text remains readable instead of part of it appearing outside the list.
Internal consistency fix with no change to how the service behaves today. The automatic check that watches each running component now asks the standard "can this serve requests right now?" question rather than a broader one that happened to give the same answer. The benefit is future-proofing: as more internal checks are added, only the ones that genuinely affect serving traffic will be able to mark a component as troubled.
Nothing changes for visitors, and the checks that guard a release before it goes live behave exactly as before.
Access to the site is now closed unless a page is deliberately opened. Every part of the site that is not explicitly marked as public asks for a sign-in, so a screen or address added later that nobody remembered to protect is refused rather than quietly reachable by anyone.
Nothing changes for visitors. The home page, portfolio, blog, contact, imprint, privacy and changelog pages, the sitemap and the crawler instructions all remain open to everyone, an address that does not exist still shows the translated "page not found" screen, and the styling, scripts and live page updates all continue to load without an account. The authoring area still asks people to sign in and still requires the authoring role.
Ticked checkboxes and selected radio buttons stay clearly visible when you point at them. Previously the coloured fill of a ticked checkbox faded to a barely-there tint as soon as the pointer reached it, leaving its white tick almost invisible; a selected radio button faded the same way, and because the filled dot is the only thing marking a radio button as chosen, it briefly looked as though the selection had been lost.
In the dark appearance the accent colour used for selected and focused controls is also a touch brighter. It had been very slightly too dark to stand out against the raised panels that cards, tiles, and dialogs are drawn on, so a tick box, a selected radio button, a switch, or a slider inside one of those panels was harder to pick out than it should have been. The keyboard focus outline benefits most: it is the only thing showing which control you have moved to with the keyboard, and inside a panel it had been right at the edge of being too faint to see.
Button labels remain just as legible, the light appearance is unchanged, and selected rows in tables, highlighted text, and slider and progress tracks keep the soft tint they have always had.
Form fields, dropdowns, checkboxes, radio buttons, and outlined buttons now have a clearly visible outline in both light and dark themes, meeting the accessibility contrast requirement for control boundaries. Previously these outlines were too faint to separate a control from the surface behind it, and because the dark appearance is what most people see by default, the weaker of the two was the common case. Pointing at one of these controls in the dark appearance now brightens its outline instead of making it vanish, so it stays clear which one is under the pointer. Dividers, table rules, and panel outlines are deliberately unchanged and stay understated.
These styling updates also now reach people who have visited before. Previously the browser could keep using its saved copy of the shared styling after an update shipped, so a returning visitor might have continued seeing the old appearance for a while. Each update is now published under its own address, so browsers pick it up immediately while still caching aggressively between updates.
Internal consistency fix with no change to how the site behaves today. The pieces that watch the site — the traffic router deciding where to send visitors, and the release gate that waits for a new version to come up — now all ask the same standard "can this serve requests right now?" question. They previously asked a broader one that happens to give an identical answer.
The benefit is future-proofing: as more internal checks are added, only the ones that genuinely affect serving traffic will be able to take a component out of rotation or hold up a release. The release smoke test also now checks all of the standard status addresses instead of just some of them, so one of them going missing gets noticed.
Nothing changes for visitors.
The address monitoring uses to ask "is this site still running?" now answers. Both the website and its content service reply to the standard liveness address with a real verdict; previously that address was not recognised at all, so anything watching it would have concluded the site had stopped when it was running perfectly well.
The liveness answer deliberately ignores the database, so a database hiccup can no longer be mistaken for a stopped site and trigger an unnecessary restart. Nothing changes for visitors, and the addresses already in use keep working exactly as before.
The address monitoring uses to ask "is this site ready to serve visitors?" now answers. Both the website and its content service reply to the standard readiness address with a real verdict; previously that address was not recognised at all, so a perfectly healthy site answered with "page not found" and anything watching it would have reported an outage that was not happening.
Nothing changes for visitors, and the readiness addresses already in use keep working exactly as before.
Setting up an environment no longer creates a separate administrator login for the site. People have signed in through the shared platform account for some time, so that separate login could not actually be used to reach anything — it only widened the set of sign-in details worth stealing. Setting up an environment now produces one fewer value, and the example environment files and the provisioning runbook no longer ask for it.
If you set up an environment before this change, the leftover value can be discarded; nothing uses it any more.
The internal design review area, which is meant for pre-release environments only, is now pinned closed on the live site so it cannot be opened there by accident.
Three improvements that arrive together, from picking up corrected versions of the shared appearance and layout packages this application had been left behind on.
The warning and information colours shown for the fraction of a second before a page finishes loading are darker, so the text on them is legible. They had been too light against the page background to meet the accessibility contrast standard.
Error pages now carry the site's colours. A page that reports "not found" is rendered by a second pass over the request, and that pass could finish before the appearance settings arrived, leaving the page unstyled.
Switching to another application from the app menu now lands on the page that application nominates rather than its bare address. Where an application asks to be entered through its sign-in page, that is where you arrive: if you already have a session, you arrive signed in rather than as a stranger, and if you do not, you are asked to sign in at the door instead of after wandering in.
Being signed in now means the authoring workspace actually works. Until now a sign-in lasted a fixed hour while the credential behind it lasted only as long as the platform said it should — fifteen minutes on staging. In the gap the workspace still looked and behaved as though everything was fine: the account menu, the editors and the media library all rendered normally, and only saving, publishing or loading content revealed that nothing was getting through. Work typed into an editor during that window had nowhere to go.
A session now ends when the credential it rests on ends, instead of outliving it. The length is taken from the credential itself rather than assumed, so it stays right in every environment even where the platform is configured differently. When a session does end, returning to work is normally just a redirect: the platform remembers who you are and signs you straight back in without asking you to sign in again. Only when that memory has also lapsed does the sign-in page appear.
An editor left open in a browser tab is covered too. Such a page keeps its own live connection and would previously have carried on presenting itself as signed in long after the credential lapsed; it now notices within moments and sends you to sign in rather than letting you keep working into failed saves.
Being signed in now survives a deployment. Until now the secret that seals a sign-in cookie was made fresh in memory each time the workspace started and was thrown away when it stopped, so every release — and every ordinary restart — invalidated everyone's cookie at once. Anyone who happened to be editing was returned to the sign-in page mid-sentence, with no indication that anything had been deployed and nothing to distinguish it from the session simply expiring.
That secret is now kept outside the workspace, in storage belonging to this application alone, and it is encrypted where it sits. Restarting or redeploying no longer disturbs a signed-in session, and two copies of the workspace running side by side now recognise each other's cookies instead of each holding a private set.
The secret is also now maintained centrally: it can be rotated or retired for the whole estate in one operation, and the workspace reports back at start-up whether its own copy is present and properly encrypted, so a failed rotation is caught rather than discovered by users who can no longer sign in.
Where no such storage is configured at all — a developer's own machine — the workspace still starts and still keeps sign-ins for as long as it is running, rather than refusing to start over storage that environment was never meant to have.
The loading placeholders shown while a page or list is still fetching — the animated skeleton bars on the blog editor, page editor, project editor, media library, publish history, tag and topic managers, site settings, and the admin dashboard — now size themselves through the stylesheet's token-driven classes instead of per-element inline sizing. The visual result is identical, but the sizing is now maintainable in one place alongside the rest of the page's styling.
The spacing below the inline tag and topic editing forms, and the spacing on the blog editor's tag chip section, are likewise now set through a class instead of an inline value, so they sit alongside the shared spacing tokens.
Fixed
- Update shared theme packages so dark error text remains readable and outlined delete actions retain their distinct appearance beside filled confirmations.
[0.2.3] - 2026-07-22
Fixed
- An upload that the media library cannot process now explains why in the upload dialog, in English and German, instead of showing a generic failure notice.
[0.2.2] - 2026-07-22
Fixed
- Media uploads that cannot be processed within the allowed time are now rejected with a clear validation error instead of an unexpected server error.
[0.2.1] - 2026-07-07
Added
- Added public changelog pages and linked the footer version label.
- Added the authoring workspace for managing site settings, pages, projects, blog content, media, navigation, publishing, and content snapshots.
- Added a media library with upload, preview, picker, and protected delete behavior.
- Added publishing controls for staging, production, scheduled publishing, job history, progress, retry, and cancel actions.
- Added product version metadata for deployed services.
Changed
- Aligned the design gallery and authoring workspace around the same author role.
- Improved shared app shell styling and app-switcher responsiveness.
Security
- Strengthened authoring authentication so admin changes require a validated signed-in author.
- Ensured public pages remain public while authoring features require authorization.
- Reduced the risk of unauthenticated same-host calls being treated as trusted.
[0.2.0] - 2026-06-25
Added
- Established 0.2.0 as the first CMS Lite SemVer baseline for deployed services.