<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Christof Karisch</title>
    <description>The latest articles on DEV Community by Christof Karisch (@christofkarisch).</description>
    <link>https://dev.to/christofkarisch</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1080430%2Fbd2d9a98-4f79-4d7c-8f4b-2339cbcd33d1.png</url>
      <title>DEV Community: Christof Karisch</title>
      <link>https://dev.to/christofkarisch</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/christofkarisch"/>
    <language>en</language>
    <item>
      <title>Taming a Third-Party React App Inside WordPress — Without Touching Its Code</title>
      <dc:creator>Christof Karisch</dc:creator>
      <pubDate>Tue, 04 Aug 2026 17:21:18 +0000</pubDate>
      <link>https://dev.to/christofkarisch/taming-a-third-party-react-app-inside-wordpress-without-touching-its-code-16g1</link>
      <guid>https://dev.to/christofkarisch/taming-a-third-party-react-app-inside-wordpress-without-touching-its-code-16g1</guid>
      <description>&lt;p&gt;Sometimes the most interesting frontend work isn't building an app — it's bending one you don't control.&lt;/p&gt;

&lt;p&gt;For the Austrian animal shelter &lt;a href="https://www.aktivertierschutz.at/" rel="noopener noreferrer"&gt;Arche Noah (Aktiver Tierschutz Austria)&lt;/a&gt;, the pet adoption listings are powered by a commercial WordPress plugin that renders a &lt;strong&gt;React app&lt;/strong&gt; on the page. The shelter manages its animals in an external SaaS ("Pet-Manager"), and the plugin pulls that data and renders searchable lists, filters, and detail views.&lt;/p&gt;

&lt;p&gt;The catch: the design didn't match the site at all, several fields were wrong for the audience (raw birth dates instead of age, cat genders in the dog filter), and important UI was missing (a phone CTA on every animal). And of course:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We &lt;strong&gt;can't fork the plugin&lt;/strong&gt; — every update would wipe our changes.&lt;/li&gt;
&lt;li&gt;We &lt;strong&gt;can't patch the React bundle&lt;/strong&gt; — it's minified vendor code.&lt;/li&gt;
&lt;li&gt;The markup &lt;strong&gt;doesn't exist at page load&lt;/strong&gt; — React renders it client-side, and re-renders it constantly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So we built an &lt;em&gt;augmentation layer&lt;/em&gt;: a single custom-code block (CSS + vanilla JS with &lt;code&gt;MutationObserver&lt;/code&gt;) that reshapes the app from the outside. You can see the result live on the &lt;a href="https://www.aktivertierschutz.at/hundevergabe/" rel="noopener noreferrer"&gt;dog adoption page&lt;/a&gt; and the &lt;a href="https://www.aktivertierschutz.at/katzenvergabe/" rel="noopener noreferrer"&gt;cat adoption page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post is about the three rules we learned — including the React reconciliation bug that only showed up when users clicked "next animal".&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule 1: Never move or remove React-managed DOM nodes
&lt;/h2&gt;

&lt;p&gt;This is the big one, and we learned it the hard way.&lt;/p&gt;

&lt;p&gt;The detail view shows a "Merkmale" (characteristics) table row that we wanted to display as its own "Besonderheiten" (special traits) card next to the "Eigenschaften" (properties) card. Our first version did the obvious thing: grab the &lt;code&gt;.Eigenschaften&lt;/code&gt; node, move it into a new row, delete the characteristics &lt;code&gt;&amp;lt;tr&amp;gt;&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;It worked perfectly. On first load.&lt;/p&gt;

&lt;p&gt;But the app has "previous animal / next animal" navigation. On those transitions, React doesn't rebuild the DOM from scratch — it &lt;strong&gt;reconciles&lt;/strong&gt;: it diffs its virtual DOM against what it &lt;em&gt;believes&lt;/em&gt; is in the real DOM and applies minimal patches. If you've moved or deleted nodes React thinks it owns, its bookkeeping is now wrong. In our case, React silently rendered a broken subtree: the characteristics row stopped being processed, and even an unrelated transformation (birth date → age) stopped firing.&lt;/p&gt;

&lt;p&gt;No error, no warning. Just a subtly wrong UI, one click away from the state everyone tests.&lt;/p&gt;

&lt;p&gt;The fix defines the whole approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hide, don't remove.&lt;/strong&gt; React doesn't care about &lt;code&gt;display: none&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Append foreign nodes, don't reparent React's nodes.&lt;/strong&gt; Elements &lt;em&gt;you&lt;/em&gt; create and append survive React's re-renders — React ignores children it didn't render, as long as you don't disturb its own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On every render, only update text/content.&lt;/strong&gt; Never restructure.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processDetail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.petfinder .tm-pet:not(.card)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;eig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.Eigenschaften&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;eig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Read the current value from React's table (may be absent)&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;merkmaleValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.Allgemein .table tr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;td:first-child&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Merkmale&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;td:last-child&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;merkmaleValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// Hide only — this &amp;lt;tr&amp;gt; belongs to React&lt;/span&gt;
        &lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-kwm-mhidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;show&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;merkmaleValue&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;merkmaleValue&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;merkmaleValue&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Keine Besonderheiten bekannt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Our OWN node, appended into React's row — survives re-renders&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;eigRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;eig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;closest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.row&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;eigRow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;eigRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.kwm-besond&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Besonderheiten card col-12 col-md-6 kwm-besond&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;eigRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;div class="h3 mt-4"&amp;gt;Besonderheiten&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;show&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Write only on actual change — prevents observer feedback loops&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rule 2: Make every MutationObserver pass idempotent
&lt;/h2&gt;

&lt;p&gt;All transformations run inside &lt;code&gt;MutationObserver&lt;/code&gt; callbacks watching the app's root container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;pf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;petfinder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;processDetail&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MutationObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;processDetail&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;childList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;subtree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readyState&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's an obvious trap here: your callback mutates the DOM, which fires the observer, which runs the callback, which mutates the DOM… To stay out of that loop, every pass must be &lt;strong&gt;idempotent&lt;/strong&gt; — running it twice must be a no-op the second time. We use three patterns, depending on how React treats the node:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern A — a &lt;code&gt;data-&lt;/code&gt; guard&lt;/strong&gt;, for nodes React creates once and never recycles (list cards):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kwmDone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ...transform...&lt;/span&gt;
&lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kwmDone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pattern B — a self-consuming condition&lt;/strong&gt;, for nodes React &lt;em&gt;recycles&lt;/em&gt;. This one is subtle: on "next animal", React reuses the existing &lt;code&gt;&amp;lt;tr&amp;gt;&lt;/code&gt; elements and just resets their text — so a &lt;code&gt;data-&lt;/code&gt; guard on the element would wrongly skip the fresh content. Instead, the transformation itself removes its own trigger condition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Fires only while the label is the untransformed "Geburtsdatum".&lt;/span&gt;
&lt;span class="c1"&gt;// After we rename it to "Alter", the condition no longer matches —&lt;/span&gt;
&lt;span class="c1"&gt;// until React's next render resets it. Idempotent, no guard needed.&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.petfinder .tm-pet:not(.card) .table tr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;td:first-child&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Geburtsdatum&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;valueCell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;td:last-child&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;valueCell&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;calcAge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valueCell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;valueCell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pattern C — compare before writing&lt;/strong&gt;, for content you keep in sync on every render (the &lt;code&gt;box.innerHTML !== html&lt;/code&gt; check in Rule 1). No change, no mutation, no observer echo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule 3: Let CSS do everything it can
&lt;/h2&gt;

&lt;p&gt;The less JavaScript touches the DOM, the fewer reconciliation and observer problems you have. Two CSS tricks earned their keep:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fixing zebra stripes after hiding a row.&lt;/strong&gt; Hiding a &lt;code&gt;&amp;lt;tr&amp;gt;&lt;/code&gt; with &lt;code&gt;display: none&lt;/code&gt; doesn't remove it from &lt;code&gt;:nth-child()&lt;/code&gt; counting — so &lt;code&gt;table-striped&lt;/code&gt; flips parity from that row on, and your visible rows suddenly alternate wrong. Since we mark the hidden row with an attribute, a sibling combinator can flip the parity back for everything after it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.table-striped&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;tbody&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;odd&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eef3e5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Everything after the hidden row: invert the striping */&lt;/span&gt;
&lt;span class="nc"&gt;.table-striped&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;tbody&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-kwm-mhidden&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;odd&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.table-striped&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;tbody&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-kwm-mhidden&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;even&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eef3e5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Layout with &lt;code&gt;:has()&lt;/code&gt; instead of moving nodes.&lt;/strong&gt; Our appended "Besonderheiten" box lands as a third column in a Bootstrap row that React already populated. Instead of reordering React's columns (forbidden — see Rule 1), we force the &lt;em&gt;other&lt;/em&gt; columns full-width, so our box and its partner wrap onto their own line together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.tm-pet&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.card&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;.row&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;.kwm-besond&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;class&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;"col-"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.Eigenschaften&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.kwm-besond&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One more that surprised us: Bootstrap 5's &lt;code&gt;table-striped&lt;/code&gt; colors cells via an &lt;strong&gt;inset &lt;code&gt;box-shadow&lt;/code&gt;&lt;/strong&gt; and CSS custom properties, not &lt;code&gt;background&lt;/code&gt;. If your override isn't sticking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;td&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--bs-table-accent-bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus: context-aware behavior without an API
&lt;/h2&gt;

&lt;p&gt;The plugin renders the same app everywhere; it doesn't know "this is the dogs page". We encode that context in wrapper classes (&lt;code&gt;.kwm-filter.dogs&lt;/code&gt;, &lt;code&gt;.kwm-slider.cats&lt;/code&gt;) around the shortcode and read them from the augmentation layer — e.g. to restrict the gender filter to &lt;code&gt;Rüde/Hündin&lt;/code&gt; on the dog page and &lt;code&gt;Kater/Kätzin&lt;/code&gt; on the cat page, or to route card clicks to the right list page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;slider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;closest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.kwm-slider&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cats&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/vergabekatzen/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dogs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/vergabehunde/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;?kwm_pf_id=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;kwm_pf_v=details&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cheap, declarative, and it survives plugin updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Treat React-managed DOM as read-only structure.&lt;/strong&gt; Hide with CSS, append your own nodes, update text — never move or delete what React rendered. The bugs you cause otherwise don't appear on first load; they appear on the second interaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design every observer pass to be idempotent&lt;/strong&gt;, and pick the right guard: &lt;code&gt;data-&lt;/code&gt; flags for stable nodes, self-consuming conditions for recycled nodes, compare-before-write for synced content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefer CSS over JS&lt;/strong&gt; for layout and visibility — &lt;code&gt;:has()&lt;/code&gt;, sibling combinators, and attribute hooks set by your JS layer cover more than you'd expect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep it all in one place.&lt;/strong&gt; Everything lives in a single custom-code block, version-controlled in git — the plugin stays untouched and updateable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Is this fragile? Somewhat — it's coupled to the vendor's class names, and a major plugin redesign would mean rework. But every transformation fails &lt;em&gt;soft&lt;/em&gt; (a missing selector just means an untransformed field, never a crash), and compared to forking a commercial plugin, it's the far more maintainable trade-off.&lt;/p&gt;

&lt;p&gt;And the real win isn't technical: the shelter — one of Austria's largest, with room for 163 dogs and 204 cats — gets an adoption UI that looks like their site and speaks to adopters, on &lt;a href="https://www.aktivertierschutz.at/" rel="noopener noreferrer"&gt;aktivertierschutz.at&lt;/a&gt;. If you're in Austria and thinking about adopting: &lt;a href="https://www.aktivertierschutz.at/hundevergabe/" rel="noopener noreferrer"&gt;the dogs are over here&lt;/a&gt;. 🐾&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This project was implemented by &lt;a href="https://www.sovaro-consulting.com/" rel="noopener noreferrer"&gt;Sovaro Consulting&lt;/a&gt; — we help organizations get the most out of WordPress, even when third-party plugins fight back.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>wordpress</category>
      <category>javascript</category>
      <category>css</category>
    </item>
    <item>
      <title>I almost credited llms.txt for a Google AI Mode win. Then I read what Google actually says.</title>
      <dc:creator>Christof Karisch</dc:creator>
      <pubDate>Wed, 24 Jun 2026 15:17:52 +0000</pubDate>
      <link>https://dev.to/christofkarisch/i-almost-credited-llmstxt-for-a-google-ai-mode-win-then-i-read-what-google-actually-says-22ko</link>
      <guid>https://dev.to/christofkarisch/i-almost-credited-llmstxt-for-a-google-ai-mode-win-then-i-read-what-google-actually-says-22ko</guid>
      <description>&lt;p&gt;I shipped an &lt;code&gt;llms.txt&lt;/code&gt; file for a client, their visibility in Google's AI Mode jumped over the same period, and I nearly wrote it up as a clean before/after win for the file.&lt;/p&gt;

&lt;p&gt;Then I checked the primary source. This is the honest version — including the part where my first interpretation was wrong.&lt;/p&gt;

&lt;p&gt;Context: I'm Christof, a software engineer and technical team lead under &lt;a href="https://sovaro-consulting.com/de" rel="noopener noreferrer"&gt;Sovaro Consulting&lt;/a&gt; in Austria. The client is California Metals, a B2B company specializing in &lt;a href="https://www.californiametals.com/" rel="noopener noreferrer"&gt;Sustainable Metals&lt;/a&gt; — low-carbon alloys and finished components for aerospace, automotive and marine OEMs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I observed
&lt;/h2&gt;

&lt;p&gt;Over one reporting cycle, in Google's AI Mode:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;California Metals started appearing as the first result for relevant queries.&lt;/li&gt;
&lt;li&gt;Two of its certifications were surfaced (a third, ISO 14001, was not — yet).&lt;/li&gt;
&lt;li&gt;Its LinkedIn entity gained weight, to the point of showing up twice in the right-hand panel.
I had just shipped an &lt;code&gt;llms.txt&lt;/code&gt; summarizing the company, its certifications and its key pages. Tempting story: &lt;em&gt;we curated a machine-readable summary, and Google's AI started reading from it.&lt;/em&gt; A clean before/after — the kind that's very easy to believe.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's also almost certainly wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Google actually says
&lt;/h2&gt;

&lt;p&gt;Google updated its &lt;a href="https://developers.google.com/search/docs/fundamentals/ai-optimization-guide" rel="noopener noreferrer"&gt;AI optimization guide&lt;/a&gt; (Search Central docs, last updated 2026-06-15) with a mythbusting section that names &lt;code&gt;llms.txt&lt;/code&gt; directly. The short version: you don't need &lt;code&gt;llms.txt&lt;/code&gt; or any special AI markup to appear in Google Search &lt;strong&gt;including its generative AI features&lt;/strong&gt;, because Google Search doesn't use those files. Maintaining one neither helps nor hurts your Google ranking.&lt;/p&gt;

&lt;p&gt;This explicitly covers AI Overviews and AI Mode. Per Google's own &lt;a href="https://developers.google.com/search/docs/appearance/ai-features" rel="noopener noreferrer"&gt;AI features documentation&lt;/a&gt;, those surfaces run on the &lt;em&gt;same&lt;/em&gt; index and the &lt;em&gt;same&lt;/em&gt; ranking and quality systems as ordinary organic results. AI Mode adds a "query fan-out" — it spins off several related sub-queries and pulls from the normal Search index — but there's no separate &lt;code&gt;llms.txt&lt;/code&gt; channel feeding it.&lt;/p&gt;

&lt;p&gt;So the file I shipped was not what put my client at the top of AI Mode. Google grounds AI Mode in what it already crawls and trusts across the open web. Which also means the certifications shown there come from sources Google crawls — not from my &lt;code&gt;llms.txt&lt;/code&gt;. ISO 14001 will appear in AI Mode when Google picks it up from credible pages (the site itself, certification registries, structured data), &lt;strong&gt;not&lt;/strong&gt; "automatically when I update the llms.txt." That assumption was the core of my mistake.&lt;/p&gt;

&lt;h2&gt;
  
  
  "But doesn't ChatGPT read it?"
&lt;/h2&gt;

&lt;p&gt;This is the most common pushback, so it's worth being precise. The bots that actually feed ChatGPT — OAI-SearchBot for its search answers, GPTBot for training — crawl HTML directly, and OpenAI's own crawler documentation manages them through &lt;code&gt;robots.txt&lt;/code&gt; without mentioning &lt;code&gt;llms.txt&lt;/code&gt; at all. Independent log studies covering hundreds of millions of AI-bot requests found hits to &lt;code&gt;/llms.txt&lt;/code&gt; to be statistically negligible. GPTBot reportedly fetches the file now and then, but fetching isn't the same as using it to choose or cite sources.&lt;/p&gt;

&lt;p&gt;Where &lt;code&gt;llms.txt&lt;/code&gt; genuinely gets read is a different layer: coding and agent tooling — &lt;a href="https://cursor.com" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;, &lt;a href="https://www.anthropic.com/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt;, &lt;a href="https://github.com/features/copilot" rel="noopener noreferrer"&gt;GitHub Copilot&lt;/a&gt;, &lt;a href="https://windsurf.com" rel="noopener noreferrer"&gt;Windsurf&lt;/a&gt; — pulling a documentation site's pages with less token waste, plus emerging agent protocols like OpenAI's Agents SDK. That's real, and it's growing fast.&lt;/p&gt;

&lt;p&gt;But notice what that use case requires: documentation worth fetching, and an audience that queries AI tools about your product.&lt;/p&gt;

&lt;h2&gt;
  
  
  What probably actually moved the needle
&lt;/h2&gt;

&lt;p&gt;Looking at the same reporting period honestly, the likeliest drivers are textbook SEO, not the text file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Entity/authority signals.&lt;/strong&gt; The client's LinkedIn presence gained weight in that window — that's exactly the kind of corroborating entity signal Google's AI surfaces lean on. The double-listing is a symptom of a strengthening entity, not of a Markdown file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The rest of the cycle's work:&lt;/strong&gt; clearer content, internal linking, crawlability, consistent NAP/brand data across the web.
None of that is glamorous, and none of it needs an AI-specific hack. That's the actual takeaway Google keeps repeating: AI search visibility &lt;em&gt;is&lt;/em&gt; SEO.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  So is llms.txt useless? No — but be precise
&lt;/h2&gt;

&lt;p&gt;It's still worth shipping, for the &lt;strong&gt;right&lt;/strong&gt; reason: it's a business-to-agent (B2A) surface for non-Google LLMs and agent tooling (ChatGPT and Claude agents, Cursor, Windsurf), not a Google ranking lever. Just do it correctly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Name it &lt;code&gt;llms.txt&lt;/code&gt; — plural — and serve it at the domain root (&lt;code&gt;/llms.txt&lt;/code&gt;) with a &lt;code&gt;text/plain&lt;/code&gt; content type. That's the only path agent tools look for.&lt;/li&gt;
&lt;li&gt;Lead with a tight, factual blockquote — that paragraph is what an agent lifts as its mental model of the brand. No marketing adjectives.&lt;/li&gt;
&lt;li&gt;Curate, don't dump. A short, high-signal link list.&lt;/li&gt;
&lt;li&gt;Only list indexable, canonical pages.
The file itself is trivial Markdown:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# California Metals&lt;/span&gt;
&lt;span class="gt"&gt;
&amp;gt; Supplier of low-carbon, sustainably sourced metals, alloys and finished&lt;/span&gt;
&lt;span class="gt"&gt;&amp;gt; components for aerospace, defense, automotive, marine and luxury-goods&lt;/span&gt;
&lt;span class="gt"&gt;&amp;gt; manufacturers. Certifications: AS9100, ISO 14001:2015, ISO 20400.&lt;/span&gt;

&lt;span class="gu"&gt;## Core pages&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Capabilities&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://www.californiametals.com/...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Materials and processes.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Sustainability&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://www.californiametals.com/...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: CO2-reduction and sourcing standards.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Industries&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://www.californiametals.com/...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Sectors and applications.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most company sites are on WordPress, where a static file in the web root (or a Yoast/Rank Math toggle) is enough. If you use Next.js, serve it from a route handler — same source of truth as the sitemap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/llms.txt/route.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;force-static&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`# California Metals

&amp;gt; Supplier of low-carbon, sustainably sourced metals and alloys...
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/plain; charset=utf-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;The improvement was real; the attribution wasn't. Google's AI surfaces rewarded ordinary SEO and a strengthening brand entity — the &lt;code&gt;llms.txt&lt;/code&gt; came along for the ride rather than driving it. Ship the file as cheap B2A infrastructure if you want to be early for agent traffic, but keep it on the right side of the ledger: it isn't what gets a non-developer site to the top of AI Mode. Clear content, clean crawlability, and a coherent brand entity are.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written by Christof Karisch — software engineer and technical team lead at &lt;a href="https://sovaro-consulting.com/de" rel="noopener noreferrer"&gt;Sovaro Consulting&lt;/a&gt;. Work done for my client &lt;a href="https://www.californiametals.com/" rel="noopener noreferrer"&gt;California Metals&lt;/a&gt;, a sustainable metals supplier.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Moz Domain Authority Score for SEO Success</title>
      <dc:creator>Christof Karisch</dc:creator>
      <pubDate>Mon, 01 Jul 2024 08:18:13 +0000</pubDate>
      <link>https://dev.to/christofkarisch/moz-domain-authority-score-for-seo-success-nfn</link>
      <guid>https://dev.to/christofkarisch/moz-domain-authority-score-for-seo-success-nfn</guid>
      <description>&lt;p&gt;At &lt;a href="https://www.formundzeichen.at/"&gt;Form und Zeichen&lt;/a&gt;, we continually navigate the evolving SEO landscape to provide top-notch digital marketing solutions. Recently, for our client &lt;a href="https://www.californiametals.com/"&gt;California Metals&lt;/a&gt;, we needed a reliable metric to compare their website's performance against competitors. Seeking an insightful and engaging tool, we discovered &lt;a href="https://moz.com/learn/seo/domain-authority"&gt;Moz Domain Authority&lt;/a&gt; (DA) and its visualization diagram, revolutionizing our SEO assessments and communications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring Domain Authority with Moz
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://moz.com/learn/seo/domain-authority"&gt;Moz Domain Authority&lt;/a&gt; score diagram is an interactive visualization tool that allows SEO professionals and website owners to compare DA scores across multiple domains, track DA score changes over time, identify areas for improvement in link building strategies, and analyze competitors' SEO performance. This powerful diagram leverages data from Moz's extensive link index and presents it in an easy-to-understand format, making it an invaluable asset for both seasoned SEO experts and newcomers to the field.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F759hoyrm0f2e4v5ks59y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F759hoyrm0f2e4v5ks59y.png" alt="Image description" width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrating the Moz DA Score into Your SEO Workflow
&lt;/h2&gt;

&lt;p&gt;To make the most of this powerful tool, consider the following tips: Make it a habit to check and update your DA score diagram at least once a month. Share the diagram with your SEO team to foster collaborative efforts in improving your website's authority. Use the visual nature of the diagram to clearly communicate SEO progress and strategies to clients. Let the insights gained from the diagram guide your content creation and link building strategies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://moz.com/learn/seo/domain-authority"&gt;Moz Domain Authority&lt;/a&gt; score diagram is a versatile and insightful tool for SEO professionals and website owners. By incorporating it into your regular workflow, you can gain valuable insights, track progress, and make data-driven decisions to improve your website's authority and overall search engine performance. Whether you're a seasoned expert or new to SEO, this visualization tool can help streamline your efforts and drive meaningful results in the competitive digital landscape.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Accessibility errors and special file types in WordPress Elementor</title>
      <dc:creator>Christof Karisch</dc:creator>
      <pubDate>Mon, 23 Oct 2023 20:21:38 +0000</pubDate>
      <link>https://dev.to/christofkarisch/accessibility-errors-and-special-file-types-in-wordpress-elementor-kik</link>
      <guid>https://dev.to/christofkarisch/accessibility-errors-and-special-file-types-in-wordpress-elementor-kik</guid>
      <description>&lt;p&gt;As web accessibility becomes increasingly important, at our Design Agency &lt;a href="https://www.formundzeichen.at/"&gt;Design Agentur Form und Zeichen in Graz&lt;/a&gt;, we are placing a stronger emphasis on fixing accessibility errors on our customer’s websites. A great page builder plugin for WordPress is Elementor. For small Websites, we utilize Elementor most of the time. While Elementor has a great implementation of various accessibility features, we noticed a critical error, that appears when WordPress pages are configured improperly.&lt;/p&gt;

&lt;p&gt;In this post, we will explain the “skip to content” error in WordPress Elementor using our client’s website, &lt;a href="https://www.californiametals.com/"&gt;California Metals&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Moreover, to inspire your web projects, we'd like to inform you that WordPress also supports 3D uploads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check your website for the skip to content error
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://wave.webaim.org/extension/"&gt;WAVE&lt;/a&gt; is a useful plugin available for Chrome, Firefox and Edge. With this tool, we can quickly find critical accessibility errors on the website. When navigating to details, the tool shows that the “skip to content” link is broken.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e18BjRpg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9o5ct065h1l7xw7qpmk1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e18BjRpg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9o5ct065h1l7xw7qpmk1.png" alt="WAVE tool for chrome: Analyzing California Metals" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reason why the skip to content link is broken, is that two HTML tags from Elementor’s site structure are missing. The missing  tag has the id “content”, which the “skip to content” link refers to. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FNsbar9H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nw5072zplt2byfm5d94t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FNsbar9H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nw5072zplt2byfm5d94t.png" alt="WAVE tool for chrome: Analyzing California Metals" width="800" height="143"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Read the Article about &lt;a href="https://www.formundzeichen.at/en/development/skip-to-content-error-in-wordpress-elementor/"&gt;Fixing the skip to content error&lt;/a&gt;, if you encountered the same problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Loading GLTF models in Elementor
&lt;/h2&gt;

&lt;p&gt;To load a model on a page, we need to use the HTML element of Elementor. On the &lt;a href="https://threejs.org/examples/"&gt;Three.js examples page&lt;/a&gt;, you can find the used script tag by inspecting the website. With a few adoptions, we can use those examples on our Elementor page. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--unK31rk2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vudeugpto33yvlzpfq6r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--unK31rk2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vudeugpto33yvlzpfq6r.png" alt="wordpress elementor three.js gltf example" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're interested in adding 3D models to your website or if you've faced file upload issues, read our blog post on &lt;a href="https://www.formundzeichen.at/en/development/three-js-in-wordpress-elementor/"&gt;using three.js with Elementor&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>wordpress</category>
    </item>
    <item>
      <title>Website design among different industries</title>
      <dc:creator>Christof Karisch</dc:creator>
      <pubDate>Sun, 18 Jun 2023 22:34:25 +0000</pubDate>
      <link>https://dev.to/christofkarisch/website-design-among-different-industries-5eoi</link>
      <guid>https://dev.to/christofkarisch/website-design-among-different-industries-5eoi</guid>
      <description>&lt;p&gt;At our Design Agency &lt;a href="https://www.formundzeichen.at/"&gt;Design Agentur Form und Zeichen in Graz&lt;/a&gt;, we specialize in creating beautiful websites that are tailored to each industry. In this article, we will showcase some of our client's websites, highlighting the design concepts behind them. Join us as we explore the inspirations and ideas that brought these designs to life.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enduring metal
&lt;/h2&gt;

&lt;p&gt;To convey a sense of durability, stability, and trust for our client &lt;a href="https://www.californiametals.com/"&gt;California Metals - Sustainable Metals&lt;/a&gt;, we aimed to create a visual impression that aligned with these values. While traditional metallic colors such as gray (steel), yellow (gold), red (copper), and blue (zinc, osmium) were considered, none of them seemed quite fitting. However, given our customer's commitment to supplying sustainable metals, we made a bold choice to incorporate a green metallic look. This distinctive color choice not only sets our client apart, but also emphasizes their unique company philosophy. Through this carefully crafted visual element, we aim to leave a lasting impression that resonates with their target audience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Colorful statements
&lt;/h2&gt;

&lt;p&gt;When working with our esteemed client &lt;a href="https://die-tierretter.at/"&gt;Aktiver Tierschutz Austria&lt;/a&gt;, we had a distinct vision in mind. Our goal was to draw attention to their noble mission of aiding animals while evoking positive emotions through visually appealing marketing materials. To achieve this, we opted for a vibrant and modern design approach. The use of multiple colors became instrumental, with a particular emphasis on light and friendly tones such as yellow, light blue, and rosy hues. This intentional color palette aims to captivate viewers' attention, inviting them to appreciate the playful and engaging aspects of the design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The selection of colors for your design plays a pivotal role. Colors establish the foundation for how a design communicates with people, how it visually presents itself, and the emotions it evokes. It is essential to invest time in discovering the perfect color palette, as doing so can elevate your designs to new heights. By carefully considering and choosing the right colors, you can enhance the overall impact and effectiveness of your designs, ensuring they resonate with your target audience on a deeper level. Remember, the power of colors can take your designs to the next level and make a lasting impression.&lt;/p&gt;

</description>
      <category>design</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Advanced Next.js challenges</title>
      <dc:creator>Christof Karisch</dc:creator>
      <pubDate>Sun, 11 Jun 2023 11:23:57 +0000</pubDate>
      <link>https://dev.to/christofkarisch/advanced-nextjs-challenges-4dh5</link>
      <guid>https://dev.to/christofkarisch/advanced-nextjs-challenges-4dh5</guid>
      <description>&lt;p&gt;At our Design Agency &lt;a href="https://www.formundzeichen.at/"&gt;Design Agentur Form und Zeichen in Graz&lt;/a&gt;, we are passionate about leveraging Next.js for our programming needs. We thrive on the opportunity to address and overcome even the most specialized customer requirements, thanks to the abundance of powerful libraries available. In this article, I will share a curated summary of fascinating challenges we have successfully tackled using modern techniques.&lt;/p&gt;

&lt;h2&gt;
  
  
  The perfect hosting provider
&lt;/h2&gt;

&lt;p&gt;As an EU-based company, we understand the significance of GDPR compliance for our customers. Many clients specifically request EU-based hosting to ensure their data remains within the region. However, finding an EU-based hosting provider that matches the exceptional quality of &lt;a href="https://vercel.com/"&gt;Vercel&lt;/a&gt; can be a challenge. &lt;/p&gt;

&lt;p&gt;We have written a blog article that ranks and reviews the top EU-based hosting providers. In this article, you will find a comprehensive summary of each provider's features, enabling you to choose the one that best suits your needs while ensuring GDPR compliance. Link to the article: &lt;a href="https://www.formundzeichen.at/en/development/next-js-hosting-in-the-eu/"&gt;Next.js hosting in the EU&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Achieving Exceptional Performance in Complex Logical Programs
&lt;/h2&gt;

&lt;p&gt;In the realm of Next.js programming, we occasionally encounter intricate logical challenges that demand innovative solutions. When faced with requirements of a logical nature, complexity can rise rapidly. For instance, envision the task of developing a bicycle configurator for a webshop, where various components such as the brake, wheel, and bicycle size have to be compatible.&lt;/p&gt;

&lt;p&gt;To address these complexities effectively, our blog article offers a comprehensive, &lt;a href="https://www.formundzeichen.at/en/development/nodejs-sat-solver/"&gt;step-by-step tutorial on harnessing the power of SAT solvers&lt;/a&gt;. By leveraging SAT solvers, we unlock a robust approach to tackling such tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There are many interesting challenges for web projects. I am curious to learn more about solving complex tasks and provide perfect solutions for our customers. You can find some ideas for modern web development on my &lt;a href="https://dev.to/christofkarisch/"&gt;profile page&lt;/a&gt;. If you want to read comprehensive tutorials for modern web development, have a look at our &lt;a href="https://www.formundzeichen.at/en/develpment-blog/"&gt;development blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>Modern Web Development with WordPress Elementor</title>
      <dc:creator>Christof Karisch</dc:creator>
      <pubDate>Sat, 27 May 2023 00:09:32 +0000</pubDate>
      <link>https://dev.to/christofkarisch/modern-web-development-30jo</link>
      <guid>https://dev.to/christofkarisch/modern-web-development-30jo</guid>
      <description>&lt;p&gt;As a web developer working at &lt;a href="https://www.formundzeichen.at/"&gt;Design Agentur Form und Zeichen in Graz&lt;/a&gt;, I have encountered various challenges while striving to build stunning, modern websites and stable web applications. Depending on the specific project requirements, we leverage different technologies and services. In this post, I aim to present some intriguing approaches for modern web development for small websites and provide a concise overview of essential software that every web developer should be familiar with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modern WordPress and some hidden gems
&lt;/h2&gt;

&lt;p&gt;Back in the day, when numerous content management systems (CMS) emerged and competed for dominance, it became commonplace to purchase a promising theme or utilize a free one to swiftly build a website. For a developer it was easy to build a clean and simple WordPress theme from scratch, but it was not possible to eliminate the plugins, especially when the website had to provide a lot of features or if it used ecommerce plugins like &lt;a href="https://woocommerce.com/"&gt;WooCommerce&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, as the website grew in size and the demand for additional functionality increased, more and more plugins were installed, leading to instability and a less professional appearance in terms of design.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qkTfgz0x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c1uzj5kw6kxl023cxamv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qkTfgz0x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c1uzj5kw6kxl023cxamv.png" alt="WordPress Logo" width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Furthermore, as time elapses, security issues begin to manifest. It is important to regularly update old themes and plugins, but unfortunately, some of them cease to receive updates. In fact, we had to resolve security breaches on multiple customer websites that were compromised by automated scripts. This is particularly crucial when it comes to &lt;a href="https://wordpress.org/"&gt;WordPress&lt;/a&gt;, as it is one of the mostly widely used CMS and therefore a target for many automated hacking attacks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simplicity and security with Elementor
&lt;/h3&gt;

&lt;p&gt;When visual post editing plugins like visual composer showed up, things got easier. Without detailed knowledge, a user could change the content of the website and even apply some minor layout and design changes. But with new possibilities, new problems arrived. Those visual editors produced a high amount of metadata, that is stored in the database. Because of the bad efficiency of the plugin, saving an edited page could take up to 20 seconds, depending on the complexity of the page. The page speed for customers, which is very important for Search Engine Optimization (SEO) suffered too. With cache plugins, the situation got better, at least for the publicly presented pages.&lt;/p&gt;

&lt;p&gt;When &lt;a href="https://elementor.com/"&gt;Elementor&lt;/a&gt; was released, there suddenly was a simple very easy-to-use editor, that apparently even works very efficient compared to others.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kAi4pa9i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vpzn0kawo75qiwqllv04.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kAi4pa9i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vpzn0kawo75qiwqllv04.png" alt="Elementor Logo" width="800" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The way of setting up a simple website changed completely. Instead of using a theme, even beginners are able to build simple websites. The first step is to install the free and clean &lt;a href="https://elementor.com/products/hello-theme/"&gt;Hello Elementor Theme&lt;/a&gt;. Because the theme is not the main product of, Elementor is very simple and clean and thus a very low security risk. Elementor also has a great interest in keeping the theme updated, as it is the foundation of a stable Elementor setup. They also provide a &lt;a href="https://github.com/elementor/hello-theme-child"&gt;child theme&lt;/a&gt;, if you want to extend it with custom code. But by now it is not necessary to install the child theme for small websites anymore, because Elementor has a great integration of custom code blocks for JavaScript and CSS code or any other HTML tag that is loaded to the HTML DOM.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modern design elements
&lt;/h3&gt;

&lt;p&gt;Nowadays, all important devices including smartphones are capable of processing complex website content without effort. With these capabilities, new possibilities arise. In a recent &lt;a href="https://www.formundzeichen.at/en/development/three-js-in-wordpress-elementor/"&gt;blog post about the integration of 3D objects into your WordPress Elementor website&lt;/a&gt; that I published on our &lt;a href="https://www.formundzeichen.at/"&gt;website&lt;/a&gt;, you can find a step-by-step guide. It is very easy to load 3D objects, and it only takes some basic HTML and JavaScript knowledge to adjust the code to your needs. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SoEFH4Ne--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4xevk5rhwn2toxqes4qh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SoEFH4Ne--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4xevk5rhwn2toxqes4qh.png" alt="load 3D objects with WordPress Elementor" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Fast websites with stable caching plugins
&lt;/h3&gt;

&lt;p&gt;I mentioned above, that we had to fix several security breaches on WordPress websites. Most of the time the vulnerability was caused by plugins. But even when the plugins are updated frequently, there are some unfixed high severity security issues in some plugins. Especially, caching plugins have a high chance of producing security problems. The plugin we had the best experience with is &lt;a href="https://wordpress.org/plugins/w3-total-cache/"&gt;W3 Total Cache&lt;/a&gt;. It is very fast, and we never had a security breach caused by this plugin so far. &lt;/p&gt;

&lt;h3&gt;
  
  
  Easy resets with a backup plugin
&lt;/h3&gt;

&lt;p&gt;Even when you have a stable and secure website setup, it is always advisable to configure automated backups. I have encountered numerous situations where a simple plugin update caused a WordPress website to crash. In such cases, or if you accidentally remove important components from your website, having a quick reset option can be extremely helpful.&lt;/p&gt;

&lt;p&gt;The most stable option is to setup &lt;a href="https://jetpack.com/"&gt;Jetpack&lt;/a&gt; for your website. But this service is charged. If you want to use a free alternative, I would recommend &lt;a href="https://wordpress.org/plugins/updraftplus/"&gt;UpdraftPlus&lt;/a&gt;. This backup solution is hosted on your website, and you can connect file storage services like &lt;a href="https://dropbox.com/"&gt;Dropbox&lt;/a&gt; to be sure to never lose your data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic security setup
&lt;/h3&gt;

&lt;p&gt;Finally, the stability of the website can be improved by a huge step with a security plugin. &lt;a href="https://wordpress.org/plugins/wordfence/"&gt;Wordfence Security&lt;/a&gt; is one of the most popular plugins of this kind. Make sure to back up your website before you install plugins like this, because they change internal WordPress files and settings. There is a small possibility, that your website crashes during the setup of the plugin because of wrong hosting settings or because you lose the connection. In case of a crash, just reset your website with your backup solution and try again. &lt;/p&gt;

&lt;h2&gt;
  
  
  A stable WordPress installation
&lt;/h2&gt;

&lt;p&gt;When you use those tool, you have a stable and fast basic installation. With Elementor, it is easy to build pages without deep programming knowledge. Of course, there are many interesting aspects of developing websites with WordPress, that I did not cover in this post. But it is a great start and prevents you from running into many potential problems. &lt;/p&gt;

&lt;p&gt;If you found this post helpful, please feel free to leave a comment and share your thoughts. I would also love to hear your recommendations for any plugins you consider essential for a basic setup.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>wordpress</category>
    </item>
  </channel>
</rss>
