https://kiko.io/kiko.io PostsMemorable (Tech) Stuffhttps://kiko.io/images/favicon-32x32.pnghttps://kiko.io/images/icon-72x72.png2024-02-24T14:36:07.000ZKristof Zerbekristof.zerbe@gmail.comHexohttps://kiko.io/post/SVWW-vs-Paderborn-2024-02-23/SVWW vs. Paderborn @ 2024-02-232024-02-24T14:36:07.000Z<p><img src="https://kiko.io/images/social-media/SVWW-vs-Paderborn-2024-02-23.png" /></p>
<div class="float-matchgrid">
<img src="/images/logos/SV-Wehen-Wiesbaden.svg" />
<span>1:2</span>
<img src="/images/logos/SC-Paderborn.svg" />
</div>
<p>Matchday … and another evening game and this time against <strong>SC Paderborn</strong>, who had to endure quite a debacle (4:0) against Kiel last week, but are still knocking at the top of the table. </p>
<p>This time I also got some extra tickets for friends and my wife, because one of them is from Paderborn and has been following his team for years and had no problem travelling 3 hours there and 3 hours back just for one game. My wife comes from the Sauerland region and Paderborn is not far away, so she felt a bit of sympathy for the SC, even though she now lives in Wiesbaden. The other two are actually Eintracht fans and know the Waldstadion with its 58,000 seats and wanted to see the cosy atmosphere of the Brita-Arena.</p>
<p>The meeting point was 90 minutes before kick-off at the railway station. There was enough time to delay the boys’ arrival, take a short walk to the stadium (about a kilometre) and enjoy a stadium sausage or two. My wife and I had decided to take the bus for the sake of convenience. We were on time and waiting for the others when I realised that instead of my smartphone, I had my wife’s almost identical one in my pocket. F***! How were the others supposed to reach me if they were late or something else was wrong? I don’t know how we survived without these mobile gadgets at the end of the nineties!? I felt amputated, blind and deaf. ME, without my smartphone … that never happened before!</p>
<span id="more"></span>
<p>OK … we had agreed an exact meeting point, but only one turned up. No sign of the other two. 15 minutes later, the second one showed up and said he had tried to reach me, but I hadn’t answered the phone. Well, how could he? The phone was on the charger at home. But there was no sign of the last one. One of the others then managed to connect with him via Slack and it turned out that he had missed his second train because the first one was late. He asked why I wasn’t answering the phone! </p>
<p>Finally, after 40 minutes, we were all together and decided to take at least 1 taxi, as two of the boys were not good on foot. But there were five of us, so my wife said the two of us could walk. Ok, then. 10 minutes later, all five of us were standing in the queue in front of the stadium entrance when I realised that I had printed out the tickets for the other four, but not my season ticket, because it was stored digitally on … my smartphone! F***!!</p>
<p>Of course there is NO taxi in front of the stadium before the start of the game, but only afterwards, and OF COURSE there wasn’t one in sight that was unloading guests and so I had to run like a madman back to the train station to reveal my distress to a very friendly and football-interested taxi driver. 6pm and only 30 minutes to drive home through the city, grab my filthy phone and get back to the stadium.</p>
<p>What can I say … not only was the guy okay, but he also dropped me off outside the stadium at exactly 6.25pm and I took my seat just as the SVWW kick-off music finished and the teams entered the pitch. I was done, but happy. A quick greeting to Bärbel and my friends 3 rows behind me and the game began… </p>
<hr>
<h2 id="The-Game"><a href="#The-Game" class="headerlink" title="The Game"></a>The Game</h2><p>The game was rather unspectacular, although we had lost away at Schalke last week and Paderborn, as I said, had to take a real beating against Kiel, so both teams actually had to win. We started in front of only 6.300 spectators with a lot of pressure, but after 5 minutes the attack slowed down a bit because we made a lot of mistakes in defence and invited our opponents to play their game. You could clearly see the difference in ability between the two teams. Paderborn’s players were much more confident on the ball and the fans in the stands gave free rein to their displeasure. They simply played carelessly.</p>
<p>Wiesbaden repeatedly managed to shift the game into the opponent’s half through high pressing, but individual mistakes meant that the opponents were always very quick in front of our goal. Two or three precise passes and our goalkeeper <strong>Stritzel</strong> had work to do. It was all the more astonishing that a really good and fast ball relay led to <strong>1:0 for SVWW</strong> in the 31st minute. <strong>Franco Kovacevic</strong> …Yessss!</p>
<div class="image-masonry" id="image-masonry-21qk6e">
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Paderborn-2024-02-23/PXL_20240223_172728706.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Paderborn-2024-02-23/PXL_20240223_172831224.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Paderborn-2024-02-23/PXL_20240223_173004504.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Paderborn-2024-02-23/PXL_20240223_173707259.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Paderborn-2024-02-23/PXL_20240223_173739076.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Paderborn-2024-02-23/PXL_20240223_180009994.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Paderborn-2024-02-23/PXL_20240223_181550201.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Paderborn-2024-02-23/PXL_20240223_181602559.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Paderborn-2024-02-23/PXL_20240223_183658110.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Paderborn-2024-02-23/PXL_20240223_184642163.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Paderborn-2024-02-23/PXL_20240223_192405590.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Paderborn-2024-02-23/PXL_20240223_192625866.jpg" alt="" />
</div>
</div>
<p>But the pattern remained. A sprint and a one-two pass from Paderborn and our defence was outplayed. It was far too easy and so it came as it had to: <strong>1:1</strong> just 8 minutes later .. :|</p>
<p>Unfortunately, the picture hadn’t really changed after half-time and we were simply lucky that a ball from the visitors hit the crossbar in the 47th minute and another only the post in the 51st.</p>
<p>The coach decided to use his entire contingent of substitutes and so <strong>Goppel</strong> and <strong>Lee</strong> came on in the 59th minute, <strong>Agrafiotis</strong> and <strong>Iredale</strong> in the 74th and last but not least <strong>Rieble</strong> in the 83rd minute. But even if that helped a little, Paderborn scored the winning goal in the 83rd minute to make it <strong>1:2</strong>.</p>
<p>After that it was a game to one goal, because Wiesbaden tried everything to at least equalise and they fought really passionately, but just a little too late and too imprecise.</p>
<p>⇾ <a href="https://www.kicker.de/wiesbaden-gegen-paderborn-2024-bundesliga-4861867/spielbericht">Match report on kicker.de</a></p>
<hr>
<h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>Even though our team only played a good game from time to time over the entire 90 minutes, it was noticeable that the referee’s performance was even worse. As soon as a Paderborn player fell to the ground, a free-kick was whistled, but if one of our players was chopped down, he allowed the game to go on. He also didn’t seem to care if the Paderborn players used their hands in the penalty area or if a particularly hot-headed player from the visitors got violent, in which case both players received a yellow card.</p>
<p>The only good thing about this match day was that the teams in the basement of the league couldn’t win either and I have something to talk about because of my odyssey before the match.</p>
<a href="https://www.kicker.de/2-bundesliga/tabelle">
<img loading="lazy" src="tabelle-24-02-24.png" alt="Table 2. Liga, Sunday 24. February 2024" />
</a>
https://kiko.io/post/Discoveries-28-UI-Components/Discoveries #28 - UI Components2024-02-23T13:14:10.000Z<p><img src="https://kiko.io/images/social-media/Discoveries-28-UI-Components.png" /></p><p>UI is the first building block for UX. Build a user interface that is easy to understand and use and your users will have a good experience and enjoy your website or web app.</p>
<p>In this new edition of Discoveries (after three long months without), we look at UI elements that are worth a second look. Some of these I'm pretty sure I'll incorporate into one of my existing projects and others I'll find an opportunity for in the future.</p>
<ul class="anchorlist">
<li data-anchor="#syntax-highlighting-code-blocks-with-prism-and-the-custom-highlight-api">Syntax highlighting code blocks with Prism and the Custom Highlight API</li><li data-anchor="#syntax-js">Syntax.js</li><li data-anchor="#replace-javascript-dialogs-with-new-html-dialog">Replace JavaScript Dialogs With New HTML Dialog</li><li data-anchor="#build-an-off-canvas-menu-with-dialog-and-web-components">Build an off-canvas menu with <dialog> and web components</li><li data-anchor="#windowise">Windowise</li><li data-anchor="#accordion-slider-responsive-and-touch-enabled-accordion">Accordion Slider - Responsive and Touch-enabled accordion</li><li data-anchor="#floatype-js">floatype.js</li><li data-anchor="#canvas-datagrid">canvas-datagrid</li><li data-anchor="#treedata-js">TreeData.js</li><li data-anchor="#grid-overflow">Grid-Overflow</li>
</ul>
<span id="more"></span>
<hr id="syntax-highlighting-code-blocks-with-prism-and-the-custom-highlight-api"/>
<h2 style="margin-bottom: 10px;">
Syntax highlighting code blocks with Prism and the Custom Highlight API
</h2>
<small>by Bramus <br><a href="https://codepen.io/bramus/pen/VwRqGVo">https://codepen.io/bramus/pen/VwRqGVo</a></small>
<p>"Show me the code or it doesn't happen". Every tech-related blogger has to have to show code once in a while. In HTML there is the CODE tag for this, but hardly anyone can do without syntax highlighting. The best-known libraries for this are <a href="https://highlightjs.org/">highlight.js</a> and <a href="https://prismjs.com/">Prism.js</a>. Bramus shows us how the latter will work with the new Custom Highlight API (W3C Draft).</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://codepen.io/bramus/pen/VwRqGVo">
<img class="limit" src="syntax-highlighting-code-blocks-with-prism-and-the-custom-highlight-api.png" alt="Syntax highlighting code blocks with Prism and the Custom Highlight API" />
</a>
<hr id="syntax-js"/>
<h2 style="margin-bottom: 10px;">
Syntax.js
</h2>
<small>by William Troup <br><a href="https://github.com/williamtroup/Syntax.js">https://github.com/williamtroup/Syntax.js</a></small>
<p>Speaking of highlighting … William has set out to develop an alternative to the top dogs with Syntax.js. Under an MIT licence, without dependencies and fresh UI ideas.</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://github.com/williamtroup/Syntax.js">
<img class="limit" src="syntax-js.png" alt="Syntax.js" />
</a>
<hr id="replace-javascript-dialogs-with-new-html-dialog"/>
<h2 style="margin-bottom: 10px;">
Replace JavaScript Dialogs With New HTML Dialog
</h2>
<small>by Mads Stoumann <br><a href="https://css-tricks.com/replace-javascript-dialogs-html-dialog-element/">https://css-tricks.com/replace-javascript-dialogs-html-dialog-element/</a></small>
<p>The relatively new HTML tag DIALOG offers developers more possibilities than you might think, because it goes far beyond the alert() or prompt() approach. In this article, Mads explains the basics using examples.</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://css-tricks.com/replace-javascript-dialogs-html-dialog-element/">
<img class="limit" src="replace-javascript-dialogs-with-new-html-dialog.png" alt="Replace JavaScript Dialogs With New HTML Dialog" />
</a>
<hr id="build-an-off-canvas-menu-with-dialog-and-web-components"/>
<h2 style="margin-bottom: 10px;">
Build an off-canvas menu with <dialog> and web components
</h2>
<small>by Mark Conroy <br><a href="https://blog.logrocket.com/build-off-canvas-menu-web-components/">https://blog.logrocket.com/build-off-canvas-menu-web-components/</a></small>
<p>Mark goes one step further with the DIALOG element and shows how you can use it to build an attractive off-canvas menu … and I already know exactly where I will use this approach ;)</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://blog.logrocket.com/build-off-canvas-menu-web-components/">
<img class="limit" src="build-an-off-canvas-menu-with-dialog-and-web-components.png" alt="Build an off-canvas menu with <dialog> and web components" />
</a>
<hr id="windowise"/>
<h2 style="margin-bottom: 10px;">
Windowise
</h2>
<small>by Gao Sun <br><a href="https://gao-sun.github.io/windowise/">https://gao-sun.github.io/windowise/</a></small>
<p>More than 7 years ago, Gao presented Windowise, a library that uses all kinds of HTML tags to offer the user interesting and effective interaction options. The name of the project says it all.</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://gao-sun.github.io/windowise/">
<img class="limit" src="windowise.png" alt="Windowise" />
</a>
<hr id="accordion-slider-responsive-and-touch-enabled-accordion"/>
<h2 style="margin-bottom: 10px;">
Accordion Slider - Responsive and Touch-enabled accordion
</h2>
<small>by David Ghiurău (bqworks) <br><a href="https://bqworks.net/accordion-slider/">https://bqworks.net/accordion-slider/</a></small>
<p>Presenting images in an interesting way is always a challenge and many slideshow libraries look very similar. David's Accordion Slider (especially example 1) is a real exception and works on all device classes, which is not a matter of course.</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://bqworks.net/accordion-slider/">
<img class="limit" src="accordion-slider-responsive-and-touch-enabled-accordion.png" alt="Accordion Slider - Responsive and Touch-enabled accordion" />
</a>
<hr id="floatype-js"/>
<h2 style="margin-bottom: 10px;">
floatype.js
</h2>
<small>by Kailash Nadh <br><a href="https://github.com/knadh/floatype.js">https://github.com/knadh/floatype.js</a></small>
<p>We all know autocomplete drop downs from our favourite IDE, whatever it is called. Kailash has something similar for every normal textarea.</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://github.com/knadh/floatype.js">
<img class="limit" src="floatype-js.png" alt="floatype.js" />
</a>
<hr id="canvas-datagrid"/>
<h2 style="margin-bottom: 10px;">
canvas-datagrid
</h2>
<small>by Tony Germaneri <br><a href="https://github.com/TonyGermaneri/canvas-datagrid">https://github.com/TonyGermaneri/canvas-datagrid</a></small>
<p>This is a Canvas based data grid web component, which looks and feels like an early Excel or other spreadsheet. Let your user load JSON, edit cells, sort columns and so on. Cool and useful.</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://github.com/TonyGermaneri/canvas-datagrid">
<img class="limit" src="canvas-datagrid.png" alt="canvas-datagrid" />
</a>
<hr id="treedata-js"/>
<h2 style="margin-bottom: 10px;">
TreeData.js
</h2>
<small>by Raphael Amorim <br><a href="https://raphamorim.io/treeData.js/">https://raphamorim.io/treeData.js/</a></small>
<p>Not only genealogists like to visualise connections as a tree. Such a tree is quickly created using a Value/Parent object and Raphael's solution.</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://raphamorim.io/treeData.js/">
<img class="limit" src="treedata-js.png" alt="TreeData.js" />
</a>
<hr id="grid-overflow"/>
<h2 style="margin-bottom: 10px;">
Grid-Overflow
</h2>
<small>by Roman Flössler <br><a href="https://github.com/Roman-Flossler/Grid-Overflow">https://github.com/Roman-Flossler/Grid-Overflow</a></small>
<p>This is a pure CSS solution for masonry layout and grid layout, where grid items can be given vertigo, panorama or VIP class to overflow into the next cell.</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://github.com/Roman-Flossler/Grid-Overflow">
<img class="limit" src="grid-overflow.png" alt="Grid-Overflow" />
</a>
https://kiko.io/post/Manipulation-of-Lists-within-a-Sentence-of-Natural-Language/Manipulation of Lists within a Sentence of Natural Language2024-02-20T18:30:21.000Z<p><img src="https://kiko.io/images/social-media/Manipulation-of-Lists-within-a-Sentence-of-Natural-Language.png" /></p><p>The <a href="/photos"><strong>photo section</strong></a> on my blog, the main page of which I have now named COLLECTION, initially only served as an overview of the available or already used hero images for the various pages of the blog, such as posts and others. In the meantime, detailed pages for the individual photos and a world map have been added, in order to follow the IndieWeb idea that I want to publish my photos on my own site with all the information first and then syndicate them on other platforms.</p>
<p>At some point I implemented three different view modes for the photos, more out of a desire to play around than out of necessity: <strong>Grid</strong>, <strong>List</strong> and lastly <strong>Masonry</strong>. Depending on the device class, one mode or the other has its advantages in terms of overview.</p>
<p><img src="/post/Manipulation-of-Lists-within-a-Sentence-of-Natural-Language/kiko-photos-old.png" alt="Mode Switch Controls"></p>
<p>What was still missing, however, was the option to filter and/or sort the amount of photos according to their use, as over time there are more rather than fewer and it becomes increasingly difficult to find the image you are looking for. I had already placed 3 icon buttons at the top right-hand corner for the various view modes and was faced with the decision of how to implement further control elements such as drop-down lists or similar. But I didn’t want the interface to look like “Your Company’s App” at some point, plastered with controls that distract the eye from the photos. Ideas such as folding and unfolding control panels, i.e. hiding the functionality, were also out of the question at second glance. That would only have shifted the problem.</p>
<span id="more"></span>
<hr>
<h2 id="Why-not-a-sentence-in-natural-language"><a href="#Why-not-a-sentence-in-natural-language" class="headerlink" title="Why not a sentence in natural language?"></a>Why not a sentence in natural language?</h2><p>After thinking about it for a while, I realised that I had converted the display of the number of photos as a numeral with text at the top left-hand corner, for instance “289 photos”. <strong>Why not take this idea one step further and make a complete sentence out of it</strong> … like</p>
<blockquote>
<p>289 photos overall displayed as list sorted by date created descending</p>
</blockquote>
<p>or, now with the important information marked …</p>
<blockquote>
<p><u>127</u> photos <u>unused</u> displayed as <u>grid</u> sorted by <u style="white-space: nowrap;">date created</u> <u>ascending</u></p>
</blockquote>
<p>or …</p>
<blockquote>
<p><u>139</u> photos <u>used</u> <u style="white-space: nowrap;">for posts</u> displayed as <u>masonry</u> sorted by <u style="white-space: nowrap;">file name</u> <u>ascending</u></p>
</blockquote>
<p>Although this sentence is not grammatically correct, it contains all the information that describes the list and that could be adapted dynamically. If you break it down into its components, you get placeholders that can be filled variably:</p>
<pre><code class="highlight txt">[COUNT] photos [FILTER-STATUS] [FILTER-TYPE] displayed as [PRESENTATION] and sorted by [SORT-FIELD] [SORT-ORDER]</code></pre>
<p>These placeholders could now not only be used to place a variable value, but some could also be used as direct trigger points for changing the view.</p>
<p>Here a list of all the necessary placeholders with the values they should be able to assume:</p>
<ul>
<li><p><strong>COUNT</strong> - display only<br>Number of filtered photos</p>
</li>
<li><p><strong>FILTER-STATUS</strong> - trigger<br>Possible values <code>overall</code>, <code>unused</code> and <code>used</code></p>
</li>
<li><p><strong>FILTER-TYPE</strong> - trigger<br><em>(only necessary when FILTER-STATUS is set to <code>used</code>)</em><br>Possible values: <code>overall</code>, <code>for posts</code>, <code>for drafts</code>, <code>for notes</code>, <code>for default pages</code>, <code>for dynamic pages</code>, <code>for anything pages</code> or <code>at start page</code></p>
</li>
<li><p><strong>PRESENTATION</strong> - trigger<br>Possible values: <code>list</code>, <code>grid</code> or <code>masonry</code></p>
</li>
<li><p><strong>SORT-FIELD</strong> - trigger<br>Possible values: <code>file name</code>, <code>title</code> or <code>date created</code></p>
</li>
<li><p><strong>SORT-ORDER</strong> - trigger<br>Possible values: <code>ascending</code> or <code>descending</code></p>
</li>
</ul>
<p>To make clear that some of the placeholder elements in this sentence are not just for display, but also have a function, I have given them the <code>MARK</code> tag, which has the nice side effect that the browser takes care of the colour highlighting without any intervention.</p>
<p>These thoughts result in the following HTML framework for the sentence, within a wrapper named <code>view-bar</code>:</p>
<pre><code class="highlight html"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"view-bar"</span>></span>
<span class="tag"><<span class="name">span</span> <span class="attr">id</span>=<span class="string">"view-count"</span>></span>[COUNT]<span class="tag"></<span class="name">span</span>></span>
<span class="tag"><<span class="name">span</span>></span>photos<span class="tag"></<span class="name">span</span>></span>
<span class="tag"><<span class="name">span</span> <span class="attr">id</span>=<span class="string">"view-filter-status"</span>></span>
<span class="tag"><<span class="name">mark</span>></span>[FILTER-STATUS]<span class="tag"></<span class="name">mark</span>></span>
<span class="tag"></<span class="name">span</span>></span>
<span class="tag"><<span class="name">span</span> <span class="attr">id</span>=<span class="string">"view-filter-type"</span>></span>
<span class="tag"><<span class="name">mark</span>></span>[FILTER-TYPE]<span class="tag"></<span class="name">mark</span>></span>
<span class="tag"></<span class="name">span</span>></span>
<span class="tag"><<span class="name">span</span>></span>displayed as<span class="tag"></<span class="name">span</span>></span>
<span class="tag"><<span class="name">span</span> <span class="attr">id</span>=<span class="string">"view-presentation"</span>></span>
<span class="tag"><<span class="name">mark</span>></span>[PRESENTATION]<span class="tag"></<span class="name">mark</span>></span>
<span class="tag"></<span class="name">span</span>></span>
<span class="tag"><<span class="name">span</span>></span>sorted by<span class="tag"></<span class="name">span</span>></span>
<span class="tag"><<span class="name">span</span> <span class="attr">id</span>=<span class="string">"view-sort-field"</span>></span>
<span class="tag"><<span class="name">mark</span>></span>[SORT-FIELD]<span class="tag"></<span class="name">mark</span>></span>
<span class="tag"></<span class="name">span</span>></span>
<span class="tag"><<span class="name">span</span> <span class="attr">id</span>=<span class="string">"view-sort-order"</span>></span>
<span class="tag"><<span class="name">mark</span>></span>[SORT-ORDER]<span class="tag"></<span class="name">mark</span>></span>
<span class="tag"></<span class="name">span</span>></span>
<span class="tag"></<span class="name">div</span>></span></code></pre>
<p><strong>Example:</strong></p>
<p>
<div class="view-bar">
<span id="view-count">[COUNT]</span>
<span>photos</span>
<span id="view-filter-status">
<mark>[FILTER-STATUS]</mark>
</span>
<span id="view-filter-type">
<mark>[FILTER-TYPE]</mark>
</span>
<span>displayed as</span>
<span id="view-presentation">
<mark>[PRESENTATION]</mark>
</span>
<span>sorted by</span>
<span id="view-sort-field">
<mark>[SORT-FIELD]</mark>
</span>
<span id="view-sort-order">
<mark>[SORT-ORDER]</mark>
</span>
</div>
</p>
<hr>
<h2 id="How-to-implement-the-triggers"><a href="#How-to-implement-the-triggers" class="headerlink" title="How to implement the triggers?"></a>How to implement the triggers?</h2><p>The standard control element for selecting from two values in HTML is the checkbox … ON and OFF, and a matching text label. If there are more, radio buttons or drop-down lists are usually used in forms. But it should not look like a form, so it was out of the question to display one of the above-mentioned control elements in the sentence when clicking or tapping on one of the trigger points. </p>
<p>It felt most natural to me if the user simply clicked or tapped on the selected placeholder and the next possible value would be selected, displayed and the view of the photos immediately changed accordingly. In other words, a <strong>multi-toggle element</strong>.</p>
<p>It was clear to me that the user would have to interact with elements with more than two options several times to achieve the desired result, but on the one hand this made up for the simplicity of the interaction and on the other hand the selected value should be saved in the browser so that, for example, the favoured presentation could be directly preset when the page is loaded. </p>
<p>Furthermore, simple toggles in HTML and CSS could also be implemented without JavaScript, but this would not work for multi-Toogle elements with a defined sequence, but I needed JS to save the current value anyway.</p>
<hr>
<h2 id="First-Trigger-Presentation"><a href="#First-Trigger-Presentation" class="headerlink" title="First Trigger: Presentation"></a>First Trigger: Presentation</h2><p>The following HTML code shows the implementation of the first trigger for the presentation mode, which enables the user to switch between three options: <code>list</code>, <code>grid</code> and <code>masonry</code>.</p>
<pre><code class="highlight html"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"view-bar"</span>></span>
...
<span class="tag"><<span class="name">span</span>></span>displayed as<span class="tag"></<span class="name">span</span>></span>
<span class="tag"><<span class="name">span</span> <span class="attr">id</span>=<span class="string">"view-presentation"</span>></span>
<span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"radio"</span> <span class="attr">checked</span></span>
<span class="tag"> <span class="attr">name</span>=<span class="string">"view-presentation"</span> </span>
<span class="tag"> <span class="attr">id</span>=<span class="string">"presentation-grid"</span> </span>
<span class="tag"> <span class="attr">data-presentation</span>=<span class="string">"grid"</span></span>
<span class="tag"> <span class="attr">data-next-presentation</span>=<span class="string">"list"</span>></span>
<span class="tag"><<span class="name">label</span> <span class="attr">for</span>=<span class="string">"presentation-grid"</span> </span>
<span class="tag"> <span class="attr">title</span>=<span class="string">"Display as list"</span>></span>
<span class="tag"><<span class="name">mark</span>></span>grid<span class="tag"></<span class="name">mark</span>></span>
<span class="tag"></<span class="name">label</span>></span>
<span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"radio"</span> </span>
<span class="tag"> <span class="attr">name</span>=<span class="string">"view-presentation"</span> </span>
<span class="tag"> <span class="attr">id</span>=<span class="string">"presentation-list"</span></span>
<span class="tag"> <span class="attr">data-presentation</span>=<span class="string">"list"</span></span>
<span class="tag"> <span class="attr">data-next-presentation</span>=<span class="string">"masonry"</span>></span>
<span class="tag"><<span class="name">label</span> <span class="attr">for</span>=<span class="string">"presentation-list"</span> </span>
<span class="tag"> <span class="attr">title</span>=<span class="string">"Display as masonry"</span>></span>
<span class="tag"><<span class="name">mark</span>></span>list<span class="tag"></<span class="name">mark</span>></span>
<span class="tag"></<span class="name">label</span>></span>
<span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"radio"</span> </span>
<span class="tag"> <span class="attr">name</span>=<span class="string">"view-presentation"</span> </span>
<span class="tag"> <span class="attr">id</span>=<span class="string">"presentation-masonry"</span></span>
<span class="tag"> <span class="attr">data-presentation</span>=<span class="string">"masonry"</span></span>
<span class="tag"> <span class="attr">data-next-presentation</span>=<span class="string">"grid"</span>></span>
<span class="tag"><<span class="name">label</span> <span class="attr">for</span>=<span class="string">"presentation-masonry"</span> </span>
<span class="tag"> <span class="attr">title</span>=<span class="string">"Display as grid"</span>></span>
<span class="tag"><<span class="name">mark</span>></span>masonry<span class="tag"></<span class="name">mark</span>></span>
<span class="tag"></<span class="name">label</span>></span>
<span class="tag"></<span class="name">span</span>></span>
...
<span class="tag"></<span class="name">div</span>></span></code></pre>
<p>The set value is held by a radio element and a toggle group is formed for all radio elements with the same value for the <code>name</code> attribute. The <code>data</code> attributes define the current and next value, which is later set via JavaScript by clicking or tapping. The label element linked to the radio holds the text to be displayed in the mark element.</p>
<p>The following CSS code ensures that only the actual text is visible and all auxiliary elements are hidden.</p>
<pre><code class="highlight css"><span class="selector-class">.view-bar</span> <span class="selector-tag">input</span><span class="selector-attr">[type=radio]</span> {
<span class="attribute">display</span>: none;
}
<span class="selector-class">.view-bar</span> <span class="selector-tag">input</span><span class="selector-attr">[name=<span class="string">"view-presentation"</span>]</span> + <span class="selector-tag">label</span> {
<span class="attribute">cursor</span>: pointer;
<span class="attribute">display</span>: none;
}
<span class="selector-class">.view-bar</span> <span class="selector-tag">input</span><span class="selector-attr">[name=<span class="string">"view-presentation"</span>]</span><span class="selector-pseudo">:checked</span> + <span class="selector-tag">label</span> {
<span class="attribute">display</span>: inline-block;
}</code></pre>
<p>In the HTML, one of the radio elements of a group is provided with the <code>checked</code> attribute in order to have a necessary value when loading for the first time. In the script, the selection is later on saved in a cookie, which is read again when the page is reloaded and saved in a global variable called <code>currentPresentation</code>. The trigger for this selection is also bound to the respective click events in the script, whereby the next value to be set is taken from the NEXT data attribute defined in the HTML for the respective element, in this case ``data-next-presentation```.</p>
<pre><code class="highlight js"><script>
<span class="comment">// helper</span>
<span class="keyword">function</span> <span class="title function_">getCookie</span>(<span class="params">name</span>) {
<span class="keyword">var</span> v = <span class="variable language_">document</span>.<span class="property">cookie</span>.<span class="title function_">match</span>(<span class="string">'(^|;) ?'</span> + name + <span class="string">'=([^;]*)(;|$)'</span>);
<span class="keyword">return</span> v ? v[<span class="number">2</span>] : <span class="literal">null</span>;
}
<span class="keyword">function</span> <span class="title function_">setCookie</span>(<span class="params">name, value, days</span>) {
<span class="keyword">var</span> d = <span class="keyword">new</span> <span class="title class_">Date</span>;
d.<span class="title function_">setTime</span>(d.<span class="title function_">getTime</span>() + <span class="number">24</span>*<span class="number">60</span>*<span class="number">60</span>*<span class="number">1000</span>*days);
<span class="variable language_">document</span>.<span class="property">cookie</span> = name + <span class="string">"="</span> + value + <span class="string">";path=/;expires="</span> + d.<span class="title function_">toGMTString</span>();
}
...
<span class="comment">// preset last value of PRESENTATION from cookie or take the default checked radio from DOM</span>
<span class="keyword">let</span> currentPresentation = <span class="title function_">getCookie</span>(<span class="string">"photos-presentation"</span>);
<span class="keyword">if</span> (!currentPresentation) {
currentPresentation =
<span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">"input[name=view-presentation]:checked"</span>).<span class="title function_">getAttribute</span>(<span class="string">"data-presentation"</span>);
}
<span class="comment">// Bind click event to PRESENTATION radios</span>
<span class="variable language_">document</span>.<span class="title function_">querySelectorAll</span>(<span class="string">"input[name=view-presentation]"</span>).<span class="title function_">forEach</span>(<span class="function"><span class="params">element</span> =></span> {
element.<span class="title function_">addEventListener</span>(<span class="string">"click"</span>, <span class="function">(<span class="params">event</span>) =></span> {
<span class="title function_">setPresentation</span>(element);
});
});
<span class="comment">// method for switching PRESENTATION mode</span>
<span class="keyword">function</span> <span class="title function_">setPresentation</span>(<span class="params">ePresentation</span>) {
currentPresentation = ePresentation.<span class="title function_">getAttribute</span>(<span class="string">"data-next-presentation"</span>);
<span class="title function_">setCookie</span>(<span class="string">"photos-presentation"</span>, currentPresentation, <span class="number">365</span>);
<span class="title function_">setView</span>();
}
<span class="comment">// general method for setting view</span>
<span class="keyword">function</span> <span class="title function_">setView</span>(<span class="params"></span>) {
<span class="comment">//... code for changing the view</span>
<span class="comment">// show current PRESENTATION</span>
<span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">"#presentation-"</span> + currentPresentation).<span class="property">checked</span> = <span class="literal">true</span>;
}
<span class="comment">// initial call on load to set view</span>
<span class="title function_">setView</span>();
</script></code></pre>
<p>The <code>setView</code> method is a global function that is called each time one of the triggers involved changes, as in the presentation mode example. Only this function actually changes the view and must therefore also be called when the page is loaded after the triggers have been set and the cookies have been read.</p>
<p>The following codepen interactively shows how this first example with the same code works. I have not included the implementation of the photo list here, as it does not play an important role here.</p>
<iframe height="360"
id="codepen-vYPzNyq"
class="codepen"
src="https://codepen.io/kristofzerbe/embed/vYPzNyq?height=360&default-tab=result&theme-id=dark"
style="width: 100%;"
scrolling="no"
title="Codepen: Example, Part 1"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true">
</iframe>
<p> </p>
<hr>
<h2 id="Further-Triggers-Sort-Field-and-Sort-Order"><a href="#Further-Triggers-Sort-Field-and-Sort-Order" class="headerlink" title="Further Triggers: Sort Field and Sort Order"></a>Further Triggers: Sort Field and Sort Order</h2><p>The two other placeholders <strong>SORT-FIELD</strong> and <strong>SORT-ORDER</strong> work in a similar way, so I will omit the illustration of the additional HTML, CSS and JS code here and only provide the extended CodePen. The difference is here that both values are handled in one global variable called <code>currentSorting</code>, because they are mutually dependent, in the sense of: <em>Tell me which field I should sort in which direction</em>.</p>
<iframe height="360"
id="codepen-YzggopG"
class="codepen"
src="https://codepen.io/kristofzerbe/embed/YzggopG?height=360&default-tab=result&theme-id=dark"
style="width: 100%;"
scrolling="no"
title="Codepen: Example, Part 2"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true">
</iframe>
<p> </p>
<hr>
<h2 id="Final-Triggers-Filter-Status-and-Filter-Type"><a href="#Final-Triggers-Filter-Status-and-Filter-Type" class="headerlink" title="Final Triggers: Filter Status and Filter Type"></a>Final Triggers: Filter Status and Filter Type</h2><p>Last but not least, <strong>FILTER-STATUS</strong> and <strong>FILTER-TYPE</strong> are not only dependent on each other, but are also nested, as FILTER-TYPE is only required if FILTER-STATUS is set to <code>USED</code>, because it should only be possible to differentiate between the types of used photos in this status.</p>
<p>Another difference between these two and the others is that only these triggers manipulate the length of the list and therefore the COUNT … and the selected value is not stored in a cookie for later use, but the default is always OVERALL.</p>
<p>The HTML and CSS code to be added in this last step is structured similarly to that for the previous triggers and can be viewed in the final CodePen:</p>
<iframe height="360"
id="codepen-oNVOpgW"
class="codepen"
src="https://codepen.io/kristofzerbe/embed/oNVOpgW?height=360&default-tab=result&theme-id=dark"
style="width: 100%;"
scrolling="no"
title="Codepen: Example, Final Part"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true">
</iframe>
<p>The additional JavaScript code is somewhat more interesting and I will only show the added code here in the listing, otherwise it will be too long:</p>
<pre><code class="highlight js"><script>
...
<span class="comment">// preset value for STATUS and TYPE from default checked radio</span>
<span class="keyword">let</span> currentStatus =
<span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">"input[name=view-filter-status]:checked"</span>).<span class="title function_">getAttribute</span>(<span class="string">"data-status"</span>);
<span class="keyword">let</span> currentType =
<span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">"input[name=view-filter-type]:checked"</span>).<span class="title function_">getAttribute</span>(<span class="string">"data-type"</span>);
...
<span class="comment">// Bind click event to STATUS and TYPE radios</span>
<span class="variable language_">document</span>.<span class="title function_">querySelectorAll</span>(<span class="string">"input[name=view-filter-status]"</span>).<span class="title function_">forEach</span>(<span class="function"><span class="params">element</span> =></span> {
element.<span class="title function_">addEventListener</span>(<span class="string">"click"</span>, <span class="function">(<span class="params">event</span>) =></span> {
<span class="title function_">setFilterStatus</span>(element);
});
});
<span class="variable language_">document</span>.<span class="title function_">querySelectorAll</span>(<span class="string">"input[name=view-filter-type]"</span>).<span class="title function_">forEach</span>(<span class="function"><span class="params">element</span> =></span> {
element.<span class="title function_">addEventListener</span>(<span class="string">"click"</span>, <span class="function">(<span class="params">event</span>) =></span> {
<span class="title function_">setFilterType</span>(element);
});
});
...
<span class="comment">// method for switching FILTER STATUS</span>
<span class="keyword">function</span> <span class="title function_">setFilterStatus</span>(<span class="params">eStatus</span>) {
currentStatus = eStatus.<span class="title function_">getAttribute</span>(<span class="string">"data-next-status"</span>);
<span class="keyword">if</span> (currentStatus === <span class="string">"all"</span>) { <span class="comment">// reset TYPE</span>
currentType = <span class="string">"all"</span>;
}
<span class="title function_">setView</span>();
}
<span class="comment">// method for switching FILTER TYPE</span>
<span class="keyword">function</span> <span class="title function_">setFilterType</span>(<span class="params">eType</span>) {
currentType = eType.<span class="title function_">getAttribute</span>(<span class="string">"data-next-type"</span>);
<span class="title function_">setView</span>();
}
<span class="keyword">function</span> <span class="title function_">setView</span>(<span class="params"></span>) {
<span class="comment">//... code for filtering list and changing view</span>
<span class="comment">// show current STATUS + TYPE</span>
<span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">"#filter-status-"</span> + currentStatus).<span class="property">checked</span> = <span class="literal">true</span>;
<span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">"#filter-type-"</span> + currentType).<span class="property">checked</span> = <span class="literal">true</span>;
<span class="comment">// show or hide the TYPE trigger</span>
<span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">"view-filter-type"</span>)
.<span class="property">style</span>.<span class="property">display</span> = (currentStatus === <span class="string">"used"</span>) ? <span class="string">"inline-block"</span> : <span class="string">"none"</span>;
...
}
</script></code></pre>
<p>In addition to the initialisation of the global variables and the click bindings, including the new <code>set...</code> methods for them, the <code>setView()</code> method has been extended to display the applied filter and to show or hide the TYPE trigger, which may or may not be necessary depending on the STATUS. In addition, the implementation of the real filter and the fresh of the COUNT must now also be integrated here.</p>
<hr>
<h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>It’s a lot of code and I had to refactor it a few times, but I find the result easy to use and visually convincing. However, I’m still not completely satisfied because the UX is suboptimal, especially when clicking through the many FILTER TYPES. Showing a dropdown with all the options would be faster and more pleasant. On my <a href="/tools/tiny-tools">tools</a> page, I have built <a href="https://github.com/LCweb-ita/LC-select">LC-select</a> into a trigger, because a multi-selection is necessary there, but the result doesn’t really convince me yet and I have to revise it again.</p>
<p>I briefly thought about wrapping the whole thing in a configurable script, as a plugin or even as a web component, but decided against it, because I have everything I need with HTML, CSS and JS and it is easy to maintain and extend. An additional abstraction layer would not make it better, but rather more complicated and less transparent. It would be like so often: The attempt to make the use of a feature easier for yourself and others is paid for at some point with complexity that can hardly be resolved and hours of troubleshooting not on the actual feature but on the tools required for it.</p>
<p>Happy Coding … :)</p>
https://kiko.io/post/WIRTZ-DNA-Tour-2024-Leipzig-2024-02-17/WIRTZ DNA-Tour 2024, Leipzig @ 2024-02-172024-02-18T16:31:36.000Z<p><img src="https://kiko.io/images/social-media/WIRTZ-DNA-Tour-2024-Leipzig-2024-02-17.png" /></p><p>To anticipate: I’m a fucking groupie when it comes to seeing <strong>Daniel Wirtz</strong> in action on stage. My wife (also a long-time fan) and I drove 400 and some crushed kilometres to <strong>Leipzig</strong> to see his show at the <a href="https://www.felsenkeller-leipzig.com/">Felsenkeller</a>. FINALLY he is back on tour with his new damn good and loud album <a href="https://wirtz.bandzoogle.com/album/2657636/dna">DNA</a>.</p>
<p>For those of you who don’t know what the hell I’m talking about: Daniel Wirtz, or simply <strong>WIRTZ</strong>, is the only German rock artist who really deserves that name. You may forgive me, but I’ve been listening to Rock, Hard Rock, Metal and the like since the early 80s (my first album was KISS - Unmasked), and not one German citizen had enough rock in their blood for me like the Brits or Americans have in abundance. I was always of the opinion that Grunge, Nu Metal and all similar sounds couldn’t come from Germany, let alone … be performed with German lyrics! But I was wrong…</p>
<p>I first became aware of WIRTZ in 2015 on a nice German TV show called “<a href="https://www.vox.de/themen/thema/sing-meinen-song-das-tauschkonzert-2015-t8941.html">Sing mein Song- Das Tauschkonzert</a>“ (Sing My Song, The Swap Concert), where he rocked out songs by other artists in his inimitable way. He sang the lyrics of other authors and put his own guitar sound underneath. Awesome! But what is most ingenious about WIRTZ are his own lyrics. Straightforward on the one hand and very poetic on the other. Simply awesome shit in this combination!</p>
<span id="more"></span>
<blockquote>
<p>Man hat ein Ticket gewonnen und als der Film endlich startet<br>merkt man der Trailer sagt nicht, was einen am Ende erwartet<br>Ob mit tiefen Wunden oder kleineren Kerben<br>Man kann die Wellen nicht reiten, ohne dabei nass zu werden</p>
</blockquote>
<p>(English translation, without end rhymes)</p>
<blockquote>
<p>You’ve won a ticket and when the film finally starts<br>you realise the trailer doesn’t tell you what to expect at the end<br>whether with deep wounds or minor nicks<br>you can’t ride the waves without getting wet</p>
</blockquote>
<p>My last concert with WIRTZ was a while ago, but we had tickets for his unplugged tour … but then the Corona pandemic hit and everything went to hell. Every now and then you could hear a sign of life from him on his <a href="https://www.instagram.com/daniel_wirtz/">Instagram channel</a> and in between there was his lockdown album <a href="https://www.last.fm/de/music/Wirtz/Co+WIRTZ+19+(Lockdown+Versions)">Co-WiRtz 19</a>. As he said himself, it was a difficult time for him, but his fans and I are all the more pleased that he now has released such an amazing new album on the one hand and that he is simply back on the other.</p>
<div class="image-masonry" id="image-masonry-7i1t2d">
<div>
<img loading="lazy" class="no-caption" src="/post/WIRTZ-DNA-Tour-2024-Leipzig-2024-02-17/PXL_20240217_194947390.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/WIRTZ-DNA-Tour-2024-Leipzig-2024-02-17/PXL_20240217_194957970-2.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/WIRTZ-DNA-Tour-2024-Leipzig-2024-02-17/PXL_20240217_204408389.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/WIRTZ-DNA-Tour-2024-Leipzig-2024-02-17/PXL_20240217_203810111.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/WIRTZ-DNA-Tour-2024-Leipzig-2024-02-17/PXL_20240217_203818286-2.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/WIRTZ-DNA-Tour-2024-Leipzig-2024-02-17/PXL_20240217_204426299-2.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/WIRTZ-DNA-Tour-2024-Leipzig-2024-02-17/PXL_20240217_211150032.jpg" alt="" />
</div>
</div>
<video width="100%" height="250px" controls>
<source src="/post/WIRTZ-DNA-Tour-2024-Leipzig-2024-02-17/PXL_20240217_195411250_480.mp4" type="video/mp4">
</video>
<p><strong>Let’s Rock … or fuck off!</strong></p>
https://kiko.io/post/Impressions-from-the-Carnival-Parade-in-Wiesbaden/Impressions from the Carnival Parade in Wiesbaden2024-02-14T16:34:05.000Z<p><img src="https://kiko.io/images/social-media/Impressions-from-the-Carnival-Parade-in-Wiesbaden.png" /></p><p>In Germany, especially along the Rhine, there is only one thing for many people every February: Fasching, Fastnacht, Fasnet or simply Karneval. Many terms that all mean the same thing: Making party as long as you can. Mainz, on the other (wrong) side of the Rhine from my home town Wiesbaden, is one of the three strongholds of carnival in this respect. Their Rose Monday parade is really impressive, but Wiesbaden also has such a parade, on Sunday, and it is usually a little longer than the one in Mainz, although not as pompous and live on TV.</p>
<p>This Sunday, after a long time, I was once again at the parade in Wilhelmstraße at 12.11 pm and had my camera with me …</p>
<span id="more"></span>
<div class="photo-masonry" id="photo-masonry-sft7k8">
<div>
<a class="spotlight" href="/photos/D50_6737_2402"
data-description="D50_6737_2402"
data-src="/shed/D50_6737_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6737_2402">
<img src="/shed/D50_6737_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6738_2402"
data-description="D50_6738_2402"
data-src="/shed/D50_6738_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6738_2402">
<img src="/shed/D50_6738_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6740_2402"
data-description="D50_6740_2402"
data-src="/shed/D50_6740_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6740_2402">
<img src="/shed/D50_6740_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6744_2402"
data-description="D50_6744_2402"
data-src="/shed/D50_6744_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6744_2402">
<img src="/shed/D50_6744_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6757_2402"
data-description="D50_6757_2402"
data-src="/shed/D50_6757_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6757_2402">
<img src="/shed/D50_6757_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6760_2402"
data-description="D50_6760_2402"
data-src="/shed/D50_6760_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6760_2402">
<img src="/shed/D50_6760_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6765_2402"
data-description="D50_6765_2402"
data-src="/shed/D50_6765_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6765_2402">
<img src="/shed/D50_6765_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6766_2402"
data-description="D50_6766_2402"
data-src="/shed/D50_6766_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6766_2402">
<img src="/shed/D50_6766_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6772_2402"
data-description="D50_6772_2402"
data-src="/shed/D50_6772_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6772_2402">
<img src="/shed/D50_6772_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6805_2402"
data-description="D50_6805_2402"
data-src="/shed/D50_6805_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6805_2402">
<img src="/shed/D50_6805_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6807_2402"
data-description="D50_6807_2402"
data-src="/shed/D50_6807_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6807_2402">
<img src="/shed/D50_6807_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6810_2402"
data-description="D50_6810_2402"
data-src="/shed/D50_6810_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6810_2402">
<img src="/shed/D50_6810_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6816_2402"
data-description="D50_6816_2402"
data-src="/shed/D50_6816_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6816_2402">
<img src="/shed/D50_6816_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6821_2402"
data-description="D50_6821_2402"
data-src="/shed/D50_6821_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6821_2402">
<img src="/shed/D50_6821_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6850_2402"
data-description="D50_6850_2402"
data-src="/shed/D50_6850_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6850_2402">
<img src="/shed/D50_6850_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6853_2402"
data-description="D50_6853_2402"
data-src="/shed/D50_6853_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6853_2402">
<img src="/shed/D50_6853_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6862_2402"
data-description="D50_6862_2402"
data-src="/shed/D50_6862_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6862_2402">
<img src="/shed/D50_6862_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6865_2402"
data-description="D50_6865_2402"
data-src="/shed/D50_6865_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6865_2402">
<img src="/shed/D50_6865_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6872_2402"
data-description="D50_6872_2402"
data-src="/shed/D50_6872_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6872_2402">
<img src="/shed/D50_6872_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6874_2402"
data-description="D50_6874_2402"
data-src="/shed/D50_6874_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6874_2402">
<img src="/shed/D50_6874_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6877_2402"
data-description="D50_6877_2402"
data-src="/shed/D50_6877_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6877_2402">
<img src="/shed/D50_6877_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6884_2402"
data-description="D50_6884_2402"
data-src="/shed/D50_6884_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6884_2402">
<img src="/shed/D50_6884_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6903_2402"
data-description="D50_6903_2402"
data-src="/shed/D50_6903_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6903_2402">
<img src="/shed/D50_6903_2402/mobile.jpg">
</a>
</div>
<div>
<a class="spotlight" href="/photos/D50_6906_2402"
data-description="D50_6906_2402"
data-src="/shed/D50_6906_2402/normal.jpg"
data-button="Open Page"
data-button-href="/photos/D50_6906_2402">
<img src="/shed/D50_6906_2402/mobile.jpg">
</a>
</div>
</div>
https://kiko.io/post/SVWW-vs-Nurnberg-2024-02-09/SVWW vs. Nürnberg @ 2024-02-092024-02-12T15:05:44.000Z<p><img src="https://kiko.io/images/social-media/SVWW-vs-Nurnberg-2024-02-09.png" /></p>
<div class="float-matchgrid">
<img src="/images/logos/SV-Wehen-Wiesbaden.svg" />
<span>1:1</span>
<img src="/images/logos/1-FC-Nuernberg.svg" />
</div>
<p>Tim and I have known each other since we were at school, which is over 40 years now. He has always been a fan of 1. FC Nürnberg and I can still remember him teasing me on Monday mornings at school about my HSV’s poor performance at the weekend and me teasing him when his Nuremberg team had lost again. Now we were both just sympathisers of our clubs rather than real fans, because I’d never been to a stadium in Hamburg and I don’t think Tim had ever been to the Nuremberg arena either.</p>
<p>Now my passion for football with my hometown club <strong>SV Wehen Wiesbaden</strong> has awakened in the last year and I have a season ticket for this season and the possibility to get 4 more tickets for every game. So it made sense to ask Tim if he would like to watch the home game in Wiesbaden against his <strong>1. FC Nürnberg</strong>, which are playing in the second division again since their relegation in the 2019/2020 season. It’s the first time that Tim and I have gone to the stadium together and he convinced his son Tom and two of his mates to go along, even though the three of them didn’t really seem to have anything to do with FCN.</p>
<p>The spectacular game in the first half of the season a few months ago, with a total of 3 red cards, unfortunately ended in a 2:1 loss for the Wiesbaden team. So we had something to settle with the Nurembergers and at least the fans in the stadium were highly motivated.</p>
<span id="more"></span>
<hr>
<h2 id="The-Game"><a href="#The-Game" class="headerlink" title="The Game"></a>The Game</h2><p>However, our team saw things a little differently in the first half. There was not much sign of motivation. On the contrary, they seemed to be very nervous, as there was no other explanation for the many passing errors and lost tackles. Even our defence boss Vukotic was completely out of sorts and it was only thanks to our goalkeeper <strong>Stritzel</strong> and the similarly careless attacking line of Nuremberg, who were much more reliable on the ball, that we didn’t have to go into half-time with a huge deficit. The most energetic player in the first 45 minutes was SVWW coach <strong>Kauczinski</strong>, who shouted himself hoarse on the sideline and spoke of the worst first half of the season in the subsequent press conference.</p>
<div class="image-masonry" id="image-masonry-ltxgge">
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Nurnberg-2024-02-09/PXL_20240209_165926024.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Nurnberg-2024-02-09/PXL_20240209_171433396.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Nurnberg-2024-02-09/PXL_20240209_172706114.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Nurnberg-2024-02-09/PXL_20240209_173002197.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Nurnberg-2024-02-09/PXL_20240209_175308655.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Nurnberg-2024-02-09/PXL_20240209_181633787.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Nurnberg-2024-02-09/PXL_20240209_183330192.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Nurnberg-2024-02-09/PXL_20240209_184514083.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Nurnberg-2024-02-09/PXL_20240209_184926974.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Nurnberg-2024-02-09/PXL_20240209_185144866.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Nurnberg-2024-02-09/PXL_20240209_185156343.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Nurnberg-2024-02-09/PXL_20240209_192920570.jpg" alt="" />
</div>
</div>
<p>The coach seemed to have expressed his frustration in the dressing room, as the team came onto the pitch not only changed in terms of personnel but also mentally. They held out and there were also one or two good combinations in the direction of the Nuremberg goal. The nimble Thijmen Goppel had the first goal on his feet in the 48th minute, but unfortunately failed to beat the goalkeeper.</p>
<p>What followed was a fan spectacle that we have already seen in other stadiums this week: In frustration against the DFL’s decision to allow major investors into German football, large banners were hoisted in the Nuremberg curve and masses of tennis balls were thrown onto the pitch. The referee had to interrupt the game for almost 10 minutes for safety reasons, while both fan camps took turns chanting “Sch*** DFL”. It was quite impressive.</p>
<video width="100%" height="250px" controls>
<source src="/post/SVWW-vs-Nurnberg-2024-02-09/PXL_20240209_184625118_480p.mp4" type="video/mp4">
</video>
<p>Shortly after they were allowed to play on, Wiesbaden pushed forward again and after a nice combination, Nuremberg lost sight of Prtajin, who had enough time and space to make it <strong>1:0</strong> from a good 15 metres! YES…!</p>
<p>The game was now becoming quite attractive. It was constantly back and forth, sometimes even a bit wild, but it was fun to watch. Even when Nuremberg were awarded a corner after about an hour and then almost half a dozen more, because although ours struggled to get the ball out of their own half, it kept going over the goal line. It came as it had to come: a header to make it <strong>1:1</strong> … :|</p>
<p>In the last 10 minutes of the game, it seemed as if FCN had run out of steam and we could really go all the way, but Prtajin was the last to beat the good Nuremberg goalkeeper. Too bad…</p>
<p>⇾ <a href="https://www.kicker.de/wiesbaden-gegen-nuernberg-2024-bundesliga-4861849/analyse">Match report on kicker.de</a></p>
<hr>
<h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>Overall, the draw was okay for us, as bad as the first half was. The one point helps us a little against relegation, because we now have 27 points and are six points off the relegation places. But there are still 13 games to play and if a team from the bottom, such as Kaiserslautern or Braunschweig, gets their act together, things could get really tight. Our guys have to become more stable again and stop making so many individual mistakes.</p>
<p>Well, next week we’re away against Schalke 04, who are still a few places below us and aren’t getting much going at the moment. Hopefully they won’t start winning against us.</p>
<a href="https://www.kicker.de/2-bundesliga/tabelle">
<img loading="lazy" src="tabelle-24-02-11.png" alt="Table 2. Liga, Sunday 11. February 2024" />
</a>
https://kiko.io/post/SVWW-vs-Hertha-BSC-2024-01-27/SVWW vs. Hertha BSC @ 2024-01-272024-01-28T12:04:17.000Z<p><img src="https://kiko.io/images/social-media/SVWW-vs-Hertha-BSC-2024-01-27.png" /></p>
<div class="float-matchgrid">
<img src="/images/logos/SV-Wehen-Wiesbaden.svg" />
<span>3:1</span>
<img src="/images/logos/Hertha-BSC.svg" />
</div>
<p>After four consecutive defeats, the last of which came a week ago away against <strong>Magdeburg</strong>, the first home game of the year in the second half of the 2. Bundesliga against <strong>Hertha BSC</strong> was on the programme this Saturday.</p>
<p>I was a little tense, as the team was slowly slipping down the table, but confident that we could win against the Berliners as we had done in the first half of the season, even though Hertha had been unbeaten for almost 10 games. Bärbel, the good soul to my left, was also back in the stadium and we encouraged each other in the best football weather (a little cold but sunny). We had to win again to avoid falling into a mental downward spiral. We and 10,454 spectators believed in it … no, less, because a good quarter of the spectators were quite vociferous Berlin fans. The visitors’ curve was packed.</p>
<span id="more"></span>
<p>While Bärbel and I had some small talk and she told me how nice it was to be able to enjoy her retirement after more than 40 years in one and the same company and I thought about my alarm clock ringing at 07:00 on Monday morning, our fans drummed and sang themselves warm and were suddenly swept away by the vocal power of the Berliners! Unbelievable how this curve could sing and give me goose bumps!</p>
<hr>
<h2 id="The-Game"><a href="#The-Game" class="headerlink" title="The Game"></a>The Game</h2><p>Everything went as expected in the first 20 minutes of the game. We are by no means a team that goes straight forward, no, we stand at the back and let the others play. And that’s what Hertha did. They played … but you could somehow tell that they weren’t fully focussed. Perhaps it was due to the unrest in the club, as their president had recently passed away unexpectedly. Somehow it was unimaginative possession and occasionally some solid poking. It wasn’t a pretty game and there were hardly any scoring chances for the Berliners because they simply made too many mistakes.</p>
<p>We weren’t really happy with ours either. The “football experts” in the row behind me were once again verbally picking on our striker <strong>Franko Kovacevic</strong>, who had joined us this season but had so far remained extremely pale … until, after a quick counter-attack and a shot on goal from Heußer, Kovacevic somehow managed to poke the ball into the net after it had been blocked by the keeper! <strong>1:0!</strong></p>
<p>11 appearances, only 2 of them from the start for Kovacivic and in this one he finally scores his first goal. The joy was immense and you could literally see the load off his mind. Half the bench hugged him as he celebrated the goal and the pundits behind me seemed speechless.</p>
<p>But it got even better after Wehen-Wiesbaden had two really good chances by Prtajin and Goppel in the remainder of the first half but couldn’t put the ball in the net and Hertha seemed happy to go in at half-time. The visitors came out of the half much more motivated and had two good chances, only to see <strong>Kovacevic</strong> put the ball in the net again after a cross from Wehen’s Jacobsen because his foot was in the right place at the right time. <strong>2:0</strong> instead of 1:1 … yessss and again Kovacevic and that in best striker manner!</p>
<div class="image-masonry" id="image-masonry-dw7au1">
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Hertha-BSC-2024-01-27/PXL_20240127_113119804.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Hertha-BSC-2024-01-27/PXL_20240127_113127851.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Hertha-BSC-2024-01-27/PXL_20240127_113220343.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Hertha-BSC-2024-01-27/PXL_20240127_113749100.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Hertha-BSC-2024-01-27/PXL_20240127_113959081.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Hertha-BSC-2024-01-27/PXL_20240127_114934307.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Hertha-BSC-2024-01-27/PXL_20240127_115602237.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Hertha-BSC-2024-01-27/PXL_20240127_115652902.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Hertha-BSC-2024-01-27/PXL_20240127_121414472.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Hertha-BSC-2024-01-27/PXL_20240127_121642584.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Hertha-BSC-2024-01-27/PXL_20240127_121931243.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Hertha-BSC-2024-01-27/PXL_20240127_122328411.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Hertha-BSC-2024-01-27/PXL_20240127_133007677.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Hertha-BSC-2024-01-27/PXL_20240127_135329363.jpg" alt="" />
</div>
</div>
<p>Berlin wobbled, but did not give up. They were lucky that AGAIN Kovacevic only hit the crossbar two minutes later, but Kenny made it 2:1 from distance just three minutes later.</p>
<p>It was now a fast-paced and thrilling game and our keeper Strietzel had a lot to do and my nerves were a little frayed. A narrow lead with just under half an hour left to play. Both teams now made a few substitutions, which only briefly slowed the pace of the game. That was until the 72nd minute, when <strong>Thijmen Goppel</strong> left half the Hertha defence standing in a wonderful counter-attack and scored to make it <strong>3:1</strong>! Yessss…</p>
<p>The Berlin team were now a little out of steam, even if they kept trying, but our team slowed the game down a little and cleverly took a minute or two off the clock until the referee finally blew the final whistle in the 95th minute.</p>
<p>⇾ <a href="https://www.kicker.de/wiesbaden-gegen-hertha-2024-bundesliga-4861831/spielbericht">Match report on kicker.de</a></p>
<hr>
<h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>If we want to stay in the 2nd division, we have to at least somehow win our home games. We are currently in 10th place in the table, but only 5 points or 3 defeats away from the relegation places. All the teams below us will be doing everything they can to overtake us. Kaiserslautern and Rostock also won this weekend and next week we have to play Karlsruhe. We’ll have to grit our teeth and hope that Frank Kovacevic will become our new goal machine now that he seems to have hit his stride.</p>
<a href="https://www.kicker.de/2-bundesliga/tabelle">
<img loading="lazy" src="tabelle-24-01-28.png" alt="Table 2. Liga, Sunday 28. January 2024" />
</a>
https://kiko.io/post/Favourite-Pens-of-2023/Favourite Pens of 20232024-01-07T11:32:16.000Z<p><img src="https://kiko.io/images/social-media/Favourite-Pens-of-2023.png" /></p><p>I’ve been collecting imaginative and technologically tricky Codepens on a Trello list for quite a while, primarily to learn and perhaps implement them in a project at some point. A good source for high-quality pens is the recently published list of the <a href="https://codepen.io/2023/popular/pens/">Top 100 Pens of 2023 on codepen.io</a>. I have already noticed some of the works over the course of the year, but some have slipped through my fingers and so I am glad that I have this list, the order of which is calculated from the hearts (likes) given and their level.</p>
<p>Here are my 10 favourite pens from 2023 … and, like last year, an 11th jaw-droppingly good pen to start with.</p>
<p><img src="/post/Favourite-Pens-of-2023/climacode.png" alt="Fantastic piece of CSS art ... and a little JavaScript"></p>
<p>What <strong><a href="https://codepen.io/RAFA3L">Rafa</a></strong> has conjured up with his pen <a href="https://codepen.io/RAFA3L/pen/ZEmBzEv"><strong>ClimaCode</strong></a> is truly breathtaking and is spread over 3,658 lines of HTML (including SVG objects), 904 lines of CSS and “only” 138 lines of JavaScript. It’s fun to try out all the controls of this climate dashboard and enjoy the soft parallax effects. My favourite button is of course Nessy.</p>
<p>(Also try out his coffee generator <a href="https://codepen.io/RAFA3L/pen/QWJOmrL">“For The Love Of Coffee”</a>, which is indispensable for IT professionals ;))</p>
<span id="more"></span>
<hr><br>
<h2 id="1-The-aurora-only-CSS-by-Ostylowany"><a href="#1-The-aurora-only-CSS-by-Ostylowany" class="headerlink" title="1. The aurora (only CSS) by Ostylowany"></a>1. The aurora (only CSS) by <a href="https://codepen.io/ostylowany">Ostylowany</a></h2>
<iframe height="480"
id="codepen-vYzPVZL"
class="codepen"
src="https://codepen.io/kristofzerbe/embed/vYzPVZL?height=480&default-tab=result&theme-id=dark"
style="width: 100%;"
scrolling="no"
title="Codepen: The aurora (only CSS)"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true">
</iframe>
<p><br><hr><br></p>
<h2 id="2-…CSS-only-fluid-slider…-by-Ana-Tudor"><a href="#2-…CSS-only-fluid-slider…-by-Ana-Tudor" class="headerlink" title="2. …CSS-only fluid slider… by Ana Tudor"></a>2. …CSS-only fluid slider… by <a href="https://codepen.io/thebabydino">Ana Tudor</a></h2>
<iframe height="600"
id="codepen-qByGqOm"
class="codepen"
src="https://codepen.io/kristofzerbe/embed/qByGqOm?height=600&default-tab=result&theme-id=dark"
style="width: 100%;"
scrolling="no"
title="Codepen: ...CSS-only fluid slider..."
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true">
</iframe>
<p><br><hr><br></p>
<h2 id="3-Futuristic-Dial-Button-by-LukyVj"><a href="#3-Futuristic-Dial-Button-by-LukyVj" class="headerlink" title="3. Futuristic Dial Button by LukyVj"></a>3. Futuristic Dial Button by <a href="https://codepen.io/LukyVj">LukyVj</a></h2>
<iframe height="420"
id="codepen-xxyEYMJ"
class="codepen"
src="https://codepen.io/kristofzerbe/embed/xxyEYMJ?height=420&default-tab=result&theme-id=dark"
style="width: 100%;"
scrolling="no"
title="Codepen: Futuristic Dial Button"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true">
</iframe>
<p><br><hr><br></p>
<h2 id="4-Gooey-Toggle-Switch-by-Nicolas-Jesenberger"><a href="#4-Gooey-Toggle-Switch-by-Nicolas-Jesenberger" class="headerlink" title="4. Gooey Toggle Switch by Nicolas Jesenberger"></a>4. Gooey Toggle Switch by <a href="https://codepen.io/nicolasjesenberger">Nicolas Jesenberger</a></h2>
<iframe height="400"
id="codepen-xxmbvxL"
class="codepen"
src="https://codepen.io/kristofzerbe/embed/xxmbvxL?height=400&default-tab=result&theme-id=dark"
style="width: 100%;"
scrolling="no"
title="Codepen: Gooey Toggle Switch"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true">
</iframe>
<p>(Don’t miss his <a href="https://codepen.io/nicolasjesenberger/pen/bGQwBYo">Squishy Switch</a> either)</p>
<p><br><hr><br></p>
<h2 id="5-Gorgeous-animated-gradient-borders…-by-Jason-Lengstorf"><a href="#5-Gorgeous-animated-gradient-borders…-by-Jason-Lengstorf" class="headerlink" title="5. Gorgeous animated gradient borders… by Jason Lengstorf"></a>5. Gorgeous animated gradient borders… by <a href="https://codepen.io/jlengstorf">Jason Lengstorf</a></h2>
<iframe height="400"
id="codepen-WNPGMJo"
class="codepen"
src="https://codepen.io/kristofzerbe/embed/WNPGMJo?height=400&default-tab=result&theme-id=dark"
style="width: 100%;"
scrolling="no"
title="Codepen: Gorgeous animated gradient borders..."
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true">
</iframe>
<p><br><hr><br></p>
<h2 id="6-Fancy-hover-effect-on-avatar-by-Temani-Afif"><a href="#6-Fancy-hover-effect-on-avatar-by-Temani-Afif" class="headerlink" title="6. Fancy hover effect on avatar by Temani Afif"></a>6. Fancy hover effect on avatar by <a href="https://codepen.io/t_afif">Temani Afif</a></h2>
<iframe height="540"
id="codepen-MWBjraa"
class="codepen"
src="https://codepen.io/kristofzerbe/embed/MWBjraa?height=540&default-tab=result&theme-id=dark"
style="width: 100%;"
scrolling="no"
title="Codepen: Fancy hover effect on avatar"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true">
</iframe>
<p><br><hr><br></p>
<h2 id="7-3D-Cards-by-Mubanga"><a href="#7-3D-Cards-by-Mubanga" class="headerlink" title="7. 3D Cards by Mubanga"></a>7. 3D Cards by <a href="https://codepen.io/mubangadv">Mubanga</a></h2>
<iframe height="720"
id="codepen-YzJNbOa"
class="codepen"
src="https://codepen.io/kristofzerbe/embed/YzJNbOa?height=720&default-tab=result&theme-id=dark"
style="width: 100%;"
scrolling="no"
title="Codepen: 3D Cards"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true">
</iframe>
<p><br><hr><br></p>
<h2 id="8-Stripe-Sessions-style-Flex-Carousel-by-Simon-Goellner"><a href="#8-Stripe-Sessions-style-Flex-Carousel-by-Simon-Goellner" class="headerlink" title="8. Stripe Sessions style Flex Carousel by Simon Goellner"></a>8. Stripe Sessions style Flex Carousel by <a href="https://codepen.io/simeydotme">Simon Goellner</a></h2>
<iframe height="800"
id="codepen-gOBMZMe"
class="codepen"
src="https://codepen.io/kristofzerbe/embed/gOBMZMe?height=800&default-tab=result&theme-id=dark"
style="width: 100%;"
scrolling="no"
title="Codepen: Stripe Sessions style Flex Carousel"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true">
</iframe>
<p><br><hr><br></p>
<h2 id="9-Carousel-with-drag-and-wheel-by-Fabio-Ottaviani"><a href="#9-Carousel-with-drag-and-wheel-by-Fabio-Ottaviani" class="headerlink" title="9. Carousel with drag and wheel by Fabio Ottaviani"></a>9. Carousel with drag and wheel by <a href="https://codepen.io/supah">Fabio Ottaviani</a></h2>
<iframe height="720"
id="codepen-xxJMbbg"
class="codepen"
src="https://codepen.io/kristofzerbe/embed/xxJMbbg?height=720&default-tab=result&theme-id=dark"
style="width: 100%;"
scrolling="no"
title="Codepen: Carousel with drag and wheel"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true">
</iframe>
<p><br><hr><br></p>
<h2 id="10-Background-Slider-Swiper-by-Ecem-Gokdogan"><a href="#10-Background-Slider-Swiper-by-Ecem-Gokdogan" class="headerlink" title="10. Background Slider | Swiper by Ecem Gokdogan"></a>10. Background Slider | Swiper by <a href="https://codepen.io/ecemgo">Ecem Gokdogan</a></h2>
<iframe height="580"
id="codepen-QWzeQOK"
class="codepen"
src="https://codepen.io/kristofzerbe/embed/QWzeQOK?height=580&default-tab=result&theme-id=dark"
style="width: 100%;"
scrolling="no"
title="Codepen: Background Slider | Swiper"
frameborder="no"
loading="lazy"
allowtransparency="true"
allowfullscreen="true">
</iframe>
https://kiko.io/post/App-Defaults-2023/App Defaults 20232023-12-30T23:27:00.000Z<p><img src="https://kiko.io/images/social-media/App-Defaults-2023.png" /></p><p>First I saw it on <a href="https://rknight.me/blog/app-defaults/">Robb Knight’s website</a> and said to myself “yeah look, another one with a list of his tools”. Then I saw it on <a href="https://garrit.xyz/posts/2023-11-07-app-defaults">Garrit’s blog</a>, then on <a href="https://chriscoyier.net/2023/11/25/default-apps-2023/">Chris Coyier’s</a> and today on <a href="https://coryd.dev/posts/2023/default-apps-2023/">Cory Dransfeld’s</a> and <a href="https://chrismcleod.dev/blog/default-apps-for-2023/">Chris McLeod’s</a> and I thought: funny trend with the <a href="https://defaults.rknight.me/"><strong>App Defaults</strong></a>. Let’s go for it…</p>
<span id="more"></span>
<ul>
<li><strong>Mail:</strong> <a href="https://gmail.com/">Gmail</a></li>
<li><strong>Tasks:</strong> Google Tasks via <a href="https://www.appgenix-software.com/">Business Calendar 2</a>, <a href="https://trello.com/">Trello</a></li>
<li><strong>Contacts:</strong> <a href="https://contacts.google.com/">Google Contacts</a></li>
<li><strong>Photo Shooting:</strong> Nikon D500, <a href="https://store.google.com/product/pixel_8">Google Pixel 8</a></li>
<li><strong>Photo Editing/Management:</strong> <a href="https://www.adobe.com/products/photoshop-lightroom-classic.html">Adobe Lightroom Classic</a></li>
<li><strong>RSS:</strong> <a href="https://feedly.com/">Feedly</a></li>
<li><strong>News:</strong> <a href="https://news.google.com/">Google News</a>, <a href="https://feedly.com/">Feedly</a></li>
<li><strong>Browser:</strong> <a href="https://www.google.com/intl/en/chrome/">Google Chrome</a>, <a href="https://www.microsoft.com/edge">Microsoft Edge</a>, <a href="https://vivaldi.com/">Vivaldi</a></li>
<li><strong>Chat:</strong> <a href="https://signal.org/">Signal</a></li>
<li><strong>Bookmarks:</strong> <a href="https://www.google.com/intl/en/chrome/">Google Chrome</a>, <a href="/collections/tiny-tools/">kiko.io’s Tiny Tools</a></li>
<li><strong>Read It Later:</strong> <a href="https://trello.com/">Trello</a>, <a href="https://www.google.com/intl/en/chrome/">Google Chrome</a> (Android)</li>
<li><strong>Word Processing:</strong> <a href="https://www.microsoft.com/microsoft-365">M365</a></li>
<li><strong>Spreadsheets:</strong> <a href="https://www.microsoft.com/microsoft-365">M365</a></li>
<li><strong>Presentations:</strong> <a href="https://www.microsoft.com/microsoft-365">M365</a></li>
<li><strong>Music:</strong> <a href="https://play.google.com/store/apps/details?id=com.musicplayer.blackplayerfree&hl=de&gl=US">BlackPlayer</a></li>
<li><strong>Passwords:</strong> <a href="https://keepass.info/">KeePass</a></li>
<li><strong>Code Editor</strong> <a href="https://code.visualstudio.com/">Visual Studio Code</a>, <a href="https://visualstudio.microsoft.com/">Visual Studio</a></li>
<li><strong>Source Control</strong> <a href="https://github.com/">GitHub</a>, <a href="https://www.microsoft.com/en/microsoft-365/onedrive/online-cloud-storage">OneDrive</a></li>
<li><strong>Shopping Lists:</strong> <a href="https://keep.google.com/">Google Keep</a></li>
<li><strong>Meal Planning:</strong> Leni - Ultimate version of wife (no link available)</li>
</ul>
<p>I used to be called the “tool boy” because I had to try out every damn tool and accordingly have many on every type of device. But this list is the stuff I deal with every day.</p>
https://kiko.io/post/IndieFediWebVerse/IndieFediWebVerse2023-12-27T12:54:38.000Z<p><img src="https://kiko.io/images/social-media/IndieFediWebVerse.png" /></p><p>Today I was listening to Mike McCue’s Dot Social podcast where he <a href="https://about.flipboard.com/inside-flipboard/eugen-rochko/">talks</a> with Eugen Rochko about Mastodon and ActivityPub. At around 12:30 Eugen talked about Twitter and the fact that Tumblr is willing to join the Fediverse network and he asked a question, which he immediately tried to improve again:</p>
<blockquote>
<p>I think that is the future, because why should we have all these different accounts … ehh … like … ehh … all these different experiences that are required to connect with different people, when we could have just one account and connect with everyone who uses different services just from one account?</p>
</blockquote>
<div class="video-wrapper" style="margin-top:1rem;">
<iframe title="The State of the Federation, with Mastodon's Eugen Rochko" width="560" height="315" src="https://flipboard.video/videos/embed/60495342-c321-4949-9cc9-0fa1a1f2d788?start=12m30s" frameborder="0" allowfullscreen="" sandbox="allow-same-origin allow-scripts allow-popups"></iframe>
</div>
<p>This slip of the tongue and Eugens emphasis on “ONE account” hit me, because it points to something that has bothered me about Fediverse so far, as wonderfully open and forward-looking as it is:</p>
<p><strong>It doesn’t free me from the need to be a duplicate of myself everywhere in the form of an account.</strong></p>
<p>… because I’m not only a Mastodon, Pixelfed or <you name it > user. I’m me at kiko.io in the first place!</p>
<span id="more"></span>
<hr>
<h2 id="My-home-website-is-my-castle"><a href="#My-home-website-is-my-castle" class="headerlink" title="My home website is my castle"></a>My <s>home</s> website is my castle</h2><p>I consider this website to be the center of my digital and public persona. This is me, in terms of longer articles about this and that and an initial place for my photos, that are suitable for a wide audience. From here I transport my content to other channels like Mastodon, dev.to or photo platforms like Pixelfed and some old classic silos like Flickr or 500px.</p>
<p>This syndication is done manually and I’m fine with that at the moment. For one thing, some platforms have no API at all and for another, they all have a slightly different post structure or even length limits. Tools like Bridgy Fed, which turn a website into its own Fediverse actor, won’t help me and I don’t really want that either, because I want to decide on the form of distribution myself. Maybe one day there will be an AI-driven tool that will help me to summarize my content in my own language specifically for the platform I want my content to syndicate and post it with a click. But for now I want to decide how, where and when.</p>
<p>But my point here is a different one: I am the owner, creator and operator of this blog, or to put it in ActivityPub language: <strong>@me@kiko.io</strong>. But I am not only interested in publishing my articles or photos, but also in communicating in other ways, away from my own content, but still with reference to myself and my main identity.</p>
<p>On Mastodon, however, I am <em>@kiko@indieweb.de</em> and on Pixelfed you interact with <em>@kristofz@pixelfed.social</em> because the name ‘kiko’ was already taken. To get to the point: I always appear on the platforms as someone else, away from my main identity. I chat with one identity, comment on a photo with another and like a book review with a completely different one. It’s a bit of a split personality a la Dr. Jekyll and Mister Hyde, but it should be always <em>@me@kiko.io</em>, or at least something like that.</p>
<p>The difference between the classic web services and all Fediverse services is, that they are interoperable, so you need and get an address and not just a login name and password. But they behave in this particular way like the classic services: they create the address on their domain for you after you have logged in with your username and password. You can’t bring the address with you. </p>
<p>The only widely known interoperable service where this is not the case is email, which is often used as an example for comparison with the Fediverse. Of course you can also use a Gmail or Yahoo mail address, but in my case me@kiko.io is a working mail address, even if I rarely use it. This is not possible in the Fediverse.</p>
<p>I recently tried to make my main identity discoverable using a WebFinger file, at least in the Fediverse, but is less than a crutch and has its pitfalls.</p>
<p>I would like to see some sort of alias system where you can choose which alias of your main identity you want to use on each platform. Using the main identity doesn’t work if you are active on more than one platform because the Fediverse inbox/outbox system requires a unique address. But <em>@mastodon@kiko.io</em> or <em>@pixelfed@kiko.io</em> would work. Along a pattern such as <code>@{Platform}@{Own Domain}</code> … or even better and more flexible by a Webfinger-like file on your (static) website which defines the endpoints of a particular alias of your main identity to the plattform.</p>
<p>For example, if I no longer want to run Mastodon via indieweb.social (Tim, that will never happen), then I export my data there, import it to mastodon.social, for example, and just change the endpoint of the address <em>@mastodon@kiko.io</em> in my file, deploy and I’m done. Full control.</p>
<p>Now, if we imagine there was a tool that could collect and aggregate the interactions that occur in the inboxes of such main identity aliases under the full control of the domain owner, would feel like a dream come true, at least for me. I appreciate <a href="https://brid.gy/">brid.gy</a> and <a href="https://webmention.io/">webmention.io</a> and the work of their authors and operators Ryan Barret and Aaron Parecki, but these tools are band-aids to make the shortcomings of today’s Fediverse somewhat bearable.</p>
<hr>
<p>Unfortunately, I am not in a position or capable to implement everything like this technically, even if I understand the principles behind all the stuff, but surely there is someone out there with similar ideas and the skills to go with it.</p>
<p>This reorganization or rather evolution of the Fediverse would really be a liberation from the old ways of functioning and would actually give control back to the users, who are the real basis of social media platforms.</p>
<p>A little more <a href="https://indieweb.org/">IndieWeb</a> would be good for the Fediverse. Just in the direction of a IndieFediWebVerse…</p>
https://kiko.io/post/Comparison-of-Lightroom-Classic-Denoise-with-Topaz-Photo-AI/Comparison of Lightroom Classic Denoise with Topaz Photo AI2023-12-24T12:38:51.000Z<p><img src="https://kiko.io/images/social-media/Comparison-of-Lightroom-Classic-Denoise-with-Topaz-Photo-AI.png" /></p><p>I don’t like to call myself a “photographer”, as my friends sometimes do when they are enthusiastic about one of my pictures, but rather a “photo enthusiast”, because I know that I have no idea about real photography and am simply too lazy to take the perfect photo.</p>
<p>My current gear is a <strong>Nikon D500</strong> and the incredible <strong>Tamron 18-400mm f/3.5-6.3 Di II VC HLD</strong>, which allows me to capture everything reasonably well from very close up to far away. When traveling, where I take most of my pictures, I usually also have my <strong>Nikkor 35mm Ff/1.8</strong> and a fisheye lens with me, but a change of lens is often not appropriate. Not that my wife doesn’t understand that I often fiddle with my camera for minutes on end to then make the strangest contortions for the best shooting angle while she simply enjoys the beautiful view, but I don’t want to overtax her patience either. So I usually leave the Tamron on and live with “ISO Auto” to compensate for the lack of luminous intensity of this lens monster in low light conditions.</p>
<p>The problem that naturally arises from this is <strong>considerable grain</strong> and often also <strong>motion blur</strong>. If, like me, you usually take photos according to the motto “Point & Shoot”, some of the best photos, in terms of the scenic representation, are simply a mess afterwards, even with massive use of my favorite image editing tool <strong>Lightroom Classic</strong>.</p>
<p>To address this problem when post-processing photos, it was a step forward when Adobe added an AI-supported denoise function to Lightroom Classic in one of the latest versions. However, this has often not satisfied me, as the setting options are limited and the results often overshoot the mark.</p>
<p>Yesterday, <a href="https://indieweb.social/@StefanMuenz@vivaldi.net/111631806056217261">Stefan Münz drew my attention to the “Topaz Photo AI”</a> tool and I used the morning to find out whether the additional around 150 USD required for a license would make sense … as a Christmas present to myself.</p>
<span id="more"></span>
<p>Here’s a problematic photo I took handheld with the Tamron at f/4.5 and a shutter speed of 1/400 at the Heineken brewery this spring, as edited without denoising and published here. ISO … (drum roll) … 40.000!</p>
<div class="photo-list" id="photo-list-2g6b9y">
<figure>
<a href="/photos/23-05-Holland-0184" class="no-break">
<img loading="lazy" src="/pool/23-05-Holland-0184/normal.jpg">
</a>
<figcaption>Brass Crane</figcaption>
</figure>
</div>
<hr>
<h2 id="Lightroom-Classic"><a href="#Lightroom-Classic" class="headerlink" title="Lightroom Classic"></a>Lightroom Classic</h2><p>My first attempt to improve the image in terms of denoising with Lightroom Classics (13.1) denoise feature, set to 80%:</p>
<div id="image-compare-gm4lnu">
<img class="image-compare image-original no-zoom no-caption" src="/post/Comparison-of-Lightroom-Classic-Denoise-with-Topaz-Photo-AI/0184-lrc-denoise.jpg" alt="" />
<img class="image-compare image-modified no-zoom no-caption" src="/post/Comparison-of-Lightroom-Classic-Denoise-with-Topaz-Photo-AI/0184-lrc-denoise-80-result.jpg" alt="" />
</div>
<p>Ouch … the result is a bit too bold, even if the noise reduction and sharpness are excellent. You can certainly adjust the colors and contrast with Lightroom’s on-board tools, but you have few options to influence the result in advance and, in my opinion, the AI is a little freewheeling here.</p>
<p>In another image, which is not as extreme in terms of color, Adobe’s AI performs much better:</p>
<div id="image-compare-mts96h">
<img class="image-compare image-original no-zoom no-caption" src="/post/Comparison-of-Lightroom-Classic-Denoise-with-Topaz-Photo-AI/0557-lrc-before.jpg" alt="" />
<img class="image-compare image-modified no-zoom no-caption" src="/post/Comparison-of-Lightroom-Classic-Denoise-with-Topaz-Photo-AI/0557-lrc-after.jpg" alt="" />
</div>
<hr>
<h2 id="Topaz-Photo-AI"><a href="#Topaz-Photo-AI" class="headerlink" title="Topaz Photo AI"></a>Topaz Photo AI</h2><p>My second attempt with Topaz Photo AI 2.2.1 (2023 Release), accessed via the available Lightroom plugin, by calling it via <code>File</code> > <code>Plug-in Extras</code> > <code>Process with Topaz Photo AI</code>, which takes the original RAW photo:</p>
<div id="image-compare-9ne1pb">
<img class="image-compare image-original no-zoom no-caption" src="/post/Comparison-of-Lightroom-Classic-Denoise-with-Topaz-Photo-AI/0184-topaz-denoise-before.jpg" alt="" />
<img class="image-compare image-modified no-zoom no-caption" src="/post/Comparison-of-Lightroom-Classic-Denoise-with-Topaz-Photo-AI/0184-topaz-denoise-after.jpg" alt="" />
</div>
<p>Wow … definitely better and closer to the original, because the Topaz AI is a little more careful and also gives the user a lot to influence the result. You can choose between different models for both individual actions and, for example, limit yourself to the main motif in the image.</p>
<p>The result of the second image is as good as that of Lightroom:</p>
<div id="image-compare-ifee6u">
<img class="image-compare image-original no-zoom no-caption" src="/post/Comparison-of-Lightroom-Classic-Denoise-with-Topaz-Photo-AI/0557-topaz-before.jpg" alt="" />
<img class="image-compare image-modified no-zoom no-caption" src="/post/Comparison-of-Lightroom-Classic-Denoise-with-Topaz-Photo-AI/0557-topaz-after.jpg" alt="" />
</div>
<hr>
<h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>With <a href="https://www.topazlabs.com/topaz-photo-ai">Photo AI</a>, Topaz has created a tool for Mac and Windows that can save a photo or two, and for a passionate photo enthusiast, that could be worth the $149 USD holiday sale ;)</p>
<p><img src="/post/Comparison-of-Lightroom-Classic-Denoise-with-Topaz-Photo-AI/topaz-activation.png" alt="Activated..."></p>
<p>Yeah, Santa … I’m coming.</p>
https://kiko.io/post/Don-t-be-ignorant-and-offer-a-theme-switch/Don't be ignorant and offer a theme switch2023-12-22T11:54:51.000Z<p><img src="https://kiko.io/images/social-media/Don-t-be-ignorant-and-offer-a-theme-switch.png" /></p><p>Coming from a <a href="https://minutestomidnight.co.uk/blog/giving-context-to-a-blogroll/">IndieNews post from Simone</a>, I stumbled over Garrit’s post <a href="https://garrit.xyz/posts/2023-12-12-roast-my-site" class="u-in-reply-to"><strong>Roast my Site!</strong></a>, to which Simone had referred. I was also just in roast mode again and if someone asks me so kindly, then go for it…</p>
<p>A little background …</p>
<p>I have often talked about a topic on Mastodon or other channels that keeps bothering me for very personal reasons: <strong>Dark and Light mode on websites</strong>. I am glad that both exist, but the way they are usually implemented sometimes drives me crazy.</p>
<p>My eyes (and I think many other people can relate to this) start to flicker slightly after a few seconds of reading white text on a dark background, or at least it feels that way. The stronger the contrast, e.g. pure white on pure black, the faster it goes and the worse it is. I then have to stop reading. If I then look away, white lines continue to dance in front of my eyes for about a minute until the effect disappears. <a href="https://indieweb.social/@aworkinglibrary@mstdn.social/110979375072106482">Mandy reported on Mastodon</a> about this halation effect, which in her case is due to astigmatism. I don’t know of any such corneal curvature in my case, but the result seems to be similar.</p>
<span id="more"></span>
<p>The problem is, that <strong>the effect only occurs with text</strong> and I have activated dark mode on all my devices because I generally find it more pleasant. Icons, images and the like look better and it perhaps also saves a bit of power. Just not when I read text longer than a headline!</p>
<hr>
<p>Back to Garrit and roasting him ;)</p>
<p>What really got me going was that he wrote something about “<em>Proud Member of <a href="https://darktheme.club/">darktheme.club</a></em>“ on his blog, which he also founded himself. In the FAQ it says, among other things:</p>
<div>
<blockquote><p>What’s the point of all this?<br>…<br>People with certain visual imparements have a hard time navigating a web without a dark theme. There are plugins that try to emulate a dark theme for some sites, but those are not always accurate.<br>…</p>
</blockquote>
<cite><a href="https://darktheme.club/faq">--- darktheme.club FAQ</a></cite>
</div>
<p>Yes, Garrit. You’re right. But you’re falling a little short here. It’s perfectly fine and almost a must-have nowadays to automatically display a web page to the user in the mode they have set using the CSS media feature <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme"><code>prefers-color-theme</code></a>, but unfortunately that’s not enough. Being a member of a dark theme club and referring to the visually impaired in this way excludes others, like me for example.</p>
<hr>
<h2 id="Simple-Solution"><a href="#Simple-Solution" class="headerlink" title="Simple Solution"></a>Simple Solution</h2><p>Yet it is so easy to SIGNIFICANTLY increase acceptance by simply adding a <strong>customizable theme button</strong> to your own site! People like me almost automatically look in the top right-hand corner of a page (do it now ;) to see whether the almost standardized moon or sun symbol can be found there. Every website operator, who has implemented <code>prefers-color-theme</code> has already written the necessary CSS code. <strong>The ONLY thing left to do is to add a f*** switch for it!</strong> You don’t even need JavaScript for that. It also works with pure CSS, as shown <a href="https://alexandersandberg.com/articles/creating-a-website-theme-switcher-with-css-only/">here</a> and <a href="https://codepen.io/michellebarker/pen/GzzrGJ/">here</a>. Many of the JavaScript solutions also offer the option of saving the selection in the browser’s local storage for the next visit. Your choice…</p>
<a href="https://codepen.io/michellebarker/pen/GzzrGJ/">
<img loading="lazy" src="theme-switcher.png" alt="Theme Switcher Pen from Michelle Barker" />
</a>
<p>In the list of darktheme.club members, however, there are only a handful of websites with the tag <strong>JavaScript</strong> which indicates such an individual theme switcher and I wonder whether this is due to ignorance or lack of knowledge!?<br>Even design experts and proven CSS specialists who constantly write about UI/UX and accessibility (I won’t mention any names) have the audacity to use <code>background-color:#222; color:#fff</code> on their own blogs and sometimes even do without the CSS media feature! NO … THIS IS NOT A COOL DESIGN! I immediately unfollow such people, because there must be more pretense than reality.</p>
<hr>
<h2 id="Be-kind-to-your-readers-…-let-them-choose"><a href="#Be-kind-to-your-readers-…-let-them-choose" class="headerlink" title="Be kind to your readers … let them choose"></a>Be kind to your readers … let them choose</h2><p>I approached a few bloggers and asked if they could add a theme switch and only got the lapidary answer that I could use the OS theme switch. Yes, I can, but do I want to keep fiddling with my settings just because I generally like the dark theme and only need the light theme for reading? No, since such a switch is so easy to implement, I’d rather do without the textual outpourings of these lazy morons … :|</p>
<p>As is so often the case, think of your users and they will be thankful. Maybe you didn’t had this “problem” on your radar and that’s perfectly ok (my site is also far from perfect) … but if my contribution here has given you the impetus to add a switch on your site, then I (and many others too, I’m sure) will be VERY happy about it.</p>
<p>Ok Garrit … now its time to roast me :D</p>
https://kiko.io/post/SVWW-vs-Braunschweig-2023-12-08/SVWW vs. Braunschweig @ 2023-12-082023-12-10T16:15:16.000Z<p><img src="https://kiko.io/images/social-media/SVWW-vs-Braunschweig-2023-12-08.png" /></p>
<div class="float-matchgrid">
<img src="/images/logos/SV-Wehen-Wiesbaden.svg" />
<span>1:3</span>
<img src="/images/logos/Eintracht-Braunschweig.svg" />
</div>
<p>Our last home game of the year. The last two away games against <strong>Greuther Fürth</strong> and <strong>Holstein Kiel</strong> were unfortunately lost. One <a href="https://www.kicker.de/fuerth-gegen-wiesbaden-2023-bundesliga-4861782/analyse">2:0</a> and the other <a href="https://www.kicker.de/kiel-gegen-wiesbaden-2023-bundesliga-4861791/analyse">3:2</a>, so you could say that our good run had come to an end. But well … anyone who thought it would go on like this should have seen a doctor. After all, we’re the promoted team and we have to win against far stronger teams with far more Bundesliga experience. We can only ever catch them at a weak moment and hope that in the end it will be enough to stay in the 2. Bundesliga.</p>
<p><strong>Eintracht Braunschweig</strong> was one of the teams we thought had a chance, as they were in a relegation spot with just 8 points before the matchday (we have 21) and hadn’t won an away game for months.</p>
<span id="more"></span>
<p>It’s already very cold in Wiesbaden at the beginning of December, especially as the weather was bringing in a lot of cold air from the north, so I left the scooter and took a cab to the Brita Arena. Freezing sucks and as I no longer have the heat of the youth, I bought long underwear for winter stadium visits. (Uhh, the old man wears long underpants). I was glad to have them though, because on the one hand I was there way too early and on the other hand you end up sitting for most of the 120 minutes that a game like this lasts, including waiting for kick-off and the interval. The hot cider and warm beef sausages only helped to combat the cold to a limited extent.</p>
<p>Bärbel did better, as she arrived shortly before kick-off, like most of the 7,200 spectators. We chatted a bit about trivialities and hoped that our boys wouldn’t let the opportunity pass them by today.</p>
<hr>
<h2 id="The-Game"><a href="#The-Game" class="headerlink" title="The Game"></a>The Game</h2><p>The coach seemed to have had something similar in mind, because as soon as the ball started rolling, our boys attacked their opponents and had their first 100% chance after just 20 seconds (!). A few minutes later, the ball was actually in the goal, but it was probably disallowed by the referee for offside or something similar. I don’t know but it didn’t matter as long as it continued at this pace. Braunschweig were hopelessly out of their depth and simply tried to prevent the inevitable … until the 18th minute … 1:0 - A fine header by our defender <strong>Aleksandar Vukotic</strong> :)</p>
<p>However, the problem was that our team then let themselves go a little and only benefited from the opponent’s harmlessness until the break. As if the job was already done…</p>
<div class="image-masonry" id="image-masonry-grg7je">
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Braunschweig-2023-12-08/PXL_20231208_165816147.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Braunschweig-2023-12-08/PXL_20231208_170229560.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Braunschweig-2023-12-08/PXL_20231208_170946775.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Braunschweig-2023-12-08/PXL_20231208_172204474.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Braunschweig-2023-12-08/PXL_20231208_172706390.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Braunschweig-2023-12-08/PXL_20231208_173932826.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Braunschweig-2023-12-08/PXL_20231208_174727767.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Braunschweig-2023-12-08/PXL_20231208_183243917.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Braunschweig-2023-12-08/PXL_20231208_184445039.jpg" alt="" />
</div>
</div>
<p>Bärbel and I hoped that the coach in the dressing room would be able to shake the boys up again and they would play the second half with the same energy of the first 20 minutes. But what came was a bitter disappointment. Braunschweig’s coach seemed to have achieved exactly what we had hoped for. Our opponents came onto the pitch and started playing aggressive football. In the 48th minute, the score was 1:1 after a counterattack and only 8 minutes later, Braunschweig had turned the game around: 1:2.</p>
<p>The lack of resistance and insecurity on our side was problematic. Hardly a ball got to where it needed to go, hardly a duel was won and even 5 substitutions unfortunately did nothing to change this. The boys were somehow beside themselves and so Braunschweig put the game to bed in the 76th minute: 1:3 :|</p>
<p>⇾ <a href="https://www.kicker.de/wiesbaden-gegen-braunschweig-2023-bundesliga-4861804/analyse">Match report on kicker.de</a></p>
<hr>
<h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>The stadium has rarely emptied as quickly as it did this time, but that wasn’t just due to the cold, but also the disappointment. Unfortunately, it wasn’t a Christmas present from the players to their fans in the last home game of 2023. The team have to play one last away game next week at St. Pauli before the break, but nobody expects a win against the strongest team in the 2nd division at the moment.</p>
<p>No matter … we are in mid-table, which is more than we had hoped for at the start of the season. In 7 weeks time, we’ll be back in the Brita-Arena against <strong>Herta BSC</strong> and we’ll see better games again and stay in the league!</p>
<a href="https://www.kicker.de/2-bundesliga/tabelle">
<img loading="lazy" src="tabelle-23-12-10.png" alt="Table 2. Liga, Sunday 10. December 2023" />
</a>
https://kiko.io/post/Hexo-Webfinger-and-better-discoverability/Hexo, WebFinger and better discoverability2023-12-02T13:19:03.000Z<p><img src="https://kiko.io/images/social-media/Hexo-Webfinger-and-better-discoverability.png" /></p><p>Recently I read the <a href="https://blog.maartenballiauw.be/post/2022/11/05/mastodon-own-donain-without-hosting-server.html">blog post “Mastodon on your own domain without hosting a server”</a> by <a href="https://mastodon.online/@maartenballiauw">Maarten Balliauw</a>, which dealt with how to become more visible in the Fediverse, more precisely in Mastodon, with <strong>your own domain</strong>, because in contrast to the <strong>Indieweb</strong> approach, the <strong>Fediverse</strong> relies on Actors (<code>@USER@INSTANCE</code>) of the respective instance/platform and can only include your own domain, if it becomes a Fediverse endpoint itself.</p>
<p>In my case, the latter is not possible because this blog is a static site, generated via <a href="https://hexo.io/">Hexo</a> and hosted on GitHub. It simply lacks a modifiable active server component.</p>
<p>However, Maarten has found a trick to at least make it findable in Mastadon via his own domain. First, he explains how Fediverse platforms work in general:</p>
<blockquote>
<p>– Mastodon (and others) use ActivityPub as their protocol to communicate between “actors”.<br>– Actors are discovered using WebFinger, a way to attach information to an email address, or other online resource.<br>– WebFinger lives on /.well-known/webfinger on a server.</p>
</blockquote>
<p>His idea was to simply copy the WebFinger file to his server and make it available in the same way, to allow the Fediverse server to find the correct actor, so search for <code>@me@mydomain.xxx</code> and find <code>@me@my-fediverse-instance.xxx</code>.</p>
<p>Copy a file and deliver it via Hexo over <code>.wellknown/webfinger</code>? What can be so difficult about that…</p>
<span id="more"></span>
<hr>
<h2 id="Download-the-WebFinger-file"><a href="#Download-the-WebFinger-file" class="headerlink" title="Download the WebFinger file"></a>Download the WebFinger file</h2><p>As Maarten describes, the WebFinger file (JSON) can always be found on a URL according to the following pattern:<br><code>https://<your mastodon server>/.well-known/webfinger?resource=acct:<your account>@<your mastodon server></code>.</p>
<p>I have my Mastodon account ‘kiko’ at indieweb.social and the path to the download was accordingly: <a href="https://indieweb.social/.well-known/webfinger?resource=acct:kiko@indieweb.social">https://indieweb.social/.well-known/webfinger?resource=acct:kiko@indieweb.social</a>.</p>
<hr>
<h2 id="Solution-1-Copy-and-provide-as-static-file"><a href="#Solution-1-Copy-and-provide-as-static-file" class="headerlink" title="Solution 1 - Copy and provide as static file"></a>Solution 1 - Copy and provide as static file</h2><p>First of all, Hexo knows nothing about static files. You have to teach it, for example, using the <a href="https://github.com/niahoo/hexo-generator-copy"><strong>hexo-generator-copy</strong> plugin</a>, which I did a long time ago because I deliver a lot of such files, like images, photos, manifests and so on.</p>
<p>Including the WebFinger file in the .wellknown folder in the generation was therefore an obvious choice … but did not work, as the plugin ignores all files and folders that begin with a dot or underscore. This doesn’t matter on Windows, but it probably does on other platforms, so I wanted to leave the plugin’s code untouched and wrote another generator that then generates the file separately into the output:</p>
<pre><div class="caption"><span>generator-wellknown-webfinger.js</span></div><code class="highlight js"><span class="keyword">const</span> log = <span class="built_in">require</span>(<span class="string">'hexo-log'</span>)({ <span class="attr">debug</span>: <span class="literal">false</span>, <span class="attr">silent</span>: <span class="literal">false</span> });
<span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>);
<span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">'hexo-fs'</span>);
hexo.<span class="property">extend</span>.<span class="property">generator</span>.<span class="title function_">register</span>(<span class="string">"wellknown-webfinger"</span>, <span class="keyword">async</span> <span class="keyword">function</span>(<span class="params"></span>) {
log.<span class="title function_">info</span>(<span class="string">"Processing .well-known/webfinger ..."</span>);
<span class="keyword">const</span> _rootPath = hexo.<span class="property">base_dir</span>;
<span class="keyword">const</span> _path = <span class="string">".well-known/webfinger"</span>;
<span class="keyword">let</span> content = <span class="string">""</span>;
<span class="keyword">let</span> filePath = path.<span class="title function_">join</span>(_rootPath, <span class="variable language_">this</span>.<span class="property">config</span>.<span class="property">static_dir</span>, _path);
<span class="keyword">if</span> (fs.<span class="title function_">existsSync</span>(filePath)) {
json = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(fs.<span class="title function_">readFileSync</span>(filePath));
content = <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(json); <span class="comment">// flatten JSON</span>
}
<span class="keyword">let</span> result = {
<span class="attr">data</span>: content,
<span class="attr">path</span>: _path
};
<span class="keyword">return</span> result;
});</code></pre>
<p>The whole thing is not magic, because it simply reads the local file from <code>/static/.wellknown/webfinger</code> and returns the output path and the flattened content to be rendered as the result.</p>
<p>But somehow it didn’t feel right to first download a file manually and then bypass a generator with another generator in order to deliver the file.</p>
<hr>
<h2 id="Solution-2-Download-and-deliver-during-generation"><a href="#Solution-2-Download-and-deliver-during-generation" class="headerlink" title="Solution 2 - Download and deliver during generation"></a>Solution 2 - Download and deliver during generation</h2><p>A short time later, I deleted the local file again and rewrote the generator, because the first solution didn’t make sense to me. Generating my blog now takes less than a minute anyway, so it doesn’t matter whether I fetch the WebFinger file directly from the server and write it straight to the output via <code>hexo.route.set</code>:</p>
<pre><div class="caption"><span>generator-wellknown-webfinger.js</span></div><code class="highlight js"><span class="keyword">const</span> log = <span class="built_in">require</span>(<span class="string">'hexo-log'</span>)({ <span class="attr">debug</span>: <span class="literal">false</span>, <span class="attr">silent</span>: <span class="literal">false</span> });
<span class="keyword">const</span> axios = <span class="built_in">require</span>(<span class="string">"axios"</span>);
hexo.<span class="property">extend</span>.<span class="property">generator</span>.<span class="title function_">register</span>(<span class="string">"wellknown-webfinger"</span>, <span class="keyword">async</span> <span class="keyword">function</span>(<span class="params"></span>) {
log.<span class="title function_">info</span>(<span class="string">"Processing .well-known/webfinger ..."</span>);
<span class="keyword">const</span> url =
<span class="string">`https://<span class="subst">${<span class="variable language_">this</span>.config.mastodon.server}</span>/.well-known/webfinger?resource=acct:<span class="subst">${<span class="variable language_">this</span>.config.mastodon.user}</span>@<span class="subst">${<span class="variable language_">this</span>.config.mastodon.server}</span>`</span>;
<span class="keyword">const</span> _path = <span class="string">".well-known/webfinger"</span>;
axios.<span class="title function_">get</span>(url).<span class="title function_">then</span>(<span class="function"><span class="params">response</span> =></span> {
<span class="keyword">let</span> json = response.<span class="property">data</span>;
hexo.<span class="property">route</span>.<span class="title function_">set</span>(_path, json);
});
});</code></pre>
<hr>
<h2 id="The-Result"><a href="#The-Result" class="headerlink" title="The Result"></a>The Result</h2><p>After deploying to GitHub I was able to search on a Fediverse platform for <code>@kristof@kiko.io</code> and the result is the corresponding account of my Mastodon instance. :)</p>
<p><img src="/post/Hexo-Webfinger-and-better-discoverability/mastodon-search.png" alt="Search on Mastodon"></p>
<p>However, this currently only works with one instance and the user specification is arbitrary (as Maarten also notes). For example, if I search on Pixelfed, my Mastodon account is also displayed, but not my Pixelfed account, which is also available there, because I have not integrated Pixelfeds WebFinger file.</p>
<p><img src="/post/Hexo-Webfinger-and-better-discoverability/pixelfed-search.png" alt="Search on Pixelfed"></p>
<p>As mentioned above, the Fediverse works a little differently than the Indieweb. My wish would be to find something that identifies me with my domain everywhere.</p>
<hr>
<h2 id="More-Info"><a href="#More-Info" class="headerlink" title="More Info"></a>More Info</h2>
<ul class="moreinfo-list">
<li>IndieWebCamp: <a href="https://indieweb.org/WebFinger">WebFinger</a></li><li>Brandon Rozek: <a href="https://brandonrozek.com/blog/mastodon-webfinger-alias-using-redirects/">Mastodon/Webfinger Alias using HTTP Redirects</a></li><li>GitHub Issue from r3pek at webfinger.net: <a href="https://github.com/webfinger/webfinger.net/issues/26">Is webfinger able to distinguish between 2 services on the same URI?</a></li>
</ul>
https://kiko.io/post/Mecklenburg-Lakes/Mecklenburg Lakes2023-11-26T13:59:17.000Z<p><img src="https://kiko.io/images/social-media/Mecklenburg-Lakes.png" /></p><p>August in Germany is usually a safe bet when it comes to the weather, so my wife and I decided to relax for a few days on the <a href="https://en.wikipedia.org/wiki/Mecklenburg_Lake_Plateau">Mecklenburg Lake Plateau</a> … on a chartered boat. In general, you need a driving license for everything that moves in this country, but there are a few exceptions, such as in Brandenburg/Mecklenburg where even inexperienced tourists are allowed to steer a 12-metre houseboat/yacht across the many lakes after a short briefing.</p>
<p>The weather was, let’s say, suboptimal this August, because a storm passed over us in the first three days and there was no chance of happy sailing around on the water. On the one hand, the swell and wind forces were extremely worrying for newbies and on the other, it’s simply no fun being on a boat in cold, heavy rain. We were stuck in Rheinsberg. On the one hand, the swell and wind forces were extremely worrying for newbies and on the other, it’s simply no fun being on a boat in cold, heavy rain.</p>
<span id="more"></span>
<p>Not only because it felt like luxury camping without people but only water around, but also because it gave a pleasant feeling of freedom. Yes, we weren’t nearly alone on the lakes and getting a good spot in a marina for electricity and fresh water wasn’t always easy, but then just anchoring in any lake for the night and being woken up by birdsong and a gentle swell was magical.</p>
<p>Below are a few pictures from this trip, some of which will be used again as hero images in this blog:</p>
<div class="photo-list" id="photo-list-nk1c26">
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0040" class="no-break">
<img loading="lazy" src="/photos/normal/23-08-Mecklenburg-Seen-0040.jpg">
</a>
<figcaption>Grinning Horse</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0062" class="no-break">
<img loading="lazy" src="/pool/23-08-Mecklenburg-Seen-0062/normal.jpg">
</a>
<figcaption>Sea Magic</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0154" class="no-break">
<img loading="lazy" src="/shed/23-08-Mecklenburg-Seen-0154/normal.jpg">
</a>
<figcaption>Rainy Tourism</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0160" class="no-break">
<img loading="lazy" src="/pool/23-08-Mecklenburg-Seen-0160/normal.jpg">
</a>
<figcaption>Rainy Planks</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0186" class="no-break">
<img loading="lazy" src="/pool/23-08-Mecklenburg-Seen-0186/normal.jpg">
</a>
<figcaption>Chrome Bollard</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0202" class="no-break">
<img loading="lazy" src="/shed/23-08-Mecklenburg-Seen-0202/normal.jpg">
</a>
<figcaption>Rheinsberg Palace</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0204" class="no-break">
<img loading="lazy" src="/pool/23-08-Mecklenburg-Seen-0204/normal.jpg">
</a>
<figcaption>Pink Lotus</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0211" class="no-break">
<img loading="lazy" src="/pool/23-08-Mecklenburg-Seen-0211/normal.jpg">
</a>
<figcaption>Stony Vegetation</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0216" class="no-break">
<img loading="lazy" src="/pool/23-08-Mecklenburg-Seen-0216/normal.jpg">
</a>
<figcaption>Golden Drama</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0229" class="no-break">
<img loading="lazy" src="/shed/23-08-Mecklenburg-Seen-0229/normal.jpg">
</a>
<figcaption>Buddy Support</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0256" class="no-break">
<img loading="lazy" src="/shed/23-08-Mecklenburg-Seen-0256/normal.jpg">
</a>
<figcaption>Stormy Lake</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0275" class="no-break">
<img loading="lazy" src="/photos/normal/23-08-Mecklenburg-Seen-0275.jpg">
</a>
<figcaption>Green Canal</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0323" class="no-break">
<img loading="lazy" src="/pool/23-08-Mecklenburg-Seen-0323/normal.jpg">
</a>
<figcaption>Boiler Mirroring</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0335" class="no-break">
<img loading="lazy" src="/pool/23-08-Mecklenburg-Seen-0335/normal.jpg">
</a>
<figcaption>Into the Storm</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0341" class="no-break">
<img loading="lazy" src="/pool/23-08-Mecklenburg-Seen-0341/normal.jpg">
</a>
<figcaption>Sunset Jetty</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0343" class="no-break">
<img loading="lazy" src="/pool/23-08-Mecklenburg-Seen-0343/normal.jpg">
</a>
<figcaption>Golden Surfing</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0408" class="no-break">
<img loading="lazy" src="/pool/23-08-Mecklenburg-Seen-0408/normal.jpg">
</a>
<figcaption>Blue Lake</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0432" class="no-break">
<img loading="lazy" src="/pool/23-08-Mecklenburg-Seen-0432/normal.jpg">
</a>
<figcaption>Green Ducks</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0433" class="no-break">
<img loading="lazy" src="/pool/23-08-Mecklenburg-Seen-0433/normal.jpg">
</a>
<figcaption>Sparkling Duck</figcaption>
</figure>
<figure>
<a href="/photos/23-08-Mecklenburg-Seen-0468" class="no-break">
<img loading="lazy" src="/pool/23-08-Mecklenburg-Seen-0468/normal.jpg">
</a>
<figcaption>Mirrored Boathouses</figcaption>
</figure>
</div>
https://kiko.io/post/Discoveries-27-JavaScript-Tools/Discoveries #27 - JavaScript Tools2023-11-20T14:16:39.000Z<p><img src="https://kiko.io/images/social-media/Discoveries-27-JavaScript-Tools.png" /></p><p>This month's Discoveries are about small, large, new and somewhat older JavaScript tools that can make life and coding easier for developers. Why reinvent the wheel when someone has already done it. Happy Coding :)</p>
<ul class="anchorlist">
<li data-anchor="#spacing-js-measuring">Spacing.js - Measuring</li><li data-anchor="#ukiyo-js-parallax-effect">Ukiyo.js - Parallax Effect</li><li data-anchor="#intersectionobserver-debugger">IntersectionObserver Debugger</li><li data-anchor="#mande-fetch-wrapper">mande - Fetch Wrapper</li><li data-anchor="#vest-declarative-validations-framework">Vest - Declarative Validations Framework</li><li data-anchor="#granim-js-gradient-animation">Granim.js - Gradient Animation</li><li data-anchor="#roughnotation">RoughNotation</li><li data-anchor="#barba-js-page-transitions">barba.js - Page Transitions</li><li data-anchor="#dead-or-alive-url-checker">dead-or-alive - URL Checker</li><li data-anchor="#timeago-format-date">timeago - Format Date</li>
</ul>
<span id="more"></span>
<hr id="spacing-js-measuring"/>
<h2 style="margin-bottom: 10px;">
Spacing.js - Measuring
</h2>
<small>by Steven Lei <br><a href="https://spacingjs.com/">https://spacingjs.com/</a></small>
<p>Have you ever wanted to measure your web layout during development? Steven has a solution for you. Either as JavaScript integrated in HTML or as a Chrome plugin.</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://spacingjs.com/">
<img class="limit" src="spacing-js-measuring.png" alt="Spacing.js - Measuring" />
</a>
<hr id="ukiyo-js-parallax-effect"/>
<h2 style="margin-bottom: 10px;">
Ukiyo.js - Parallax Effect
</h2>
<small>by Yiteng Jun <br><a href="https://github.com/yitengjun/ukiyo-js">https://github.com/yitengjun/ukiyo-js</a></small>
<p>This ES6 library is an easy to use tool for creating efficient background parallax effects for <img>, <picture>, <video> elements and background images. It can be called manually via JavaScript or automatically via a special class or data attribute.</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://github.com/yitengjun/ukiyo-js">
<img class="limit" src="ukiyo-js-parallax-effect.png" alt="Ukiyo.js - Parallax Effect" />
</a>
<hr id="intersectionobserver-debugger"/>
<h2 style="margin-bottom: 10px;">
IntersectionObserver Debugger
</h2>
<small>by Rodrigo Pombo <br><a href="https://github.com/pomber/intersection-observer-debugger">https://github.com/pomber/intersection-observer-debugger</a></small>
<p>This tiny script is a debugging tool, included during development, which shows you the root, target, and intersection every time an IntersectionObserver is triggered.</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://github.com/pomber/intersection-observer-debugger">
<img class="limit" src="intersectionobserver-debugger.png" alt="IntersectionObserver Debugger" />
</a>
<hr id="mande-fetch-wrapper"/>
<h2 style="margin-bottom: 10px;">
mande - Fetch Wrapper
</h2>
<small>by Eduardo San Martin Morote <br><a href="https://github.com/posva/mande">https://github.com/posva/mande</a></small>
<p>With mande Eduardo has written a great wrapper for JavaScripts fetch(), which not only relieves you of a lot of typing work, but also comes with a few useful extensions, e.g. for nuxt. This turns an API call into a one-liner.</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://github.com/posva/mande">
<img class="limit" src="mande-fetch-wrapper.png" alt="mande - Fetch Wrapper" />
</a>
<hr id="vest-declarative-validations-framework"/>
<h2 style="margin-bottom: 10px;">
Vest - Declarative Validations Framework
</h2>
<small>by Evyatar Alush <br><a href="https://github.com/ealush/vest">https://github.com/ealush/vest</a></small>
<p>Vest is a declarative validation framework for validating form input that is as easy to use as the Mocha or Jest unit test libraries.</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://github.com/ealush/vest">
<img class="limit" src="vest-declarative-validations-framework.png" alt="Vest - Declarative Validations Framework" />
</a>
<hr id="granim-js-gradient-animation"/>
<h2 style="margin-bottom: 10px;">
Granim.js - Gradient Animation
</h2>
<small>by Benjamin Blonde <br><a href="https://sarcadass.github.io/granim.js/examples.html">https://sarcadass.github.io/granim.js/examples.html</a></small>
<p>Benjamin has created a library to animate gradients as complex as you need them. It supports pausing when it's not in view and different blending modes for images.</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://sarcadass.github.io/granim.js/examples.html">
<img class="limit" src="granim-js-gradient-animation.png" alt="Granim.js - Gradient Animation" />
</a>
<hr id="roughnotation"/>
<h2 style="margin-bottom: 10px;">
RoughNotation
</h2>
<small>by Preet Shihn <br><a href="https://roughnotation.com/">https://roughnotation.com/</a></small>
<p>To emphasize something important on a sheet of paper, you sometimes use a highlighter. This library brings that to the web in an animated way. With a circle around it, underlined … or both, you're sure to attract attention.</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://roughnotation.com/">
<img class="limit" src="roughnotation.png" alt="RoughNotation" />
</a>
<hr id="barba-js-page-transitions"/>
<h2 style="margin-bottom: 10px;">
barba.js - Page Transitions
</h2>
<small>by Multiple Contributors <br><a href="https://barba.js.org/features/user-friendly/">https://barba.js.org/features/user-friendly/</a></small>
<p>Barba is a library that allows you to create fluid and smooth transitions between pages on your website, while we wait for the <a href="https://caniuse.com/?search=View+Transitions+API">View Transitions API</a>.</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://barba.js.org/features/user-friendly/">
<img class="limit" src="barba-js-page-transitions.png" alt="barba.js - Page Transitions" />
</a>
<hr id="dead-or-alive-url-checker"/>
<h2 style="margin-bottom: 10px;">
dead-or-alive - URL Checker
</h2>
<small>by Titus Wormer <br><a href="https://github.com/wooorm/dead-or-alive">https://github.com/wooorm/dead-or-alive</a></small>
<p>This URL checker ensures that links do not lead to nothing, whether on websites, in node projects or in service workers. It even checks anchor links for the presence of the element.</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://github.com/wooorm/dead-or-alive">
<img class="limit" src="dead-or-alive-url-checker.png" alt="dead-or-alive - URL Checker" />
</a>
<hr id="timeago-format-date"/>
<h2 style="margin-bottom: 10px;">
timeago - Format Date
</h2>
<small>by Whoever Titanium is... <br><a href="https://github.com/Titanium2099/timeago">https://github.com/Titanium2099/timeago</a></small>
<p>'Your message was sent 5 minutes ago'. Notes like these are easy to bring into a web application with this script. It currently only supports English, but is easily customizable.</p>
<a style="display:block; margin: 10px 0 30px;" class="img-link" href="https://github.com/Titanium2099/timeago">
<img class="limit" src="timeago-format-date.png" alt="timeago - Format Date" />
</a>
https://kiko.io/post/Speyer-Automotive/Speyer Automotive2023-11-15T15:13:13.000Z<p><img src="https://kiko.io/images/social-media/Speyer-Automotive.png" /></p><p>In September, my wife and I took a trip to the <a href="https://en.wikipedia.org/wiki/Technik_Museum_Speyer">Museum of Technology in nearby Speyer</a>. I don’t really know why I’ve never been there before, not even as a child, because the collection of cars and aeroplanes is quite unique in Germany. A dream for every boy, even if he’s not as young as he might think :)</p>
<p>Built on the old site of an aircraft manufacturer, the 100-year-old hangars provide the perfect space for exhibiting large vehicles with wheels and wings. And exactly this can be found in Speyer: A large part for cars of all kinds and the outside area for aeroplanes and even ships. Even a spaceship, the Russian Space Shuttle alternative <a href="https://en.wikipedia.org/wiki/Buran_programme">Buran</a> can be seen there.</p>
<p>Here are a few pictures of the car exhibition that I brought back from there and which will be used as post header images hero on kiko.io in the future …</p>
<span id="more"></span>
<div class="photo-list" id="photo-list-4esfqd">
<figure>
<a href="/photos/23-09-11-Speyer-0001" class="no-break">
<img loading="lazy" src="/photos/normal/23-09-11-Speyer-0001.jpg">
</a>
<figcaption>Red Bull 12</figcaption>
</figure>
<figure>
<a href="/photos/23-09-11-Speyer-0006" class="no-break">
<img loading="lazy" src="/pool/23-09-11-Speyer-0006/normal.jpg">
</a>
<figcaption>Packard Monster</figcaption>
</figure>
<figure>
<a href="/photos/23-09-11-Speyer-0007" class="no-break">
<img loading="lazy" src="/pool/23-09-11-Speyer-0007/normal.jpg">
</a>
<figcaption>Golden Cooler</figcaption>
</figure>
<figure>
<a href="/photos/23-09-11-Speyer-0008" class="no-break">
<img loading="lazy" src="/pool/23-09-11-Speyer-0008/normal.jpg">
</a>
<figcaption>Vespa Treasure</figcaption>
</figure>
<figure>
<a href="/photos/23-09-11-Speyer-0022" class="no-break">
<img loading="lazy" src="/photos/normal/23-09-11-Speyer-0022.jpg">
</a>
<figcaption>Yellow Curves</figcaption>
</figure>
<figure>
<a href="/photos/23-09-11-Speyer-0029" class="no-break">
<img loading="lazy" src="/pool/23-09-11-Speyer-0029/normal.jpg">
</a>
<figcaption>Red Motor Cover</figcaption>
</figure>
<figure>
<a href="/photos/23-09-11-Speyer-0031" class="no-break">
<img loading="lazy" src="/pool/23-09-11-Speyer-0031/normal.jpg">
</a>
<figcaption>Maybach Zeppelin</figcaption>
</figure>
<figure>
<a href="/photos/23-09-11-Speyer-0041" class="no-break">
<img loading="lazy" src="/pool/23-09-11-Speyer-0041/normal.jpg">
</a>
<figcaption>Mercedes Gold</figcaption>
</figure>
<figure>
<a href="/photos/23-09-11-Speyer-0048" class="no-break">
<img loading="lazy" src="/pool/23-09-11-Speyer-0048/normal.jpg">
</a>
<figcaption>Magirus Red</figcaption>
</figure>
</div>
https://kiko.io/post/SVWW-vs-Kaiserslautern-2023-11-12/SVWW vs. Kaiserslautern @ 2023-11-122023-11-15T13:12:00.000Z<p><img src="https://kiko.io/images/social-media/SVWW-vs-Kaiserslautern-2023-11-12.png" /></p>
<div class="float-matchgrid">
<img src="/images/logos/SV-Wehen-Wiesbaden.svg" />
<span>2:1</span>
<img src="/images/logos/1-FC-Kaiserslautern.svg" />
</div>
<p>Travelling by scooter in Germany in the middle of November is no fun. Just like on Sunday. Light rain at just under 9 degrees Celsius and quite a bit of wind. Brrr… but what can I do, there are no car parks at the arena. Not at all today, because it’s against Kaiserslautern, which is only about 100 kilometres away from Wiesbaden and therefore a lot of visitors are expected. It’s a kind of derby.</p>
<p><strong>1. FC Kaiserslautern</strong> is one of the great traditional clubs in Germany, which has celebrated a total of 4 German Championships in its history, but has really lost momentum since its relegation to the 2. Bundesliga in 2006. Between 2018 and 2022, the team even only played in the 3rd division pf German football, but after their last year’s promotion, they are hoping to be promoted to the top division again soon. Recent results in the cup have shown that they have a strong team and it was to be feared that they would overrun Wehen Wiesbaden in their run, although their last league game was lost against Fürth.</p>
<p>It was also rather uncomfortably cold in the stadium and I wondered once again how the young lads do it on the pitch in their shorts, especially the goalkeeper.</p>
<p>But the fans created the right atmosphere. As expected, the entire guest stand was full. 12,100 spectators in total; a new season record. The SVWW fan block was also packed this time and the ultras had come up with a nice choreo with complete stand banners and red smoke (see photos).</p>
<span id="more"></span>
<p>My nice seat neighbour, the older lady, was there again and we made friends this time. Her name is Bärbel and this time she was a bit more excited than normally. Well, the aim was to beat Kaiserslautern. Kaiserslautern! Every now and then she also started to grumble: about an opposing player or when things didn’t go fast enough forwards or when the ball was lost unnecessarily. Not nearly as blatant as the “gentlemen” behind us, who were swearing in one piece and indulging in football wisdom, but you could tell that the game was more important to her than others.</p>
<hr>
<h2 id="The-Game"><a href="#The-Game" class="headerlink" title="The Game"></a>The Game</h2><p>In the first half, the game was rather well balanced, even if you had the feeling that our team was the only ones playing. There was no sign of Kaiserlautern’s attacking drive. Up until the 39th minute, we had one chance to score, but the visitors hadn’t even had a shot on our goal. Nothing, nada, niente. But then the ball was in our net: <strong>0:1</strong>! A strange goal, because it didn’t look intentional. The ball just bounced at Ritter’s feet by chance and he simply took a shot. Goal. Damn…</p>
<div class="image-masonry" id="image-masonry-ot4zbk">
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Kaiserslautern-2023-11-12/PXL_20231112_120526904.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Kaiserslautern-2023-11-12/PXL_20231112_122110857.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Kaiserslautern-2023-11-12/PXL_20231112_122854839.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Kaiserslautern-2023-11-12/PXL_20231112_122739398.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Kaiserslautern-2023-11-12/PXL_20231112_122947255.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Kaiserslautern-2023-11-12/PXL_20231112_135210240.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Kaiserslautern-2023-11-12/PXL_20231112_141045409.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Kaiserslautern-2023-11-12/PXL_20231112_142300311.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Kaiserslautern-2023-11-12/PXL_20231112_142353462.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Kaiserslautern-2023-11-12/PXL_20231112_142646533.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Kaiserslautern-2023-11-12/PXL_20231112_142730319.jpg" alt="" />
</div>
<div>
<img loading="lazy" class="no-caption" src="/post/SVWW-vs-Kaiserslautern-2023-11-12/PXL_20231112_142841643.jpg" alt="" />
</div>
</div>
<p>After the break, Kaiserslautern seemed to want to build on the goal. They were now playing football. And ours were probably angry at not having been rewarded for their efforts in the first half. The game became more lively, but we still had the better scenes … and after just 4 minutes, the visitors gave our <strong>Tijmen Goppel</strong> far too much time to aim and shoot just outside the penalty area and the ball was deflected into the goal by an opponent’s leg. <strong>1:1</strong>!</p>
<p>In the 65th minute, things got curious again: Goppel was fouled and awarded a free kick about 20 metres from goal. Robin Heußer took it and his shot somehow hit the chest of <strong>Ivan Prtajin</strong> in the penalty area, from where the ball went into the goal. <strong>2:1</strong> … Game turned round!</p>
<p>The last half hour was really nerve-wracking. We continued to play better, but the pressure increased and the game became noticeably faster. Shortly before the end, substitute <strong>John Iredale</strong> ran alone with the ball across half the pitch and everyone held their breath, but just before the goal he tried to round the last defender and he was just able to scoop the ball away. G… damn it!</p>
<p>⇾ <a href="https://www.kicker.de/wiesbaden-gegen-klautern-2023-bundesliga-4861776/analyse">Match report on kicker.de</a></p>
<hr>
<h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>This was the fourth (!) game in a row that we have won and our position in the table looks correspondingly favourable again. Only three points behind the promotion places. Woohoo … but let’s not fool ourselves: There will be another losing streak in the future (as always in football) and 21 points are still not enough to keep us in the league, and that’s all that matters.</p>
<p>Two away games are scheduled in the next three weeks, against <strong>Greuther Fürth</strong> and <strong>Holstein Kiel</strong>. On 8 December, we return to the Brita Arena against <strong>Eintracht Braunschweig</strong>, currently second last in the table. They should be beatable…</p>
<a href="https://www.kicker.de/2-bundesliga/tabelle">
<img loading="lazy" src="tabelle-23-11-12.png" alt="Table 2. Liga, Sunday 12. November 2023" />
</a>
https://kiko.io/post/Add-Link-to-Trello-on-Android-via-Share-Menu/Add Link to Trello on Android via Share Menu2023-11-13T16:11:47.000Z<p><img src="https://kiko.io/images/social-media/Add-Link-to-Trello-on-Android-via-Share-Menu.png" /></p><p>I have been collecting interesting links in various Trello boards for many years and also process some of them automatically, such as my <a href="/collections/tiny-tools">Tiny Tools</a>.</p>
<p>However, the official Trello Android App has the problem eversince that, when you want to create a URL as a Trello card in the browser, the URL is entered in the <code>Description</code> and not as an <code>Attachment</code>, where it actually belongs.</p>
<p><img src="/post/Add-Link-to-Trello-on-Android-via-Share-Menu/share-trello-app.png" alt="Share Link with Trello App"></p>
<p>I have solved this for myself for years using a <strong>bookmarklet</strong> in the Chrome browser (see <a href="/post/Add-website-to-Trello-card-the-better-way/" title="Add website to Trello card the better way">Add website to Trello card the better way</a>) and get along quite well with it. However, there is one catch, that has annoyed me ever since:<br>I find an interesting link in the Mastodon WebApp, for example, and tap on it. What opens, however, is the WebView integrated in Android and not my standard Chrome browser, in which the bookmarklet would be available. So, for links that I want to store, I always have to open the WebView menu and select “Open in Chrome Browser”. I cannot use the general SHARE menu. At least not so far … :)</p>
<span id="more"></span>
<p>The bookmarklet does nothing more as sending the link with some additional information like <code>name</code> (title) to the URL <a href="https://trello.com/add-card">https://trello.com/add-card</a>, where the user can select the desired board and list:</p>
<pre><div class="caption"><span>Trello-AddCard-Bookmarklet.js</span></div><code class="highlight js"><span class="attr">javascript</span>: (<span class="keyword">function</span> (<span class="params">win, name, desc</span>) {
win.<span class="title function_">open</span>(
<span class="string">"https://trello.com/add-card"</span> +
<span class="string">"?source="</span> +
win.<span class="property">location</span>.<span class="property">host</span> +
<span class="string">"&mode=popup"</span> +
<span class="string">"&url="</span> +
<span class="built_in">encodeURIComponent</span>(win.<span class="property">location</span>.<span class="property">href</span>) +
(name ? <span class="string">"&name="</span> + <span class="built_in">encodeURIComponent</span>(name) : <span class="string">""</span>) +
(desc ? <span class="string">"&desc="</span> + <span class="built_in">encodeURIComponent</span>(desc) : <span class="string">""</span>),
<span class="string">"add-trello-card"</span>,
<span class="string">"width=500,height=600,left="</span> +
(win.<span class="property">screenX</span> + (win.<span class="property">outerWidth</span> - <span class="number">500</span>) / <span class="number">2</span>) +
<span class="string">",top="</span> +
(win.<span class="property">screenY</span> + (win.<span class="property">outerHeight</span> - <span class="number">740</span>) / <span class="number">2</span>)
);
})(<span class="variable language_">window</span>, <span class="variable language_">document</span>.<span class="property">title</span>, getSelection ? <span class="title function_">getSelection</span>().<span class="title function_">toString</span>() : <span class="string">""</span>);</code></pre>
<p>If you want to use this bookmarklet, you have to remove all line breaks from the code, decode the url and save it as a bookmark … or you visit <a href="https://trello.com/add-card">https://trello.com/add-card</a> and drag the link “Send to Trello” to your bookmark list. It comes out the same.</p>
<p><img src="/post/Add-Link-to-Trello-on-Android-via-Share-Menu/trello-add-page.png" alt="Create Link Card via Trello Add Card Page"></p>
<p>As I said above, the Trello Bookmarklet e.g. Add Card page works differently to the Android app from the same company in terms of the URL. Are different departments not talking to each other?</p>
<p>However … for me, the bookmarklet is history, because there is a much better approach that uses the Add Card page also, but can be called up from the standard Android Share Menu.</p>
<hr>
<h2 id="Power-Tool-HTTP-Shortcuts"><a href="#Power-Tool-HTTP-Shortcuts" class="headerlink" title="Power Tool: HTTP Shortcuts"></a>Power Tool: HTTP Shortcuts</h2><p>The trigger for my new solution was my search for better interaction possibilities from my blog to the IndieWeb. One hit was the article <a href="https://snarfed.org/android-indieweb-interactions-with-the-http-shortcuts-app">Android IndieWeb interactions with the HTTP Shortcuts app</a> by Ryan Barrett, which uses the app <a href="https://http-shortcuts.rmy.ch/"><strong>HTTP Shortcuts</strong></a> by <strong>Roland Meyer</strong> to serve as a <strong>Share Target</strong> for LIKE, FOLLOW and REPLY actions. </p>
<p>Share Target? A while ago I had the idea of using the <a href="https://developer.chrome.com/articles/web-share-target/">Web Share Target API</a> to solve my problem above, but haven’t got round to setting up such a PWA yet.</p>
<p>But I no longer have to do that, because the HTTP Shortcuts app not only offers me the option of serving as a share target, but also significantly more functions for creating Android shortcuts, which are extremely useful:</p>
<ul>
<li><strong>Browser Shortcut</strong> - Open the URL in the browser</li>
<li><strong>Scripting Shortcut</strong> - Write JavaScript for advanced workflows</li>
<li><strong>Multi-Shortcut</strong> - Trigger multiple shortcuts at once</li>
</ul>
<p>All these shortcuts can be used to send any HTTP requests and process the responses in a variety of ways. The feature list is impressive and well <a href="https://http-shortcuts.rmy.ch/documentation">documented</a>.</p>
<h3 id="Scripting-Shortcut-for-calling-Trello’s-Add-Card-Page"><a href="#Scripting-Shortcut-for-calling-Trello’s-Add-Card-Page" class="headerlink" title="Scripting Shortcut for calling Trello’s Add Card Page"></a>Scripting Shortcut for calling Trello’s Add Card Page</h3><p>For my case, I need a scripting shortcut to process the information shared by the Android sharing dialogue to call the Add Card page. This is done via static variables that are created in HTTP Shortcuts and can then be used later in the script:</p>
<p><img src="/post/Add-Link-to-Trello-on-Android-via-Share-Menu/httpshortcuts-variables.png" alt="Setting Up Variables"></p>
<p>Creating the shortcut is quite simple, as you can see from the screenshots. Select the type, assign a name, perhaps a suitable icon and (importantly) tick the option <strong>Show as app shortcut on launcher and promote as Direct Share target</strong>, which ensures that the shortcut appears in the Android Share menu.</p>
<p><img src="/post/Add-Link-to-Trello-on-Android-via-Share-Menu/httpshortcuts-shortcut.png" alt="Shortcuts List and Creating/Editing Shortcut"></p>
<p><img src="/post/Add-Link-to-Trello-on-Android-via-Share-Menu/httpshortcuts-shortcut-settings.png" alt="Scripting Shortcut Settings"></p>
<p>The script is no less straightforward, because it simply assembles the URL to the Trello Add Card page with the static variables and opens it using the app’s built-in JavaScript function <code>openUrl</code>:</p>
<pre><code class="highlight js"><span class="keyword">const</span> url = <span class="title function_">getVariable</span>(<span class="string">"SharedUrl"</span>);
<span class="keyword">const</span> title = <span class="title function_">getVariable</span>(<span class="string">"SharedTitle"</span>);
<span class="keyword">const</span> <span class="title function_">getHostnameFromRegex</span> = (<span class="params">url</span>) => {
<span class="keyword">const</span> matches = url.<span class="title function_">match</span>(<span class="regexp">/^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i</span>);
<span class="keyword">return</span> matches && matches[<span class="number">1</span>];
}
<span class="keyword">const</span> hostname = <span class="title function_">getHostnameFromRegex</span>(url);
<span class="keyword">const</span> trelloAddUrl =
<span class="string">"https://trello.com/add-card?source="</span> + hostname +
<span class="string">"&mode=popup&url="</span> + <span class="built_in">encodeURIComponent</span>(url) +
<span class="string">"&name="</span> + <span class="built_in">encodeURIComponent</span>(title);
<span class="title function_">openUrl</span>(trelloAddUrl);</code></pre>
<hr>
<h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>The result is convincing and frees me from the bookmarklet on my smartphone, but I will continue to use it on my desktop browser.</p>
<p class="download-link">
<a class="button" href="AddToTrello-Shortcut.zip" download>
Download Shortcut Files for Import in HTTP Shortcut <strong>AddToTrello-Shortcut.zip</strong>
</a>
</p>
<p>The HTTP Shortcuts app, however, has really got me hooked. I see a lot of potential for automating things on my smartphone and will keep you updated as I develop more solutions with it. Thank you Roland for this gem …</p>
<p><img src="/post/Add-Link-to-Trello-on-Android-via-Share-Menu/httpshortcuts-share.png" alt="Shortcut as Share Target"></p>
https://kiko.io/post/Experimenting-with-the-font-LEXEND/Experimenting with the font LEXEND2023-11-12T17:24:00.000Z<p><img src="https://kiko.io/images/social-media/Experimenting-with-the-font-LEXEND.png" /></p><p>A few weeks ago, a blog entry (I can’t remember which one) drew my attention to a font called <a href="https://www.lexend.com/"><strong>LEXEND</strong></a>. In this article, the author also went into the scientific background of the font, which was developed to simplify reading and thus support those with reading difficulties. The website <a href="https://www.lexend.com/">lexend.com</a>, operated by The Lexend Group, therefore also advertises the font with all kinds of reading statistics, although it is open source and is also freely available via Google Fonts.</p>
<p><img src="/post/Experimenting-with-the-font-LEXEND/lexend.png" alt="Lexend"></p>
<p>I simply liked the font style and wanted to try it out on this blog, which has always used the <a href="https://www.opensans.com/">Open Sans</a> font. It was harder than I thought…</p>
<span id="more"></span>
<hr>
<h2 id="The-Download-Odyssey"><a href="#The-Download-Odyssey" class="headerlink" title="The Download Odyssey"></a>The Download Odyssey</h2><p>At the bottom of the one-pager website lexend.com there is a form for the first name and e-mail address and a button “Subscribe & Send me these Fonts”. Okay … Seriously? Fuel for the spam machine? But my spam filter seems to be good enough and so I had the mail sent to me, only to be offered a file called <code>readexpro-master.zip</code> for download via the tracker button in the mail called “Download the Latest Fonts”. Again … Seriously? I expect the Lexend font files and I get something else, without any hint or description? How quickly do the people at The Lexend Group want to destroy their reputation on the net? Where was the UNSUBSCRIBE link in the mail again?<br>But of course I could have used the less prominent <a href="https://fonts.google.com/?query=lexend">link to Google Fonts</a> below the sample image in the mail, which actually leads to the expected font. But what the heck is ReadExPro? Research…</p>
<p>The font was designed in 2018 by Bonnie Shaver-Troup and Thomas Jockin and is based on the Quicksand project by Andrew Paglinawan, which was initiated in 2008 and improved by Thomas Jockin for Google Fonts in 2016. However, lexend.com only mentions Shaver-Troup and her team, who started working on Lexend with Google in 2017. In 2021, Thomas Jockin forked the project and worked with Nadine Chahine on an expansion for Arabic and renamed the font <strong>Readex Pro</strong>.</p>
<p>Ah … ok. And why doesn’t anyone tell you that on the Lexend site? So Lexend is only one half of the coin and Readex Pro makes it a whole? Why two names for it? And why do you get one when you expect the other? Mysterious and, in times of all kinds of dangers on the Internet, simply bordering on the dubious.</p>
<p>But unfortunately this is not the end of the inconsistencies. Since the files in my download of ReadEx Pro are quite large and I have no need for Arabic characters, I downloaded the Lexend font package from <a href="https://fonts.google.com/specimen/Lexend"><strong>Google Fonts</strong></a>, which ONLY contains TTF files and no WOFF2 (Webfonts), the compressed version. Google itself writes the following in its own <a href="https://fonts.google.com/knowledge/glossary/web_font">Glossary of Web Fonts</a>, but then refrains from supplying them? Another: Seriously?</p>
<blockquote>
<p>WOFF (and its successor WOFF2) are compressed file formats created specifically for web fonts. Although regular OpenType fonts (TTF and OTF files) can be used as web fonts, such usage is not recommended as it usually contravenes licence agreements-and the files are significantly larger.</p>
</blockquote>
<p>However, if you embed the font directly from Google Fonts instead of downloading it, only WOFF2 is delivered and TTF is out of the picture. It may be a consistent approach by Google to offer as much as possible only online in order to be able to better analyse the traffic behind it, but especially in Germany, where a <a href="https://inplp.com/latest-news/article/the-year-of-google-fonts-warning-letters">wave of warnings</a> has swept over website operators in the last two years precisely because of this data collection via Google Fonts, this leaves a bitter aftertaste and forces developers to do additional work where it would not be necessary.</p>
<h3 id="Convert-TTF-to-WOFF2"><a href="#Convert-TTF-to-WOFF2" class="headerlink" title="Convert TTF to WOFF2"></a>Convert TTF to WOFF2</h3><p>A short detour …</p>
<p>The Web Open Font Format (WOFF) has been around since 2012. The second version (WOFF2), in which the extremely efficient Brotli compression method is used, was drafted in 2014. All browsers support WOFF2 nowadays, so there is no longer any need to include WOFF, as is often still the case. The same applies to other historical and dead formats EOT and SVG.</p>
<p>So if you have a font as TTF and want to use it on the web, you can either use the <a href="https://github.com/google/woff2">open source compression from Google</a> on the command line or you can use a free online compressor such as the one from <a href="https://webfont.yabe.land/en/misc/convert-ttf-woff2/">Yabe Webfonts</a>.</p>
<p>To include the font in your CSS, you ONLY need the following code for modern browsers. If you have to support old favorites like IE 8 or similar, you have completely different problems anyway and should think about changing jobs.</p>
<pre><code class="highlight css"><span class="keyword">@font-face</span> {
<span class="attribute">font-family</span>: <span class="string">'MyWebFont'</span>;
<span class="attribute">src</span>: <span class="built_in">url</span>(<span class="string">'myfont.woff2'</span>) <span class="built_in">format</span>(<span class="string">'woff2'</span>);
}</code></pre>
<hr>
<p>But back to Lexend and a download that works. All fonts on Google Fonts can also be found on <strong>GitHub</strong> and so there is also a repository for Lexend: <a href="https://github.com/googlefonts/lexend"><strong>googlefonts/lexend</strong></a>. In it you will find both TTF and WOFF2 files, at least for the standard font Lexend, although not for the variants (Deca, Lexa, Giga etc.), which I don’t need for my part anyway.</p>
<pre><code class="highlight txt">\lexend-main\fonts\lexend
+---ttf
Lexend-Black.ttf
Lexend-Bold.ttf
Lexend-ExtraBold.ttf
Lexend-ExtraLight.ttf
Lexend-Light.ttf
Lexend-Medium.ttf
Lexend-Regular.ttf
Lexend-SemiBold.ttf
Lexend-Thin.ttf
+---variable
Lexend[HEXP,wght].ttf
+---webfonts
Lexend-Black.woff2
Lexend-Bold.woff2
Lexend-ExtraBold.woff2
Lexend-ExtraLight.woff2
Lexend-Light.woff2
Lexend-Medium.woff2
Lexend-Regular.woff2
Lexend-SemiBold.woff2
Lexend-Thin.woff2</code></pre>
<p>You might have noticed in the files list, that there are <strong>no italic or oblique font faces</strong>. There is a reason for this, because there are currently none for Lexend. Although there is a corresponding GitHub issue (<a href="https://github.com/google/fonts/issues/4237">Update Lexend with Italics</a>), but it hasn’t really been going anywhere for over a year. Richard Hriech did publish a cursive variant called <a href="https://github.com/richardhriech/LexendItalic">LexendItalic</a> last year, but the project looks more like a quick fix created within a day and therefore I haven’t tried it out.<br>However, the absence does not bother me, because according to the W3C, the browser mimics the so-called sloping effect if it cannot find an appropriate font. I think it does this quite well and saves me having to work with additional font files.</p>
<hr>
<h2 id="The-Integration-Madness"><a href="#The-Integration-Madness" class="headerlink" title="The Integration Madness"></a>The Integration Madness</h2><p>Let’s get straight to the point: My difficulties in using Lexend here on kiko.io were entirely homemade. Integrating the font made me realize all the CSS mistakes I’ve made in recent years, because Lexend has a significantly different character spacing than OpenSans I’ve been using so far. It has never been a good idea to lean too heavily on typography when designing layouts. The careless mixing of REM and PX was also my downfall. Everything looked pretty shitty … and gave me the incentive to refactor the entire CSS (or Stylus), regardless of whether I would continue to use Lexend or not.</p>
<p>After many days of standardising, dismantling, recalculating, rewriting and also throwing away, the site had a completely new and better look for me, although (mostly) nothing had changed in the basic structure. The Lexend font, which I find much more suitable, also contributed to this and so I will leave it like that. Whether you can now read my posts faster, as the font promises, is for others to judge.</p>
<p><button class="button" id="fontToggle" onclick="fontToggle();">Toggle to Open Sans</button></p>
<hr>
<h2 id="More-Info"><a href="#More-Info" class="headerlink" title="More Info"></a>More Info</h2><ul>
<li><a href="https://www.lexend.com/">lexend.com</a></li>
<li><a href="https://www.reddit.com/r/kindle/comments/zwdvil/lexend_italic_version_see_in_comments/?rdt=57956">Lexend Italic Version</a></li>
<li><a href="https://medium.com/@ace_studio/how-to-convert-a-ttf-variable-font-to-woff2-for-improved-web-performance-3a89da8d3b04">How to Convert a TTF Variable Font to WOFF2 for Improved Web Performance</a></li>
<li><a href="https://henry.codes/writing/how-to-convert-variable-ttf-font-files-to-woff2/">How To Convert Variable TTF Font Files To WOFF2</a></li>
</ul>