<?xml version="1.0" encoding="utf-8"?><rss version="2.0">
  <channel>
    <title>Liip Blog</title>
    <link>https://www.liip.ch/de/blog</link>
    <lastBuildDate>Tue, 21 Apr 2026 10:21:15 +0200</lastBuildDate>
            <item>
      <title>Wer braucht noch eine Lehre, wenn es KI gibt?</title>
      <link>https://www.liip.ch/de/blog/wer-braucht-noch-eine-lehre-wenn-es-ki-gibt</link>
      <guid>https://www.liip.ch/de/blog/wer-braucht-noch-eine-lehre-wenn-es-ki-gibt</guid>
      <pubDate>Tue, 21 Apr 2026 00:00:00 +0200</pubDate>
      <description><![CDATA[<h3>KI gehört für mich bereits zum Alltag</h3>
<p>Ende 2022 habe ich zum ersten Mal ChatGPT in einem Video auf TikTok gesehen und habe es direkt ausprobiert. Ich konnte mir Inhalte schneller erklären lassen als meine Lehrpersonen, Aufgaben besser lösen, als wenn ich es selbst machen würde, und sogar Ideen entwickeln, auf die ich nie gekommen wäre. Das faszinierte mich sehr. </p>
<p>Aber wie mächtig und auch wichtig KI wirklich ist, wurde mir erst anfangs meiner Lehre klar. Heute nutze ich sie täglich. Nicht, damit sie meine Arbeit macht, sondern damit ich mir selbst Neues beibringen kann, gezielter recherchieren kann und bei bestimmten Aufgaben effizienter vorankomme.</p>
<h3>Meine Lehre ist vielseitig. Genau das macht sie spannend</h3>
<p>Ich mache eine Lehre als <a href="https://www.ict-berufsbildung.ch/grundbildung/ict-lehren/entwickler-in-digitales-business-efz">Entwickler Digitales Business EFZ</a>. Ich bin momentan im zweiten Lehrjahr. Nach dem ersten Jahr im Bbc Basislehrjahr arbeite ich seit Sommer 2025 bei Liip. </p>
<p>An dieser Lehre finde ich besonders spannend, dass ich viele verschiedene Aufgaben habe. Ich arbeite in verschiedenen Circles, selbstorganisierte Teams nach Holokratie, mit. So lerne ich viele unterschiedliche Perspektiven, Aufgaben und Personen kennen.</p>
<p>Ich bin im Finance Circle gestartet, da ging es stärker um Datenmanagement. Seither hatte ich die Möglichkeit, in verschiedene weitere Teams reinzuschauen und jeweils andere Aufgaben, Perspektiven und Arbeitsweisen kennenzulernen. Heute arbeite ich im Content- und Design Circle mit Fokus auf Prozessoptimierung, Automatisierung und KI. Ich lerne auch noch einige andere Circles, Aufgaben und Rollen im Laufe meiner Lehre kennen. </p>
<figure><img alt="" src="https://liip.rokka.io/www_inarticle_5/ccafe5/grafik-1.jpg" srcset="https://liip.rokka.io/www_inarticle_5/o-dpr-2/ccafe5/grafik-1.jpg 2x"></figure>
<p>Diese Vielfalt passt gut zu mir. Ich arbeite gerne strukturiert, kommuniziere aktiv und finde es spannend, Abläufe zu analysieren und zu verbessern. Bei Liip habe ich die privilegierte Chance bereits früh in der Lehre Verantwortung zu übernehmen. Ich arbeite an wirkungsvollen internen und externen Projekten mit, treffe Entscheidungen und bringe eigene Ideen ein. Das unterscheidet Liip für mich stark von vielen anderen Ausbildungsorten.</p>
<h3>Der Umgang mit Kund*innen gehört dazu</h3>
<p>Ein Teil meiner Lehre, der von aussen vielleicht weniger sichtbar ist, ist der Umgang mit Kundinnen. Ich lerne, professionell zu kommunizieren, die eigene Firma gut zu vertreten und wichtige Faktoren für eine gute Zusammenarbeit kennen. Das bedeutet zum Beispiel, Informationen im Team klar zu vermitteln, Anforderungen abzuholen und in Projekten strukturiert zu kommunizieren. Weil ich als Lernender früh bei echten Projekten mitwirke, entwickle ich ein gutes Gespür dafür, was Kundinnen wirklich wichtig ist.</p>
<h3>Wie ich KI konkret nutze</h3>
<p>KI wird besonders hilfreich, wenn ich viel Kontext gebe. Dann wird aus einem allgemeinen Tool ein wertvoller Sparringspartner. KI unterstützt mich vor allem, wenn ich ein klares Ziel habe und schnell einen guten ersten Entwurf brauche. Ich nutze sie zum Beispiel für Brainstormings, um neue Begriffe zu verstehen, für Recherchen oder um Texte zu strukturieren.</p>
<ul>
<li><em>Fun Fact am Rande: Auch dieser Blogpost wurde teilweise mit KI strukturiert. Ich habe meine Gedanken gesammelt, den Kontext erklärt und mir dann helfen lassen, eine sinnvolle Struktur zu erstellen. So nutze ich KI im Alltag.</em></li>
</ul>
<p>Ein gutes Beispiel ist das Manual für das <a href="https://www.liip.ch/en/work/projects/liipgpt">LiipGPT</a> Backend, das ich geschrieben habe. Am Anfang hatte ich praktisch keine Erfahrung mit dem Schreiben von Manuals. Deshalb habe ich mir zuerst mithilfe von KI die Basics beigebracht. Danach habe ich der KI so viel Kontext wie möglich mitgegeben und Schritt für Schritt mit ihr einen Entwurf entwickelt. Am Ende entstand ein Handbuch, das intern oft genutzt  und für Kund*innen individuell ausgebaut wird. Für mich war das ein Moment, in dem ich merkte, wie viel Mehrwert KI bringen kann, weil ich nicht nur schneller und ohne Erfahrung arbeiten konnte, sondern auch ein Ergebnis entstanden ist, das anderen im Alltag wirklich hilft.</p>
<figure><img alt="" src="https://liip.rokka.io/www_inarticle_5/830971/grafik-2-de.jpg" srcset="https://liip.rokka.io/www_inarticle_5/o-dpr-2/830971/grafik-2-de.jpg 2x"></figure>
<aside>
<blockquote>
<h3>Meine Top 3 KI Tools für den Alltag im Digital Business</h3>
</blockquote>
<p>Ein kleiner Einblick in die Tools, die mir im Alltag am meisten helfen:</p>
<ul>
<li>
<p><strong>Claude</strong></p>
<p>Mein wichtigstes Tool für Brainstorming, Schreiben und Verstehen von neuen Themen.</p>
</li>
<li>
<p><strong>Cursor</strong></p>
<p>Hilft mir beim Coden, auch ohne tiefes Programmierwissen. Für ein Rendering Tool in Figma habe ich zuerst im Plan Mode den Kontext erklärt und dann Schritt für Schritt umgesetzt. Cursor hat mir dabei geholfen, die Idee wirklich zum Leben zu erwecken.</p>
</li>
<li>
<p><strong>NotebookLM</strong></p>
<p>Ich lade Dokumente oder Notizen hoch und bekomme daraus Zusammenfassungen, Fragen oder kompaktes Lernmaterial. Besonders nützlich für Prüfungen oder beim Einarbeiten in neue Themen.</p>
<blockquote>
</aside>
</blockquote>
</li>
</ul>
<p>Ich nutze diese Tools nicht unüberlegt. Ich teste sie, vergleiche sie und überlege genau, für welchen Anwendungsfall sie wirklich sinnvoll sind.</p>
<h3>KI ist mein bester Lernbuddy</h3>
<p>KI hat grosses Potenzial beim Lernen. Sie vereinfacht Inhalte, liefert Beispiele und passt sich an mein Niveau an. Das schafft kein Lehrbuch. Der Einstieg ins Thema gelingt so schneller. Als Lernender stelle ich der KI Fragen und Rückfragen, die ich mir im Unterricht nicht zu stellen trauen würde. </p>
<p>Gleichzeitig gilt jedoch: <strong>KI darf das eigene Denken nie ersetzen. Wer nicht versteht, wie KI funktioniert und wie sie genutzt werden kann, lernt am Ende wenig.</strong></p>
<p>Für mich gehören Offenheit und kritisches Hinterfragen zum Umgang mit neuen Tools dazu. KI kann Lernende stärken, aber nur, wenn sie bewusst als Werkzeug genutzt wird. Darum ist es wichtig, dass sich Lernende schon in der Ausbildung mit KI beschäftigen. Nicht erst später, wenn alle anderen längst routinierte Nutzer*innen sind.</p>
<h3>Wie nutzt du KI?</h3>
<p>Vielleicht lohnt es sich, nach diesem Text kurz innezuhalten und über den eigenen Umgang mit KI nachzudenken. Wie nutzt du KI heute wirklich in deinem Alltag? Welchen konkreten Mehrwert bringt sie dir dabei? Ist sie für dich nur ein Werkzeug, das du ab und zu ausprobierst? Oder setzt du sie bewusst ein, um besser, schneller oder strukturierter zu arbeiten?</p>
<p>Bei Liip erlebe ich direkt, wie eine Organisation KI nicht nur nutzt, sondern aktiv mitgestaltet:</p>
<ul>
<li>Regelmässige Wissensaustausche und Liip Talks zu KI, von neuen Modellen bis zu ethischen Fragen.</li>
<li>KI Trainings für alle Mitarbeitenden. Intern bieten wir Workshops und spezielle Module an, damit alle sicher im Umgang mit KI bleiben.</li>
<li>Entwicklung und Weiterentwicklung eigener KI Tools wie <a href="https://www.liip.ch/en/work/projects/liipgpt">LiipGPT</a>, das bereits bei Kund*innen aus dem Gesundheits-, Rechts- und öffentlichen Bereich im Einsatz ist.</li>
<li><a href="https://www.liip.ch/de/blog/ai-fuer-menschen-und-den-planeten-gestalten">AI Sustainability Guidelines</a>, ein interner Rahmen, der sicherstellt, dass KI Projekte ethisch und nachhaltig umgesetzt werden.</li>
</ul>
<p>Das zeigt mir, wie eine Organisation aussehen kann, die KI nicht blind einsetzt, sondern Verantwortung dafür übernimmt.</p>
<p>Wie nutzen Mitarbeitende in deinem Unternehmen KI? Gibt es klare Regeln oder Angebote, um KI sinnvoll einzusetzen? Wo könnte KI dich in deiner Rolle konkret unterstützen, wenn du sie gezielter anwendest?</p>
<p>KI ist kein festes Konzept, das für alle gleich funktioniert. Aber wer sie früh nutzt, merkt schnell: <strong>Die Frage ist nicht, ob man noch eine Lehre braucht, sondern wie viel mehr man aus ihr herausholt, wenn man KI hat.</strong></p>]]></description>
    </item>
        <item>
      <title>BOSW 26: der R&#252;ckblick</title>
      <link>https://www.liip.ch/de/blog/bosw-26-der-rueckblick</link>
      <guid>https://www.liip.ch/de/blog/bosw-26-der-rueckblick</guid>
      <pubDate>Mon, 20 Apr 2026 00:00:00 +0200</pubDate>
      <description><![CDATA[<p>Wie jedes Jahr versammelte The Hall in Dübendorf ein Who’s Who der Schweizer Webbranche, um die besten Projekte des vergangenen Jahres in verschiedenen Kategorien auszuzeichnen. Auch wir haben wieder Projekte eingereicht,  dieses Jahr drei, und alle haben es auf die Shortlist geschafft: die digitalen Plattformen für das Museum für Gestaltung Zürich, das Migros Karriereportal und die neue Website des Kantons Solothurn. Die Preisverleihung ermöglichte den Projektteams, nochmals zusammenzukommen. Vor allem aber bot sich die Gelegenheit, die Spannung mitzuerleben, welche Projekte mit Gold-, Silber- oder Bronze-Auszeichnungen prämiert würden.</p>
<h2>Museum für Gestaltung: ein Meisterwerk in den Bereichen Public Value und User Experience</h2>
<p>Die neue Website des Museums für Gestaltung Zürich dient als zentrale Drehscheibe für die digitale Kommunikation des Museums und ermöglicht Besucher*innen, mit dem Museum in Verbindung zu treten. In Co-Creation haben wir eine Lösung entwickelt, die es dem Design ermöglicht, für sich selbst zu sprechen und damit einen grösseren Nutzen für die Gesellschaft zu erzielen. Die BOSW-Jury hat dies anerkannt und die neue Website mit Silber in den Kategorien “User Experience” und “Public Value” ausgezeichnet. </p>
<p>Mehr zum <a href="https://www.liip.ch/en/work/projects/museum-fur-gestaltung-zurich">ausgezeichneten Projekt</a> </p>
<p>Wie Clelia Kanai, Head of Marketing &amp; Communication, betont:<br />
«Gestaltung bedeutet für uns Klarheit und Verantwortung, auch im Digitalen. Mit Liip hatten wir eine Partnerin, die diesen Anspruch geteilt und mitgetragen hat, von der ersten Konzeptidee bis zum letzten Accessibility-Check. Dass daraus ein Projekt entstanden ist, das die BOSW-Jury mit Silber in den Kategorien User Experience und Public Value auszeichnet, ist für uns Bestätigung und schöne Anerkennung zugleich.»</p>
<figure><img alt="" src="https://liip.rokka.io/www_inarticle_5/e48782/bosw2026teampic.jpg" srcset="https://liip.rokka.io/www_inarticle_5/o-dpr-2/e48782/bosw2026teampic.jpg 2x"></figure>
<h2>so.ch: Transparenz, Accessibility und Benutzerfreundlichkeit stärken das Vertrauen der Öffentlichkeit</h2>
<p>Wir haben für den Kanton Solothurn eine neue Website entwickelt: so.ch. Sie schafft echten Public Value, indem sie kantonale Dienstleistungen transparent, barrierefrei und verständlich macht und so das Vertrauen der Öffentlichkeit, der Behörden und der Wirtschaft in öffentliche Dienstleistungen stärkt.</p>
<p>Wie uns bs.ch bereits letztes Jahr gezeigt hat, ist eine nutzer<em>innenzentrierte Website entscheidend, um den Alltag der Bürger/</em>innen zu verbessern, die immer häufiger ihre Informationen und Dokumente online finden müssen. Die Jury hat die Qualität der User Experience anerkannt und uns in dieser wichtigen Kategorie mit Bronze ausgezeichnet.</p>
<p>Mehr zum <a href="https://www.liip.ch/en/work/projects/so-ch-user-experience">ausgezeichneten Projekt</a></p>
<p>Auch wenn wir nicht ganz so viele Preise gewonnen haben, wie wir uns gewünscht hätten (und natürlich auch verdient hätten :), war es spannend, den Puls der Branche zu spüren. Wir gratulieren «Swissgrid 24/7» herzlich zum Titel «Master of Swiss Web 26». Bis nächstes Jahr!</p>]]></description>
    </item>
        <item>
      <title>Zurich Climate Week</title>
      <link>https://www.liip.ch/de/blog/zurich-climate-week</link>
      <guid>https://www.liip.ch/de/blog/zurich-climate-week</guid>
      <pubDate>Thu, 16 Apr 2026 00:00:00 +0200</pubDate>
      <description><![CDATA[<p>Die erste <a href="https://www.climateweekzurich.org/" rel="noreferrer" target="_blank">Zurich Climate Week</a> ist da (4. – 9. Mai 2026) und kommt irgendwie gerade zum richtigen Zeitpunkt. In der aktuellen geopolitischen Lage hätte das Vorhaben ein Flop werden können. Das Gegenteil ist der Fall: Das Vorhaben wurde extrem gut angenommen. Echo und Teilhabe sind gross – und in einigen anderen Städten beneidet man uns … und kommt einfach nach Zürich, um mit dabei zu sein. </p>
<p>Im Kontext jüngster politischer Entwicklungen wurde hier und dort in Nachhaltigkeitsfragen (immerhin) ein «Do-Talk-Gap» attestiert. Die Zurich Climate Week setzt hier an: Sich für den Klimanotstand einsetzen <strong>und</strong> darüber sprechen. </p>
<p>Und genau das braucht es jetzt. Denn nachhaltige Transformation passiert nicht im Stillen. Sie entsteht dort, wo unterschiedliche Perspektiven und Efforts zusammenkommen: Wirtschaft, Politik, Wissenschaft und Zivilgesellschaft. Wo Wissen geteilt wird. Wo Lösungen gemeinsam entwickelt und skaliert werden. </p>
<h1>Warum wir uns engagieren</h1>
<p>Bei Liip haben wir uns aus mehreren Gründen dafür entschieden, uns am Programm der Zurich Climate Week zu beteiligen: </p>
<ul>
<li>Um das Vorhaben zu stützen, indem wir das Programm mittragen.</li>
<li>Um unser Commitment für Nachhaltigkeit über die Climate Week hinaus zu stärken.</li>
<li>Und nicht zuletzt: um unsere Erfahrungen zu teilen, von anderen zu lernen und unser Netzwerk mit Gleichgesinnten zu vergrössern.</li>
</ul>
<p>Unser Beitrag zur Climate Week steht – genauso wie die beiden Themenbereiche, die wir mit unseren Events adressieren – im Zeichen: <strong>Verantwortung übernehmen</strong>. </p>
<h1>Unser Beitrag</h1>
<p>Liip ist mit zwei Formaten im Programm vertreten – beide mit einem klaren Fokus auf Austausch, Praxisnähe und konkrete Umsetzbarkeit. </p>
<h2>Hacking for Sustainable AI</h2>
<p><a href="https://climateweekzurich.glueup.com/event/hacking-for-sustainable-ai-176395/" rel="noreferrer" target="_blank">Weitere Infos &amp; Anmeldung</a> </p>
<p>Künstliche Intelligenz ist längst Teil unseres Alltags. Sie scheint für viele Probleme Lösungen zu bieten – und oft ist sie der erste Anlaufpunkt, wenn wir nicht weiterwissen. Gleichzeitig stellen ökologische und gesellschaftliche Auswirkungen weiterhin ungelöste Herausforderungen dar. </p>
<p>Wann schafft der Einsatz von KI tatsächlich einen Mehrwert? Und wie können wir sie so nutzen, dass sie zu einer nachhaltigeren Zukunft beiträgt? </p>
<p>Gemeinsam mit <a href="http://opendata.ch" rel="noreferrer" target="_blank">opendata.ch</a> bringen wir diese Fragen in einem Mini-Hackathon auf den Tisch. Entwickler:innen, Designer:innen, Nachhaltigkeitsexpert:innen, Unternehmen und weitere Interessierte sind eingeladen, gemeinsam neue Ideen, Prototypen und Perspektiven zu entwickeln. </p>
<p>👉 Ziel: Konkrete Ansätze entwickeln, wie KI sinnvoll, verantwortungsvoll und mit echtem Mehrwert eingesetzt werden kann. </p>
<h2>Sustainability Reporting for SMEs: Real Cases, Practical Steps</h2>
<p><a href="https://climateweekzurich.glueup.com/event/sustainability-reporting-for-smes-real-cases-practical-steps-176404/" rel="noreferrer" target="_blank">Mehr Infos &amp; Anmeldung</a> </p>
<p>KMU spielen eine zentrale Rolle in der nachhaltigen Transformation. Und doch stehen viele am gleichen Punkt: Wo anfangen? Was ist wirklich relevant? Und wie aufwändig ist das Ganze? </p>
<p>In dieser Session teilen 4 KMU ihre Erfahrungen mit freiwilligem Nachhaltigkeitsreporting. Das Format ist – als Roundtable – interaktiv: Teilnehmende können Fragen stellen, eigene Erfahrungen einbringen und zusammen diskutieren, wie Hürden überwunden werden können. </p>
<p>👉 Ziel: Hemmschwellen abbauen und zeigen, dass der Einstieg machbar ist. Die Community an engagierten KMU vergrössern. </p>
<h1>Vom Reden und Handeln – gemeinsam</h1>
<p>Die Zurich Climate Week steht für eine Haltung, die wir teilen: Dringlichkeit mit Optimismus verbinden. Nicht nur über Probleme sprechen, sondern konkrete Lösungen vorantreiben. Gemeinsam Verantwortung tragen – und dabei Wirkung entfalten. </p>
<p>Gerade in einem Umfeld, in dem Unsicherheiten zunehmen und Prioritäten sich verschieben, ist es umso wichtiger, dran zu bleiben. Nachhaltigkeit ist kein kurzfristiger Trend – sondern ein langfristiges Commitment. Und gleichzeitig eine Chance: für Innovation, für neue Geschäftsmodelle, für echte Differenzierung – für neue Lebensqualität. </p>
<p>Was wir dafür brauchen, sind mehr Räume für Austausch. Mehr Transparenz. Und mehr Mut, auch Unfertiges zu teilen. </p>
<p>Die Zurich Climate Week schafft genau solche Räume. Wir freuen uns, ein Teil davon zu sein.</p>]]></description>
    </item>
        <item>
      <title>Iframes are still odd</title>
      <link>https://www.liip.ch/de/blog/iframes-are-still-odd</link>
      <guid>https://www.liip.ch/de/blog/iframes-are-still-odd</guid>
      <pubDate>Mon, 23 Mar 2026 00:00:00 +0100</pubDate>
      <description><![CDATA[<h2>The Challenge</h2>
<p>The application does one - rather complicated - task, with lots of business logic. There was no way we could rewrite it to include it directly into the website code. And because the application comes with its own Javascript and CSS, we decided to use an iframe to embed the application with clean isolation.</p>
<p>The company maintaining that application provided us with a version - running as a Docker container - where they had stripped all extra elements like the navigation, so that it would visually fit within the website. There was however no way for us to customise anything within the application.</p>
<h2>iframe security</h2>
<p>The promise of an iframe is to keep a clean security boundary between embedding page and embedded content. This means that it is by design not possible to call Javascript across the boundary. </p>
<p>Because injecting iframes could potentially trick a user into submitting data to an attacker (clickjacking), as the iframe may be from a different origin than the main page. Thus, to even render the iframe, the browser checks the Content-Security-Policy (CSP) HTTP header. That header has a field frame-src to control what may be included as an iframe. With this, I allowed the domain of the application to be included in iframes.</p>
<pre><code>Content-Security-Policy: frame-src https://my-embed.com;</code></pre>
<p>But not only does the including page need to allow an iframe. The page to be embedded also needs to allow being included with the frame-ancestors attribute of the CSP header. As we run the application Docker image under our control, I was able to add that header in the proxy that runs before the Docker image:</p>
<pre><code>Content-Security-Policy: frame-ancestors https://my-website.com;</code></pre>
<p>Several things to note:</p>
<ul>
<li>If you have other CSP rules, merge them with the rules, nginx will overwrite the header and not add to it</li>
<li>Both options also support "self" to allow embedding resp. being embedded with the same webserver</li>
<li>Prior to the CSP becoming a standard, there was an unofficial header <code>X-Frame-Options</code>, which is still supported by browsers</li>
<li><code>Content-Security-Policy</code> must be an actual HTTP header, <code>&lt;meta http-equiv=”...”&gt;</code> is ignored for <code>Content-Security-Policy</code> (and also ignored for <code>X-Frame-Options</code>).</li>
</ul>
<h2>Size of the iframe element</h2>
<p>Now we get to the weird parts. To prevent multiple scrollbars, we need the iframe element to be exactly big enough for the embedded page. If it is too small, there is an additional scrollbar (or hidden content). If it is too large, there is odd whitespace.</p>
<p>The size of the element needs to be set on the iframe, owned by the parent. The dimensions of the content are however only known by the embedded application. HTML / CSS do not provide any means to let the parent page declare that it wants the iframe to have the “necessary size”. </p>
<p>We ended up with a really convoluted way, which seems the only way to achieve this: sending messages from the child page to the parent. This problem spawned dedicated javascript libraries like <a href="https://github.com/davidjbradshaw/iframe-resizer">iframe-resizer</a>. We ended up reimplementing the logic in the React application, as it was so small that a dedicated library felt like overkill. Following the tutorial at <a href="https://github.com/craigfrancis/iframe-height/">iframe-height</a> (which also has some interesting background on the discussion about iframes in the Whatwg), we came up with this code for the containing website:</p>
<pre><code class="language-js">// register an event listener for messages
window.addEventListener('message', receiveMessage, false);

// handle a message
function receiveMessage(event) {
    const origin = event.origin || event.originalEvent.origin;
    // we configure the expected domain to allow for this additional sanity check
    if (expectedDomain !== origin) {
      return;
    }
    if (!event.data.request || 'iframeResize' !== event.data.request) {
      return;
    }
    // the id is known in the js class. we need to find the element that needs to be resized
    const iframe = document.getElementById(`iframe-${id}`);
    if (iframe) {
      // pad the height a bit to avoid unnecessary tiny scrolling
      iframe.style.height = (event.data.height + 20) + 'px';
      // width could be handled the same way if necessary - in our case the width is fix
    }
}</code></pre>
<p>Now we need to make the embedded content send a message with its height. The Javascript for that is a bit verbose to allow for different browsers, but not complicated either:</p>
<pre><code class="language-js">(
    function(document, window) {
      if (undefined === parent || !document.addEventListener) {
        return;
      }
      function init() {
        let owner = null;
        const width = Math.max(document.body.scrollWidth, document.body.offsetWidth, document.documentElement.clientWidth, document.documentElement.scrollWidth, document.documentElement.offsetWidth);
        const height = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);
        if (parent.postMessage) {
          owner = parent;
        } else if (parent.contentWindow &amp;&amp; parent.contentWindow.postMessage) {
          owner = parent.contentWindow;
        } else {
          return;
        }
        owner.postMessage({
          'request' : 'iframeResize',
          'width' : width,
          'height' : height
        }, '*');
      }

      if (document.readyState !== 'loading') {
        window.setTimeout(init);
      } else {
        document.addEventListener('DOMContentLoaded', init);
      }
      // this is needed to also adjust the iframe if something (e.g. the Javascript of the application) changes the size of the iframe without an actual page reload.
      const observer = new ResizeObserver(init);
      observer.observe(document.body);
    }
  )(document, window);"</code></pre>
<h3>iframes with same origin</h3>
<p>If the iframe comes from the same origin (= domain) as the parent page, Javascript can cross the boundary. From parent to child, there is a <code>contentWindow</code> property on the <code>iframe</code> element. From child to parent, there is <code>window.parent</code>. With same origin, those elements expose all things the window usually has. For different origins, they only expose the function <code>postMessage</code> for the secure separation.</p>
<h2>Injecting content with Nginx</h2>
<p>Remember how I said we can’t modify the application? That still holds true. If we would have loaded both applications from the same domain, we could have had the parent page add a listener inside the iframe to directly update dimensions as needed. But the application contained absolute paths for its assets, so providing it from a subfolder of the same domain would have been tricky and we had to run it on a separate domain. </p>
<p>I ended up injecting the above snippet of Javascript in the Nginx proxy that sits in front of the Docker container:</p>
<pre><code>proxy_set_header Accept-Encoding ""; # make sure we get plain response for substitution to work
...
sub_filter_last_modified on;
sub_filter "&lt;/body&gt;" "&lt;script language='javascript'&gt;${script}&lt;/script&gt;&lt;/body&gt;";
sub_filter_once on;
...
proxy_pass https://my-embed.com$request_uri;</code></pre>
<p>Now the embedded iframe communicates its size to the containing page, which adjusts the iframe size accordingly.</p>
<p>(Note that Nginx does not execute these statements in order. The sub_filter instructions apply to the response, wihle proxy_set_header and proxy_pass apply to the request.)</p>
<h2>Alternatives</h2>
<p>Web Components are a more lightweight solution to combine separate sources into one website. If what you need to integrate is just an element and not a whole application, they might be a better fit. My collegue Falk wrote about <a href="https://www.liip.ch/en/blog/web-components-the-good-the-bad-and-the-ugly">Web Components</a> last week.</p>
<hr />
<h2>Bonus: Access control for the iframe content</h2>
<p>Because the application is not under our control, we need to manage access to it. We told the supplier of the application to remove access control and simply allow items to be created and edited by ID. Of course, this means that the application must never be directly exposed to the internet, but only reachable through the proxy.</p>
<p>On the embedding side, we track which user is allowed what ids, and have Nginx do a pre-flight check against the website to get the access decision:</p>
<pre><code># at the beginning of the location for the main request to the embeded application
auth_request /auth;

location /auth {
    # preflight authorization request with symfony
    fastcgi_pass phpfcgi;
    include /usr/local/openresty/nginx/conf/fastcgi_params;
    fastcgi_param SCRIPT_FILENAME /app/public/index.php;
    # forward the original request URI to allow our main application to verify access to the specific resource
    fastcgi_param REQUEST_URI /embed-check$request_uri;
    # body is not forwarded. we have to remove content length separately, otherwise PHP-FPM will wait for the body until the auth request times out.
    fastcgi_pass_request_body off;
    fastcgi_param CONTENT_LENGTH "";
    fastcgi_param CONTENT_TYPE "";
    internal;
}</code></pre>
<p>If the call at <code>/embed-check/...</code> returns a 2xx status, Nginx continues with the request, otherwise it returns the response with the status code to the client, allowing for example to redirect to the login page. In my case, i return an empty response with status 204 if the user is allowed to access the specific resource.</p>
<p>On Symfony side, I use Symfony security to make sure the user is logged in. And then parse the path to know which item in the application the request wants to access, and check if the user has access. This leaks knowledge about the URL design of the embedded application, which is not avoidable for granular access control.</p>]]></description>
    </item>
        <item>
      <title>Preventing Context Pollution for AI Agents</title>
      <link>https://www.liip.ch/de/blog/preventing-context-pollution-for-ai-agents</link>
      <guid>https://www.liip.ch/de/blog/preventing-context-pollution-for-ai-agents</guid>
      <pubDate>Wed, 18 Mar 2026 00:00:00 +0100</pubDate>
      <description><![CDATA[<p>Context pollution happens when the context window fills up with information that is irrelevant to the current task. The more an agent has to juggle, the more likely it loses track of what it was actually doing.</p>
<p>Here are practical techniques to prevent it.</p>
<h2>Session Hygiene</h2>
<p>Start a fresh session for each task. This is the simplest technique and the easiest to get right. If earlier research is needed, write it into a temporary handoff file and let a new session pick up from there.</p>
<h2>Streamline Tool Calling</h2>
<p>Every tool call adds tokens to the context. Poorly built tools add a lot of them. To keep the context lean:</p>
<ul>
<li>Choose tools and MCPs that are well built and optimize token usage</li>
<li>Make sure via prompting that the right tools are used from the start</li>
</ul>
<p>A single bloated tool response can waste more context than an entire conversation turn.</p>
<h2>Subagents</h2>
<p>Agents can spawn other agents that run in their own context. This isolates work and keeps the parent context clean. It helps most when building large features where individual parts are independent.</p>
<p>The easiest way to use subagents is to prompt something like:</p>
<pre><code>Split the current plan into tasks, use a subagent for each task.</code></pre>
<h2>Persistent Tasks</h2>
<p>I built an MCP for Claude Code called <code>deliverables-mcp</code> that lets an agent create persistent tasks per codebase. Tasks are stored in <code>.claude/deliverables.jsonl</code> and persist across sessions.</p>
<p>This allows:</p>
<ul>
<li>Starting a new session before implementing each task</li>
<li>Running subagents in parallel based on tasks dependencies</li>
<li>Restarting a failed task in a clean session</li>
</ul>
<p>The tool replaces Claude Code's internal tasks and is deliberately called "deliverables" for two reasons:</p>
<ol>
<li>To avoid confusing the agent with two tools both called "tasks"</li>
<li>Deliverables are typically larger than just a task, which is a sweet spot for AI agents. Not so small that handoff cost dominates, but small enough that context problems are rare.</li>
</ol>
<p>You can check out <code>deliverables-mcp</code> on <a href="https://github.com/FalkZ/deliverables-mcp">GitHub</a>.</p>]]></description>
    </item>
        <item>
      <title>Der ConfIAnce-Chatbot, ein Jahr sp&#228;ter</title>
      <link>https://www.liip.ch/de/blog/der-confiance-chatbot-ein-jahr-spaeter</link>
      <guid>https://www.liip.ch/de/blog/der-confiance-chatbot-ein-jahr-spaeter</guid>
      <pubDate>Tue, 17 Mar 2026 00:00:00 +0100</pubDate>
      <description><![CDATA[<p>Vor etwas weniger als einem Jahr haben wir den ConfIAnce-Chatbot vorgestellt. Im Auftrag der Genfer Universitätskliniken (HUG) haben wir diesen Chatbot entwickelt, um einen einfachen und interaktiven Zugang zu medizinischen Informationen über häufige chronische Krankheiten zu ermöglichen. Die Inhalte werden von der medizinischen Institution selbst erstellt und validiert.</p>
<p>Ein Artikel der Initiator*innen dieses Projekts, veröffentlicht in der neuesten Ausgabe der Revue Médicale Suisse, zieht nun eine erste Bilanz ein Jahr nach der öffentlichen Lancierung.</p>
<figure><img alt="" src="https://liip.rokka.io/www_inarticle_5/9661ee/rms-confiance.jpg" srcset="https://liip.rokka.io/www_inarticle_5/o-dpr-2/9661ee/rms-confiance.jpg 2x"></figure>
<h2>Ein offizielles Angebot statt fehlerhafter Antworten online</h2>
<p>Die medizinische Grundversorgung, ein zentraler Pfeiler eines funktionierenden Gesundheitssystems, steht zunehmend unter Druck, selbst in Städten. Wenn Patient*innen ihre Hausärzt*innen nicht schnell erreichen können, suchen sie oft im Internet nach Antworten. Die Informationen, die sie dort finden, sind jedoch häufig ungenau oder sogar potenziell gefährlich.</p>
<p>In diesem Kontext kann eine gut konzipierte KI-Lösung helfen, <strong>die richtige Information zur richtigen Zeit</strong> bereitzustellen.</p>
<p>Deshalb haben wir die HUG bei der Entwicklung eines RAG-basierten Chatbots (Retrieval Augmented Generation) unterstützt. ConfIAnce ist nicht der erste Chatbot für Patient*innen. Er unterscheidet sich jedoch durch seine institutionelle Wurzeln, die Nutzung lokal validierter medizinischer Inhalte sowie durch Kontrollmechanismen, die die Zuverlässigkeit der Antworten sicherstellen.</p>
<p>Um Sicherheit zu gewährleisten, integriert das System mehrere Kontrollmechanismen, darunter <strong>Matching, Groundedness, Harmfulness, automatisierte Tests und semantisches Routing.</strong></p>
<h2>Qualitätssicherung durch Kontrolle des Systems</h2>
<p>Eine zentrale Herausforderung besteht darin, die Kontrolle über das System zu behalten. Dafür braucht es geeignete Monitoring-Funktionen.</p>
<p>Automatisierte Tests prüfen sämtliche Antworten des Chatbots auf ihre faktische Übereinstimmung mit der Wissensbasis.</p>
<p>Zusätzlich sorgt ein von Administrator*innen anpassbares Routing dafür, dass Fragen gefiltert und bei Bedarf unter menschlicher Aufsicht weitergeleitet werden. Sie können den Chatbot zudem sofort offline nehmen, falls ein Fehlverhalten vermutet wird.</p>
<p>Themen, über die häufig gefragt wird, aber in den Ausgangsdokumenten noch wenig behandelt werden, werden identifiziert und weiter ausgebaut. So wird die Wissensbasis im Rahmen eines <strong>kontinuierlichen Verbesserungsprozesses erweitert</strong>.</p>
<h2>Selbst das beste Tool bringt nur etwas, wenn es genutzt wird</h2>
<p>Damit Patient*innen den Chatbot tatsächlich verwenden, haben die HUG eine öffentliche Informationskampagne gestartet. Ziel war es, das Tool bekannt zu machen und gleichzeitig realistische Erwartungen zu setzen.</p>
<p>ConfIAnce ist <strong>kein medizinisches Gerät und ersetzt keinen Arztbesuch</strong>. Vielmehr ist er ein Informationsangebot, das Fragen zu den häufigsten chronischen Erkrankungen bei Erwachsenen beantwortet.</p>
<p>Im Februar 2025 ging ConfIAnce in der Beta-Version online. Zwischen Anfang Februar und Ende Oktober 2025 haben 3’823 Nutzer*innen mit dem Chatbot interagiert. Daraus entstanden 5’969 Gespräche und <strong>11’781 Fragen</strong>, im Durchschnitt etwa zwei Fragen pro Gespräch.</p>
<p>Die direkt im Chatbot abgegebenen Rückmeldungen sind zu <strong>75 % positiv</strong>.</p>
<h2>Hohe Akzeptanz für einen etwas anderen Chatbot</h2>
<p>Chatbots im Gesundheitsbereich werden von Patient*innen meist gut angenommen, vor allem wegen ihrer ständigen Verfügbarkeit und der einfachen Nutzung. Studien zeigen jedoch auch wiederkehrende Probleme: eine uneinheitliche Qualität der Antworten sowie mangelnde Transparenz bei den verwendeten Quellen.</p>
<p>Genau hier unterscheidet sich ConfIAnce von vielen anderen medizinischen Chatbots.<br />
Der Chatbot wurde entwickelt, um <strong>die Beziehung zwischen Patient<em>innen und Ärzt\</em>innen zu unterstützen, nicht zu ersetzen</strong>. ConfIAnce entlastet Ärzt*innen in der Grundversorgung und schafft ihnen mehr Zeit für die menschliche Seite ihres Berufs.</p>
<p>Die Autor<em>innen des Artikels in der Revue Médicale Suisse betonen zudem, dass der Chatbot, der im spezifischen Kontext der HUG und ihrer Dokumente entwickelt wurde, auch in anderen institutionellen Umgebungen eingesetzt werden kann. Damit ein solches Projekt erfolgreich ist, braucht es zunächst hochwertige Daten, wie sie hier zur Verfügung standen. Darüber hinaus ermöglichen Kontrollmechanismen, automatisierte Tests und Nutzer\</em>innen-Feedback eine kontinuierliche Verbesserung – und sorgen für Sicherheit und Relevanz, die Vertrauen schaffen.</p>]]></description>
    </item>
        <item>
      <title>LiipGPT barrierefrei machen: Unsere Reise zur WCAG-AA-Konformit&#228;t</title>
      <link>https://www.liip.ch/de/blog/liipgpt-barrierefrei-gestalten</link>
      <guid>https://www.liip.ch/de/blog/liipgpt-barrierefrei-gestalten</guid>
      <pubDate>Mon, 16 Mar 2026 00:00:00 +0100</pubDate>
      <description><![CDATA[<p>Nachdem wir uns zuerst auf die Themenbasiertheit unseres Chatbots <a href="https://www.liipgpt.ch/" rel="noreferrer" target="_blank">LiipGPT</a> konzentriert hatten (zuletzt präsentiert beim <a href="https://zuericitygpt.ch/" rel="noreferrer" target="_blank">Z&uuml;riCityGPT Relaunch</a>), richteten wir unsere Aufmerksamkeit auf Barrierefreiheit mit dem Ziel, die WCAG-AA-Konformität zu erreichen. Wie bei vielen Funktionen haben wir zuerst untersucht, wie Branchenführende wie ChatGPT, Perplexity und Claude mit Barrierefreiheit umgehen. Obwohl wir überall Verbesserungspotenzial festgestellt haben, inspirierte uns das, darüber nachzudenken, wie wir es besser machen können.</p>
<p>Unsere Reise zur Barrierefreiheit folgte vier Hauptschritten: automatische Scans und Quick-Fixes, Tastaturnavigation, Optimierung für Mobile-Zoom und anschliessend für den Screen-Reader.</p>
<h2>Automatische Scans und Quick-Fixes</h2>
<p>Wir begannen mit automatisierten Accessibility-Tests mithilfe von Browser-Erweiterungen wie <a href="https://chromewebstore.google.com/detail/ibm-equal-access-accessib/lkcagbfjnkomcinoddgooolagloogehp" rel="noreferrer" target="_blank">IBM Equal Access Accessibility Checker</a> und <a href="https://www.deque.com/axe/devtools/extension" rel="noreferrer" target="_blank">axe DevTools</a>. Diese Tools halfen uns, häufige Probleme zu identifizieren: fehlende Labels, unzureichender Farbkontrast, unsauberes semantisches HTML und fehlende ARIA-Attribute. Obwohl automatisierte Scans nur etwa 40% der Accessibility-Probleme erkennen, boten sie eine solide Grundlage für unsere Arbeit.</p>
<h2>Tastaturnavigation</h2>
<p>Eine korrekte Tastaturnavigation ist grundlegend für die Barrierefreiheit. Sicherzustellen, dass die grundlegende Tab-Navigation in der gesamten App funktioniert, ist relativ einfach. Komplexere Komponenten wie <a href="https://www.w3.org/WAI/ARIA/apg/patterns/tabs/examples/tabs-automatic/" rel="noreferrer" target="_blank">Tabs</a>, <a href="https://www.w3.org/WAI/ARIA/apg/patterns/menubar/" rel="noreferrer" target="_blank">Men&uuml;s</a> und <a href="https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/" rel="noreferrer" target="_blank">Modals</a> erfordern jedoch erweiterte Tastaturinteraktionen: Pfeiltasten, Escape-Handling und Fokus-Management gemäss den offiziellen W3C-Richtlinien. Nutzer*innen, die auf Tastaturnavigation angewiesen sind, erwarten diese spezifischen Muster. Wenn davon abgewichen wird, führt das zu Verwirrung und Frustration. Anstatt diese Muster von Grund auf neu zu implementieren, nutzten wir <a href="https://bits-ui.com/" rel="noreferrer" target="_blank">Bits UI</a>, eine Headless-UI-Bibliothek, die diese Accessibility-Richtlinien korrekt umsetzt.</p>
<p>Über einzelne Komponenten hinaus implementierten wir Fokus-Loops und Fokus-Wiederherstellung auf Anwendungsebene, damit Nutzer*innen beim Wechsel zwischen verschiedenen Bereichen der Chat-Oberfläche stets orientiert bleiben.</p>
<h2>Optimierung für Mobile-Zoom</h2>
<p>Während User-Tests für <a href="https://meinplatz.ch/" rel="noreferrer" target="_blank">meinplatz.ch</a> mit Nutzer*innen mit Behinderungen beobachteten wir etwas Auffälliges: Viele navigieren auf Websites auf mobilen Geräten mit 200% oder mehr Zoom und halten ihre Geräte nur etwa 10 cm vor die Augen. Diese Erkenntnis zeigte eine kritische Lücke in den meisten Chatbot-Implementierungen auf.</p>
<p>Die meisten Chatbots verwenden Elemente mit fixer Position: ein Chat-Input am unteren Rand und häufig eine Kopfzeile am oberen Rand. Wenn Nutzer*innen stark hineinzoomen, können fixierte Elemente den gesamten Viewport einnehmen und die Oberfläche unbenutzbar machen. Leider ist es in Browsern nicht möglich, das Zoom-Level zuverlässig zu erkennen. Unsere Lösung: Wir verwenden den Intersection Observer, um zu erkennen, wenn Header oder Footer mehr Platz einnehmen als erwartet. In diesem Fall entfernen wir ihre fixe Positionierung dynamisch, um die Nutzbarkeit wiederherzustellen.</p>
<figure class="video"><video autoplay controls loop muted playsinline><source src="https://www.liip.ch/media/pages/blog/making-liipgpt-accessible/343fac4583-1769071651/chatgpt-zoom.mp4" type="video/mp4"></video><figcaption>Elemente mit fester Position verursachen Probleme in vergr&ouml;sserten Ansichtsfenstern.</figcaption></figure>
<figure class="video"><video autoplay controls loop muted playsinline><source src="https://www.liip.ch/media/pages/blog/making-liipgpt-accessible/6900992c5e-1769071651/liipgpt-zoom.mp4" type="video/mp4"></video><figcaption>L&ouml;sung: Feststehende Elemente wieder auf statische Positionierung zur&uuml;cksetzen, sobald ein Zoom erkannt wird.</figcaption></figure>
<h2>Screen-Reader-Erfahrung</h2>
<p>Barrierefreiheit für Screen-Reader entsteht nicht automatisch, sie erfordert sorgfältiges Design. Wir konzentrierten uns darauf, klaren Kontext durch eine saubere Seitenstruktur (Landmarks und Überschriften) bereitzustellen. So verstehen Nutzer*innen jederzeit, wo sie sich befinden und was gerade passiert, und erhalten Shortcuts zu den wichtigsten Bereichen der App.</p>
<h4>Kontext bereitstellen</h4>
<p>Wir implementierten eine umfassende Outline-Struktur mit Landmarks für die Hauptnavigation, die Einstellungen und die Eingabebereiche. Jede Nachricht enthält korrekte Überschriften und Labels. Zusätzlich haben wir nach dem Chat-Input (am unteren Seitenrand) einen Skip-Link eingefügt, der Nutzer*innen hilft, schnell zum Seitenanfang zurückzukehren.</p>
<h4>Herausforderungen mit Web Components</h4>
<p>Die Arbeit mit Web Components brachte eigene Herausforderungen mit sich. VoiceOver reagiert besonders empfindlich darauf, wie Bibliotheken implementiert sind. Wir arbeiteten eng mit dem Bits-UI-Team zusammen (das sehr schnell auf Bug-Reports reagierte) und implementierten beispielsweise lokale Portals für Dropdown-Menüs, um Navigationsprobleme mit VoiceOver zu vermeiden.</p>
<h4>Umgang mit Ankündigungen</h4>
<p>Eine der schwierigsten Herausforderungen war das Management von VoiceOver-Ankündigungen, wenn mehrere Ereignisse gleichzeitig auftraten. Da das Queueing von Ankündigungen nicht zuverlässig funktioniert, haben wir Ereignisse sorgfältig sequenziert und zusammengehörige Ankündigungen zusammengeführt. Wenn Nutzer*innen zum Beispiel „Alle Optionen auswählen“ in einer Liste anklicken, würden normalerweise einzelne Ankündigungen für jede Option ausgelöst und sich gegenseitig überschreiben. Stattdessen brechen wir diese einzelnen Ankündigungen ab und ersetzen sie durch eine einzige klare Meldung, die alles zusammenfasst (alle Elemente ausgewählt oder abgewählt, auf die vordefinierte Auswahl zurückgesetzt usw.).</p>
<p>Da der Chat eine SPA ohne Seiten-Reloads ist, war es zudem wichtig, alle Änderungen anzukündigen, die nur visuell sichtbar sind, zum Beispiel: Wechsel zwischen Light- und Dark-Mode, Sprachwechsel usw.</p>
<h4>Chat-Flow für Screen Reader</h4>
<p>Wir haben die Chat-Erfahrung speziell für Screen-Reader-Nutzer*innen gestaltet:</p>
<ul>
<li>Das Eingabefeld enthält sowohl einen Placeholder als auch ein aria-label mit dem Seitentitel. Dadurch wird beim Laden der Seite Kontext bereitgestellt, da das Eingabefeld automatisch fokussiert wird und Nutzer*innen den initialen Seiteninhalt überspringen.</li>
<li>Wenn eine Antwort generiert wird, kündigen wir dies klar an und geben damit dasselbe Feedback wie bei einer visuellen Ladeanzeige.</li>
<li>Sobald eine Antwort bereit ist, wird sie ohne Markdown-Formatierung vorgelesen (kein Kursiv, keine Links usw.), um einen natürlichen Lesefluss zu gewährleisten.</li>
<li>Nach dem Vorlesen einer Antwort weisen wir darauf hin, dass Nutzer*innen direkt eine weitere Frage stellen oder zu den Optionen der letzten Nachricht navigieren können, um Feedback zu geben oder Referenzen anzusehen. Wir fügen diesen interaktiven Abschnitt der letzten Nachricht dynamisch zur Dokument-Outline hinzu und schaffen damit einen schnellen Navigations-Shortcut.</li>
<li>Der Chat-Verlauf ist als Artikel mit Labels strukturiert, sodass frühere Konversationen leicht navigierbar sind.</li>
</ul>
<figure class="video"><video autoplay controls loop muted playsinline><source src="https://www.liip.ch/media/pages/blog/making-liipgpt-accessible/912fd0d7cd-1769018708/screenreader.mp4" type="video/mp4"></video><figcaption>L&ouml;sung: Navigation im Chatbot mit dem Bildschirmleseprogramm VoiceOver auf macOS.</figcaption></figure>
<h2>Selbst ausprobieren</h2>
<p>Du kannst diese Verbesserungen mit <a href="https://www.bs.ch/alva" rel="noreferrer" target="_blank">Alva</a> erleben, dem Chatbot der Verwaltung Basel-Stadt. Versuche, mit <a href="https://www.google.com/search?q=how+to+navigate+a+website+with+voiceover" rel="noreferrer" target="_blank">VoiceOver (MacOS)</a> oder <a href="https://www.google.com/search?q=how+to+navigate+a+website+with+nva+screen+reader" rel="noreferrer" target="_blank">NVA (Windows)</a> zu navigieren, nur deine Tastatur zu verwenden oder auf einem mobilen Gerät stark hineinzuzoomen.</p>
<h2>Eine fortlaufende Reise</h2>
<p>Unser nächstes Ziel ist es, automatisierte Accessibility-Tests in unsere CI-Pipeline zu integrieren. Wie bereits erwähnt, erkennen automatisierte Scans jedoch nur rund 40 % der Accessibility-Probleme. Das bedeutet, dass wir weiterhin jede neue Funktion sorgfältig planen und manuell testen müssen. Nichts ersetzt menschliches Testen, wenn es um Barrierefreiheit geht. Automatisierte Tools können fehlende Labels oder Kontrastprobleme markieren, aber sie können nicht beurteilen, ob eine Oberfläche tatsächlich für jemanden nutzbar ist, der mit Screen-Reader oder Tastatur navigiert.</p>
<p>Barrierefreiheit ist eine fortlaufende Reise, kein Ziel. Wir setzen uns dafür ein, LiipGPT für alle nutzbar zu machen, und werden unseren Ansatz kontinuierlich auf Basis von Praxisfeedback weiterentwickeln.</p>
<h2>Brauchst du Unterstützung bei deiner Accessibility?</h2>
<p>Wir bieten Accessibility-Audits an, um Probleme in deinen Anwendungen zu identifizieren und zu beheben. Wenn du die Barrierefreiheit deines Produkts verbessern möchtest, <a href="https://www.liip.ch/en/contact">kontaktiere uns</a>, wir helfen gerne weiter.</p>]]></description>
    </item>
        <item>
      <title>Einblicke in KI und Open Source f&#252;r Beh&#246;rden bei Drupal4Gov</title>
      <link>https://www.liip.ch/de/blog/einblicke-in-ki-und-open-source-fuer-behoerden-bei-drupal4gov</link>
      <guid>https://www.liip.ch/de/blog/einblicke-in-ki-und-open-source-fuer-behoerden-bei-drupal4gov</guid>
      <pubDate>Wed, 11 Mar 2026 00:00:00 +0100</pubDate>
      <description><![CDATA[<p>Die Drupal4Gov-Konferenz war vollgepackt mit spannenden Vorträgen. Ich war auch dort, um unsere Arbeit am Projekt Kanton Basel-Stadt / Alva / blökkli vorzustellen. Darüber haben wir bereits geschrieben, aber es gibt bereits wieder neue Funktionen.</p>
<h2>GovNL: Von Monaten zu Minuten beim Aufbau von Websites</h2>
<p>GovNL kombiniert Open-Source-Drupal-Komponenten mit einem offenen Designsystem, um zahlreiche Websites der niederländischen Regierung zugänglich und skalierbar zu betreiben. Die Zeit für den Aufbau neuer Websites reduziert sich dadurch <strong>von etwa drei Monaten auf rund zehn Minuten</strong>. Beeindruckend, um es gelinde zu sagen. Das ist ein starkes Beispiel dafür, wie Design für Wiederverwendung in grossem Massstab funktionieren kann.</p>
<h2>Europäische Kommission: Koordination ist der Schlüssel zur Skalierung</h2>
<p>Die Europäische Kommission betreibt bereits <strong>770 Websites</strong> und investiert stark in das Drupal-Ökosystem. Besonders aufgefallen ist mir, wie stark der Fokus auf <strong>Koordination</strong> liegt. Man muss sicherstellen, dass die richtigen Inhalte über den richtigen Kanal innerhalb dieser grossen Website-Landschaft veröffentlicht werden. Open Source Program Offices (OSPOs) wurden eingerichtet, um Open-Source-Strategien sowohl auf Regierungsebene als auch innerhalb von Organisationen voranzutreiben.</p>
<figure><img alt="" src="https://liip.rokka.io/www_inarticle_5/ac3271/drupal4gov2026-josef.jpg" srcset="https://liip.rokka.io/www_inarticle_5/o-dpr-2/ac3271/drupal4gov2026-josef.jpg 2x"></figure>
<h2>Website des Kantons Basel-Stadt und Alva: ein Blueprint für lokale öffentliche Verwaltungen</h2>
<p>Kurz vor der Mittagspause war ich an der Reihe, <strong>die verschiedenen KI-Use-Cases</strong> vorzustellen, die wir für den <a href="https://www.liip.ch/de/work/projects/basel-stadt">Kanton Basel-Stadt</a> umgesetzt haben. Der Kanton setzte mit dem Relaunch von bs.ch neue Standards: mit einem nutzerorientierten Design, einem themenbasierten Zugang statt interner Organisationsstruktur und <a href="https://www.bs.ch/alva">Alva</a> als <strong>erstem KI-basierten Chatbot für einen Schweizer Kanton</strong>. Der Tech-Stack basiert auf Open-Source-Komponenten, und Liip hat im Rahmen des Relaunches stark zu Open Source beigetragen. Wir verwenden Drupal als CMS, Nuxt/Vue, den <a href="https://blokk.li/">bl&ouml;kkli-Editor</a> für das Headless-Frontend und Elasticsearch für die Suche. Die Inhalte werden von einem departementsübergreifenden Redaktionsteam erstellt, das einer klaren Content-Strategie folgt.</p>
<h2>KI zur Unterstützung der Öffentlichkeit und der Editor*innen</h2>
<p>Der Vortrag war auch eine Gelegenheit, mehr als 18 Monate nach dem Go-Live Zahlen zu teilen. Heute beantwortet Alva <strong>über 10 000 Fragen pro Monat</strong>, mit durchschnittlich <strong>1,36 Fragen pro Konversation und einem Wachstum von +44 % seit Alva 2.0</strong>. Dank API-Integrationen kann der Chatbot Fragen auf Basis von Echtzeitinformationen beantworten. Alva wird stark von internen Mitarbeitenden des Kantons sowie von der Öffentlichkeit genutzt. Der Chatbot zeigt und überprüft stets seine Quellen, was entscheidend ist, um Vertrauen aufzubauen.</p>
<p>Auf der Redaktionsseite arbeitet blökkli intensiv daran, Texte zu vereinfachen. Mit dem <strong>blökkli-Editor</strong> und integrierter KI können Editor*innen nun Lesbarkeitsanalysen durchführen, vorgeschlagene Vereinfachungen nebeneinander sehen und diese übernehmen oder anpassen. Alva und die KI-Funktionen auf bs.ch werden kontinuierlich weiterentwickelt, um Editor*innen und Bürger*innen vertrauenswürdige KI-Technologien bereitzustellen.</p>
<h2>KI-gestützte Technologien in der französischen Regierung</h2>
<p>Ein weiterer inspirierender Vortrag zeigte Use-Cases von KI auf der Plattform Services Publics+ der französischen Regierung. Mit über 140 000 geteilten Erfahrungen und mehr als einer Million Reaktionen nutzt das System KI-gestützte Technologien, um staatlichen Stellen dabei zu helfen, Feedback an Bürger*innen zu geben. Dazu gehören Speech-to-Text und Echtzeit-Zusammenfassungen als zentrale Technologien. C’est magnifique!</p>
<h2>Die EU vertraut Open Source mehr denn je</h2>
<p>Die Europäische Union hat den <strong>Website Evidence Collector</strong> eingeführt – ein Open-Source-Tool, das Websites auf Sicherheitsprobleme überprüft. Bemerkenswert ist, dass das Projekt unter der <strong>EUPL</strong> (European Union Public Licence) veröffentlicht wurde. Diese betont die Interoperabilität zwischen Ländern und Lizenzen und unterstützt mehrsprachige Zusammenarbeit. Ich frage mich, ob es in der Schweiz etwas Ähnliches gibt.</p>
<p>Mit Interoperable Europe bietet die EU ausserdem ein neues Portal mit einem hilfreichen <strong>Licensing Assistant</strong>. Damit kannst du <a href="https://interoperable-europe.ec.europa.eu/collection/eupl/solution/licensing-assistant/find-and-compare-software-licenses">Softwarelizenzen finden und vergleichen</a> sowie mit einem <a href="https://interoperable-europe.ec.europa.eu/collection/eupl/solution/licensing-assistant/compatibility-checker">Kompatibilit&auml;ts-Checker</a> prüfen, ob verschiedene Open-Source-Lizenzen kombiniert werden können und ob rechtliche Komplikationen entstehen könnten.</p>
<h2>Open Source zu nutzen reicht nicht - wir brauchen Champions</h2>
<p>Zum Abschluss betonte <strong>Tiffany Farris</strong> von der Strategieberatung <a href="https://www.palantir.net/">Palantir.net</a> (nicht zu verwechseln mit Palantir Technologies), dass <strong>die Nutzung von Open Source gut ist,  aber nicht ausreicht</strong>. Organisationen brauchen <strong>Champions</strong>, die Beiträge zur Community und die Gesundheit des Ökosystems aktiv vorantreiben. Design für Wiederverwendung sollte ein zentrales Prinzip sein. Aus US-Perspektive ist Beschaffung (Procurement) ein grosses Problem: Die Nutzung von Open Source wächst, aber Unterstützungsmechanismen oft nicht. Wenn Open Source als „kostenlos“ betrachtet wird, kann das dazu führen, dass Aufträge an Anbieter gehen, die ihre Arbeit zwar als Open Source vermarkten, aber kein nachhaltiges Ökosystem unterstützen. Sie schlug konkrete Anpassungen der öffentlichen Beschaffung im Sinne von <strong>„Public Money, Public Code“</strong> vor, um das Ökosystem besser zu unterstützen. Ein wirklich inspirierender Abschluss für einen intensiven Tag voller Lernen und Austausch.</p>
<p>Wenn du tiefer in die Inhalte eintauchen möchtest, kannst du dir <a href="https://www.youtube.com/playlist?list=PLNubpNMwP36QH5Y3RlbOiV4f9hjlrxCOo">die Playlist</a> der Präsentationen von Drupal4Gov EU 2026 ansehen.</p>]]></description>
    </item>
        <item>
      <title>Web Components: The Good, the Bad, and the Ugly</title>
      <link>https://www.liip.ch/de/blog/web-components-the-good-the-bad-and-the-ugly</link>
      <guid>https://www.liip.ch/de/blog/web-components-the-good-the-bad-and-the-ugly</guid>
      <pubDate>Wed, 11 Mar 2026 00:00:00 +0100</pubDate>
      <description><![CDATA[<h1>Introduction</h1>
<p>We created a fully themeable chat UI that can be embedded in any website and has no effect on the parent page. <a href="https://www.bs.ch/alva">Kanton Basel-Stadts Alva</a> and <a href="https://ramms.ch/">RAMMS' Rocky AI</a> are instances of that UI.</p>
<p>This blog post will show you what we learned about creating web components that do not influence the parent page. Here are the good, the bad and the ugly when working with web components.</p>
<h1>The Good</h1>
<p>These are the good parts of web components. They will lay the foundation for why you might use them.</p>
<h2>Portability</h2>
<p>Every system that can handle HTML can handle web components. A simple tag and a script will integrate it into any web framework. It doesn't even need to be a JavaScript framework.</p>
<pre><code class="language-html">&lt;body&gt;
  &lt;your-webcomponent&gt;&lt;/your-webcomponent&gt;
  &lt;script src="path/to/your-webcomponent.js"&gt;&lt;/script&gt;
&lt;/body&gt;</code></pre>
<h2>Native Feel</h2>
<p>IFrames are another way to embed UI into a page, and they are arguably easier to use. But the main difference is that web components feel more native to the page, since they directly integrate into the parent page's layout. This means you can use transparency, intrinsic sizing (size based on the web component's contents), and seamless event communication with the parent page.</p>
<h2>Slots</h2>
<p>With slots you can provide content that will be added at a specified point inside your web component.</p>
<p>In our chat UI, we used a slot to let integrators provide a custom loading spinner. This spinner needs to be visible immediately, before the full theme loads asynchronously.</p>
<h2>Shadow DOM - Isolating Styles</h2>
<p>A robust way to ensure that your styles do not affect the parent page is to use the Shadow DOM. Shadow DOM is a web component feature to add a boundary for styles. Styles applied inside the Shadow DOM never apply to the parent page.</p>
<h3>Caveat: Inheritable CSS Properties</h3>
<p>There is one exception to the isolation where CSS properties of the parent page apply to the web component.</p>
<p>These are the properties that pierce through the boundary:</p>
<ul>
<li>Inheritable CSS properties like <code>color</code>, <code>font-family</code>, <code>line-height</code></li>
<li>CSS custom properties like <code>--my-var</code></li>
</ul>
<p>In practice, we have found it helps to fully specify the common properties like fonts and color on every element. That way you will never be surprised by different styles on integration.</p>
<h1>Vite</h1>
<p>For bundling web components, we can highly recommend Vite. There are a lot of neat tricks you can apply while bundling. Here are the Vite features we used for our web component.</p>
<h2>Inlining Assets</h2>
<p>Vite's <a href="https://vite.dev/guide/assets#explicit-inline-handling">explicit inline handling</a> feature allowed us to inline our external CSS files into the JS bundle.</p>
<pre><code class="language-ts">import cssContentString from "./index.css?inline";</code></pre>
<p>This feature will not only inline the raw content of the imported <code>index.css</code>. It will also resolve all CSS imports, apply PostCSS transforms, and even work with CSS preprocessors like SASS. While inlined CSS is not the most efficient for browsers to render, the benefit is that we can ship a single JS file.</p>
<h2>Library Mode</h2>
<p>The Vite <a href="https://vite.dev/guide/build#library-mode">library mode</a> provides you with fine-grained control of how the bundle should behave. To enable the library mode just add the <code>build.lib</code> option in your Vite config.</p>
<h1>The Bad</h1>
<p>Not everything about web components is great though. Here are the bad parts.</p>
<h2>SSR - Hard to Get Working</h2>
<p>Server-side rendering will almost certainly not work. The rest of the page can still be rendered server-side, but the web component will only show up as a <code>&lt;your-webcomponent&gt;&lt;/your-webcomponent&gt;</code> tag. Its contents will only be rendered in the browser.</p>
<p>There is one <a href="https://lit.dev/docs/ssr/overview/">experimental package by Lit Labs</a> that tries to solve this, but we never tried it.</p>
<h2>Tailwind - Not a Great Fit</h2>
<p>Tailwind feels like a natural choice for web components, but it does not play well with them.</p>
<p>The core issue is twofold. First, Tailwind ships its own CSS reset (called Preflight), which overrides default browser styles. When injected into a page that does not use Tailwind, it potentially breaks the page. Shadow DOM could isolate this reset, but Tailwind is fundamentally not designed to work inside a Shadow DOM. Here is the <a href="https://github.com/tailwindlabs/tailwindcss/discussions/1935">discussion</a> if you are interested.</p>
<p>There are some hacky workarounds, but we tried them and had no success getting them to work reliably.</p>
<p>Our recommendation is to only use Tailwind if you are guaranteed that the parent page also uses it, and then use web components without Shadow DOM.</p>
<h1>The Ugly</h1>
<h2>Verbose Web Components API</h2>
<p>The native web component API is verbose and hard to read. A simple counter component, for example, requires manually defining a class, attaching a shadow root, setting up <code>innerHTML</code>, and wiring event listeners in <code>connectedCallback</code>. This boilerplate adds up quickly. You can see examples of the API <a href="https://github.com/mdn/web-components-examples">here</a>.</p>
<p>Fortunately, web components make for a great compile target. <a href="https://svelte.dev/docs/svelte/custom-elements">Svelte</a> and <a href="https://vuejs.org/api/custom-elements.html#definecustomelement">Vue</a> directly support compiling to web components. <a href="https://blog.logrocket.com/working-custom-elements-react/">React</a> is a bit trickier, but totally doable as well. We used this approach for our chat UI, where the first iteration was built with React and the current one with Svelte.</p>
<h1>Weird Quirks</h1>
<p>Advanced web component features come with edge cases that no documentation warns you about. Even Svelte, which has excellent web component support, ships with a notable <a href="https://svelte.dev/docs/svelte/custom-elements#Caveats-and-limitations">list of caveats</a>.</p>
<p>We even hit an undocumented edge case with slots in Svelte: the bundle script must load after the component markup, or slotted content will not render. An ugly <a href="https://github.com/FalkZ/svelte-web-components-starter/blob/main/src/slot.svelte">wrapper for slots</a> fixes the problem, but quirks like this add up and slow you down.</p>
<h2>Font Loading - Not Working Inside Shadow DOM</h2>
<p>When authoring web components, you get into the habit of defining all stylesheet links etc. in the web component body. As you should, so they do not affect the parent page. But there is another annoying detail here: @font-face will not work when defined in the Shadow DOM. If your web component needs a custom font, you need to inject the font CSS into the parent page to make it work.</p>
<h1>Conclusion</h1>
<p>I do not want to end on this ugly note though. I really think there are cases where web components are the right choice, and in our case we would choose Svelte &amp; web components again.</p>
<p>To help you get started, here is a <a href="https://github.com/FalkZ/svelte-web-components-starter">Svelte starter template</a>.</p>]]></description>
    </item>
        <item>
      <title>City of Zurich&#039;s 900+ Open Data Sets Now Have an MCP Server</title>
      <link>https://www.liip.ch/de/blog/city-of-zurich-s-900-open-data-sets-now-have-an-mcp-server</link>
      <guid>https://www.liip.ch/de/blog/city-of-zurich-s-900-open-data-sets-now-have-an-mcp-server</guid>
      <pubDate>Thu, 26 Feb 2026 00:00:00 +0100</pubDate>
      <description><![CDATA[<p><a href="https://www.linkedin.com/in/alexander-g%C3%BCntert-3379071b6/">Alexander Güntert</a> <a href="https://www.linkedin.com/posts/activity-7432101739589345280-0YcB">posted on LinkedIn</a> about a new open-source project his colleague <a href="https://www.linkedin.com/in/hayaloezkan/">Hayal Oezkan</a> had built: an <a href="https://github.com/malkreide/zurich-opendata-mcp">MCP server for Zurich's open data</a>. The post got quite some reactions and I liked the idea very much. But it still needed a local installation, not something non-developers easily know how to do. So I had it packaged and deployed on our servers, available for anyone to use as the "OGD City of Zurich" remote MCP server.</p>
<p>The City of Zurich publishes over 900 datasets as open data, spread across six different APIs. There's <a href="https://data.stadt-zuerich.ch">CKAN</a> for the main data catalog, a WFS Geoportal for geodata, the Paris API for parliamentary information from the Gemeinderat, a tourism API, SPARQL linked data, and ParkenDD for real-time parking data. All public, all freely available. But until now, making an AI assistant actually use these APIs meant writing custom integrations for each one.</p>
<p>The MCP server wraps all six APIs into 20 tools that any MCP-compatible AI assistant can call directly. Ask "How warm is it in Zurich right now?" and it queries the live weather stations. Ask about parking availability, and it pulls real-time data from 36 parking garages. It also covers parliamentary motions, tourism recommendations, SQL queries on the data store, and GeoJSON features for school locations, playgrounds, or climate data. All through a single, standardized <a href="https://modelcontextprotocol.io/">Model Context Protocol</a> interface.</p>
<p>Hayal Oezkan  built it in Python using FastMCP. One file for the server with all 20 tool handlers. The <a href="https://github.com/malkreide/zurich-opendata-mcp">repo</a> is on GitHub.</p>
<p>Deploying it on our side took very little effort. The server supports both stdio transport for local use (like in Claude Desktop or Claude Code) and SSE and HTTP Streaming for remote deployment. I packaged it with Docker, deployed it to our cluster, and now it's available as a remote MCP server that anyone can add to their AI tools without installing anything locally.</p>
<p>The natural next step was integrating this with <a href="https://zuericitygpt.ch/">ZüriCityGPT</a>. It happened, just not quite in the direction I originally had in mind.</p>
<p>ZüriCityGPT already had its own MCP server at zuericitygpt.ch/mcp, exposing tools for searching the city's website content, "Stadtratsbeschlüsse" and looking up waste collection schedules. Instead of wiring the open data tools into ZüriCityGPT, I went the other way: the open data MCP server now proxies tools from the ZüriCityGPT MCP server. A lightweight proxy client connects to the remote server via streamable-http and forwards calls. The whole thing is about 40 lines of Python.</p>
<p>So now, when you connect to the Zurich Open Data MCP server, you get 23 tools in one place. The 21 original open data tools across six APIs, plus <code>zurich_search</code> for querying the city's knowledge base and <code>zurich_waste_collection</code>  for waste pickup schedules (based on the <a href="https://openerz.metaodi.ch/documentation">OpenERZ API</a>). One MCP endpoint, many services behind it. </p>
<p>A city employee builds something useful in the open, publishes the code, and within a day it's deployed and available to a wider audience. Open data and open source working together, exactly as intended.</p>]]></description>
    </item>
      </channel>
</rss>