<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Sam Tripp</title><link>https://www.samtripp.co.uk/posts/</link><description>The personal website of Sam Tripp</description><generator>Hugo -- gohugo.io</generator><language>en-GB</language><lastBuildDate>Thu, 19 Feb 2026 20:45:00 +0000</lastBuildDate><atom:link href="https://www.samtripp.co.uk/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Creating and Hosting a Website with Hugo</title><link>https://www.samtripp.co.uk/posts/creating-and-hosting-a-website-with-hugo/</link><pubDate>Thu, 19 Feb 2026 20:45:00 +0000</pubDate><guid>https://www.samtripp.co.uk/posts/creating-and-hosting-a-website-with-hugo/</guid><description>&lt;p>A colleague wanted to host his own website. Whilst explaining that I use &lt;a href="https://gohugo.io/">Hugo&lt;/a>, I realised this would make a useful blog post others could use as a reference to setting up their own Hugo website. This is not a unique topic. There are plenty of blog posts and social media comments praising Hugo but another bit of good press won&amp;rsquo;t do the project any harm.&lt;/p>
&lt;h1 id="what-is-hugo">What is Hugo&lt;/h1>
&lt;p>Hugo is a blazingly fast static site generator written in Go with a rich ecosystem of themes, a very flexible framework and a active community. It is what I use to run this website.&lt;/p>
&lt;h1 id="why-i-like-hugo">Why I Like Hugo&lt;/h1>
&lt;ul>
&lt;li>&lt;strong>Portability:&lt;/strong> As Hugo is generating HTML pages it is very lightweight to host. I have hosted this website a few places such as locally behind nginx, AWS S3 object storage and now GitHub Pages. Should my current hosting provider decide to change its terms, I could pick up my 628 KB website and move it elsewhere&lt;/li>
&lt;li>&lt;strong>Maintainability:&lt;/strong> As it is just HTML, there is no database to keep updated and by leveraging services such as GitHub Pages, AWS S3, Cloudflare Pages there is less underlying server maintenance on my part.&lt;/li>
&lt;li>&lt;strong>Security:&lt;/strong> Again it&amp;rsquo;s just HTML pages. Fewer moving parts means a smaller attack surface. Just because it&amp;rsquo;s HTML does not mean there are no potential risks. Someone could breach the hosting provider and leave something malicious, however compared with many other content management systems and blog platforms the risk is considerably reduced.&lt;/li>
&lt;li>&lt;strong>Themes:&lt;/strong> I am by no means a great frontend developer, thankfully due to the popularity and the active community of Hugo I have been able to use a theme that somebody else has created under a permissive licence. A small selection of themes can be found on the &lt;a href="https://themes.gohugo.io/">Hugo themes website&lt;/a>.&lt;/li>
&lt;/ul>
&lt;h1 id="how-to-use-hugo">How to use Hugo&lt;/h1>
&lt;p>Want to create a new website?&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">hugo new site my-hugo-website
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> my-hugo-website
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Want to use a theme?&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git init
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke.git themes/ananke
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Creating a blog post?&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">hugo new content/posts/&lt;span class="k">$(&lt;/span>date +%y%m%d&lt;span class="k">)&lt;/span>-blog-post.md
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Want to see your website before you publish your post?&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">hugo serve -D
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Publishing your website? Once configured it can be as simple as&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">hugo deploy
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>These are just a few examples of how straightforward Hugo can be. You can find plenty more examples and useful bits of information in the &lt;a href="https://gohugo.io/getting-started/">Getting Started guide&lt;/a> and &lt;a href="https://gohugo.io/documentation/">documentation&lt;/a>&lt;/p>
&lt;h1 id="hosting-your-website">Hosting your website&lt;/h1>
&lt;p>Hugo&amp;rsquo;s flexibility leaves you with a whole load of options for hosting. I&amp;rsquo;ve listed a few when discussing maintainability but you can find a considerable number of options that have been battle tested on &lt;a href="https://gohugo.io/host-and-deploy/">Hugo&amp;rsquo;s Host and Deploy documentation page&lt;/a>. This list is not exhaustive but gives you ideas. Remember this is just HTML pages so any web server would be sufficient. At the time of writing this post I use GitHub Pages. This is predominantly cost driven as GitHub Pages provides me web hosting at no fiscal cost to myself. The simplicity of not having to manage the underlying host system has reduced the maintenance effort also.&lt;/p>
&lt;h1 id="automating-website-deployment">Automating website deployment&lt;/h1>
&lt;p>Finally you may wish to automate the deployment process if you&amp;rsquo;re using something like GitHub/GitHub Pages. If you&amp;rsquo;re in the GitHub ecosystem you could leverage GitHub Actions to achieve this thanks to the fine work of &lt;a href="https://github.com/peaceiris">peaceiris&lt;/a>. I use a 2 git repository solution to achieve this. One that is private so I can commit draft blogs prior to them going public and my public github user repo &lt;a href="https://github.com/sbt92/sbt92.github.io">sbt92/sbt92.github.io&lt;/a> which automatically enables GitHub Pages and serve my website. If you wish to do what I do you can store this Github Actions pipeline code in your private repo under &lt;strong>.github/workflows/deploy-website.yaml&lt;/strong>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Deploy website to GitHub Pages (&amp;lt;github_username&amp;gt;.github.io)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">push&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">branches&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">main&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">deploy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/checkout@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">submodules&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Fetch Hugo themes &lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">fetch-depth&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Fetch all history for .GitInfo and .Lastmod&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Setup Hugo&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">peaceiris/actions-hugo@v3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">hugo-version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;0.131.0&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Optional&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">extended&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Build website&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">hugo --minify&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Deploy&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">peaceiris/actions-gh-pages@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">personal_token&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ secrets.GITHUB_API_KEY }}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Generate this in user Development Settings and add to your private repository secrets.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">external_repository&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">&amp;lt;github_username&amp;gt;/&amp;lt;github_username&amp;gt;.github.io&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">publish_branch&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">main&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">publish_dir&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">./public&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">full_commit_message&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ github.event.head_commit.message }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cname&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">www.&amp;lt;mywebdomain&amp;gt;.co.uk&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Fixing SMLIGHT SLZB-06M Zigbee Coordinator for Zigbee2MQTT</title><link>https://www.samtripp.co.uk/posts/fixing-smlight-slzb-06m-zigbee-coordinator-for-zigbee2mqtt/</link><pubDate>Mon, 26 May 2025 12:20:00 +0100</pubDate><guid>https://www.samtripp.co.uk/posts/fixing-smlight-slzb-06m-zigbee-coordinator-for-zigbee2mqtt/</guid><description>&lt;p>I recently bought a new Zigbee Coordinator after my old SONOFF ZBDongle-P was damaged in an accident when my knee caught it while it was plugged into the front USB port of my server. Wanting to avoid the same situation again I decided to purchase the &lt;a href="https://www.aliexpress.com/item/1005004942648430.html">SMLIGHT SLZB-06M&lt;/a> to replace it after a recommendation from a friend. I liked that it gave me the choice to serve the coordinator either over USB or TCP. Decoupling the coordinator from the server did give me plenty of flexibility in the future to where I could put my server without impacting the Zigbee network.&lt;/p>
&lt;p>While it was advertised as plug and play, I found that I still needed to take a few steps to get it working with Zigbee2MQTT. The deprecated EZSP driver functions but the recommended Ember driver does not. If you find yourself with this coordinator you can test this out of the box by setting &lt;code>adapter: ember&lt;/code> in your Zigbee2MQTT configuration.yaml. If Zigbee2MQTT will not start change the configuration.yaml to &lt;code>adapter: ezsp&lt;/code>. If Zigbee2MQTT starts with the EZSP adapter set in your configuration you&amp;rsquo;ve identified that the SMLIGHT SLZB-06M needs a firmware update.&lt;/p>
&lt;p>To perform the firmware update the SMLIGHT SLZB-06M provides its own admin console on your network if your coordinator is connected to the LAN. Once you&amp;rsquo;ve accessed the admin console via its IP address on your network navigate to &lt;strong>Settings and Tools&lt;/strong> and then &lt;strong>Firmware update&lt;/strong>. Click the &lt;strong>Check for Zigbee updates&lt;/strong> button. Under the Zigbee Coordinator section you want the installed firmware version to be 20250220 or later. If it is not at that version click the &lt;strong>Flash Zigbee Coordinator 20250220&lt;/strong> button. This process usually takes a couple of minutes. Once completed set &lt;code>adapter: ember&lt;/code> in your Zigbee2MQTT configuration.yaml. Zigbee2MQTT should start up now and you can go through the process of pairing your devices again.&lt;/p></description></item><item><title>AI Model Wars: o1-mini vs gpt-4o-mini</title><link>https://www.samtripp.co.uk/posts/ai-model-wars-o1-mini-vs-gpt-4o-mini/</link><pubDate>Thu, 05 Dec 2024 22:30:00 +0000</pubDate><guid>https://www.samtripp.co.uk/posts/ai-model-wars-o1-mini-vs-gpt-4o-mini/</guid><description>&lt;p>On 20 November 2024 I received an email saying that Open AI&amp;rsquo;s o1-preview and o1-mini models are available for use via the OpenAI API. I did a brief test via &lt;a href="https://www.samtripp.co.uk/posts/creating-a-subscription-free-chatgpt-plus-alternative/">my Open WebUI application on my server&lt;/a> to prove I could access and use the o1-mini model. I was able to use the model however first impressions of the model left me quite surprised at the performance and quality of the response. It felt like it was no better than gpt-4o-mini and I was paying a considerable amount more for the tokens. I decided to stay on gpt-4o-mini until I could conduct more thorough testing of the new model to see how it fits my use cases. This day has now come and I have documented my findings in this blog post.&lt;/p>
&lt;h1 id="how-are-we-going-to-test">How are we going to test.&lt;/h1>
&lt;p>Open WebUI has some default suggested prompts which we are going to run against each model. The prompts are:&lt;/p>
&lt;ol>
&lt;li>What are 5 creative things I could do with my kids&amp;rsquo; art? I don&amp;rsquo;t want to throw them away, but it&amp;rsquo;s also so much clutter.&lt;/li>
&lt;li>Show me a code snippet of a website&amp;rsquo;s sticky header in CSS and JavaScript.&lt;/li>
&lt;li>Tell me a random fun fact about the Roman Empire.&lt;/li>
&lt;/ol>
&lt;p>We will run these prompts against each model and compare the quality of the response. We will also gather the cost of running these queries from the OpenAI Dashboard. All of these queries will be ran on the same machine using the same version of Open WebUI which is v0.4.4 .&lt;/p>
&lt;h1 id="quality-of-response">Quality of Response&lt;/h1>
&lt;p>Keep in mind that the responses generated may not be identical. What we want to focus on is the quality of those responses.&lt;/p>
&lt;h4 id="what-are-5-creative-things-i-could-do-with-my-kids-art-i-dont-want-to-throw-them-away-but-its-also-so-much-clutter">What are 5 creative things I could do with my kids&amp;rsquo; art? I don&amp;rsquo;t want to throw them away, but it&amp;rsquo;s also so much clutter.&lt;/h4>
&lt;p>&lt;details>
&lt;summary>o1-mini response&lt;/summary>
&lt;div>&lt;p>Absolutely, finding creative ways to cherish your kids&amp;rsquo; artwork while managing clutter is a wonderful approach. Here are five ideas to help you honor their creations:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Create a Rotating Gallery Wall&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>How:&lt;/strong> Designate a specific wall or space in your home as the “gallery.” Use frames, clipboards, or a wire with clips to display their latest masterpieces.&lt;/li>
&lt;li>&lt;strong>Benefits:&lt;/strong> This allows you to showcase their work proudly and easily swap out older pieces for new creations, keeping the display fresh without accumulating clutter.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Transform Art into a Photobook&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>How:&lt;/strong> Photograph or scan each piece of artwork and compile the images into a professionally printed photo book using services like Shutterfly, Mixbook, or Blurb.&lt;/li>
&lt;li>&lt;strong>Benefits:&lt;/strong> This not only preserves the artwork in a compact, organized manner but also creates a beautiful keepsake that you and your child can revisit for years to come.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Design Custom Gifts and Household Items&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>How:&lt;/strong> Use online platforms (such as Zazzle, Snapfish, or VistaPrint) to print your child’s artwork onto items like mugs, t-shirts, tote bags, pillows, or calendars.&lt;/li>
&lt;li>&lt;strong>Benefits:&lt;/strong> These personalized items make unique gifts for family members and allow you to enjoy the art in everyday use, reducing the need to store physical copies.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Make a Digital Slideshow or Screensaver&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>How:&lt;/strong> Scan or photograph the artwork and use the images to create a digital slideshow or screensaver for your computer, tablet, or smart TV.&lt;/li>
&lt;li>&lt;strong>Benefits:&lt;/strong> This provides a space-saving way to enjoy the artwork regularly and can be easily updated as new pieces are created.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Create a Collage or Mosaic&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>How:&lt;/strong> Select portions of various artworks and arrange them into a large collage or mosaic, either digitally or physically on a canvas. You can also use photo editing software to blend multiple images into a single piece.&lt;/li>
&lt;li>&lt;strong>Benefits:&lt;/strong> This artistic approach consolidates many pieces into one aesthetically pleasing artwork, reducing the volume of individual items while highlighting multiple creations in a unified display.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>Implementing these ideas can help you celebrate your children’s creativity while keeping your home organized and free from excessive clutter. Choose the methods that best fit your lifestyle and the sentimental value you place on their artwork!&lt;/p>
&lt;/div>
&lt;/details>
&lt;details>
&lt;summary>gpt-4o-mini Response&lt;/summary>
&lt;div>&lt;p>It&amp;rsquo;s wonderful that you want to preserve and celebrate your kids&amp;rsquo; art! Here are five creative ideas to help you manage the clutter while showcasing their creativity:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Create a Rotating Art Gallery&lt;/strong>: Designate a wall or a space in your home as a mini art gallery. Use frames, clipboards, or string with clothespins to display a selection of their artwork. Rotate the art regularly, allowing your kids to decide which pieces to feature and keeping the display fresh.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Art Book Compilation&lt;/strong>: Photograph or scan your children&amp;rsquo;s artwork and create a printed photo book. You can use online services to design and print the book, which will serve as a lovely keepsake and take up much less space than storing physical art.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Custom Gift Wrap&lt;/strong>: Use your kids&amp;rsquo; art as unique and personal gift wrap for presents. This not only adds a special touch to gifts but also helps minimize clutter by finding a practical use for the artwork.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Art Collage or Quilt&lt;/strong>: Take portions of different art pieces and create a larger collage or have a sewing service make a quilt out of fabric printed with their art. This way, you condense their creations into one beautiful piece that can be displayed or used.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Digital Slideshow&lt;/strong>: Create a digital slideshow or screensaver using scanned or photographed images of their artwork. This way, you can enjoy their creations on your computer or TV screens without the physical clutter. Consider pairing the slideshow with music for an engaging presentation!&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>These ideas can help you celebrate your children&amp;rsquo;s creativity while also managing the volume of art they produce.&lt;/p>
&lt;/div>
&lt;/details>&lt;/p>
&lt;p>The o1-mini model does give more detail here breaking down the suggestions into how and the benefits of doing so. The gpt-4o-mini model does not do that and captures the how and the benefit in one paragraph. Despite this difference in structure, both models deliver relatively similar answers.&lt;/p>
&lt;h4 id="show-me-a-code-snippet-of-a-websites-sticky-header-in-css-and-javascript">Show me a code snippet of a website&amp;rsquo;s sticky header in CSS and JavaScript.&lt;/h4>
&lt;p>&lt;details>
&lt;summary>o1-mini response&lt;/summary>
&lt;div>&lt;p>Creating a &lt;strong>sticky header&lt;/strong> ensures that your website&amp;rsquo;s header remains visible at the top of the viewport as users scroll down the page. You can achieve this using &lt;strong>CSS&lt;/strong> alone with the &lt;code>position: sticky&lt;/code> property or enhance its functionality with &lt;strong>JavaScript&lt;/strong> for additional effects, such as changing the header&amp;rsquo;s appearance after scrolling a certain distance.&lt;/p>
&lt;p>Below is a complete example demonstrating both CSS and JavaScript approaches to creating a sticky header.&lt;/p>
&lt;hr>
&lt;h2 id="1-html-structure">&lt;strong>1. HTML Structure&lt;/strong>&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;!DOCTYPE html&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">html&lt;/span> &lt;span class="na">lang&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;en&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">charset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;UTF-8&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Sticky Header Example&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;styles.css&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">body&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">header&lt;/span> &lt;span class="na">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;header&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>My Website&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">nav&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;#home&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Home&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;#about&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>About&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;#contact&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Contact&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">nav&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">header&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">main&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">section&lt;/span> &lt;span class="na">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;home&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h2&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Home&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h2&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Content goes here...&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">section&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">section&lt;/span> &lt;span class="na">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;about&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h2&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>About&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h2&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>More content...&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">section&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">section&lt;/span> &lt;span class="na">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;contact&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h2&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Contact&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h2&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Even more content...&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">section&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- Add more sections as needed to enable scrolling --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">main&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;script.js&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">body&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">html&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="2-css-styling-stylescss">&lt;strong>2. CSS Styling (&lt;code>styles.css&lt;/code>)&lt;/strong>&lt;/h2>
&lt;h3 id="using-css-position-sticky">&lt;strong>Using CSS &lt;code>position: sticky&lt;/code>&lt;/strong>&lt;/h3>
&lt;p>The simplest way to create a sticky header is by using the &lt;code>position: sticky&lt;/code> property. This method requires minimal code and works well in modern browsers.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* Basic Reset */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">*&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">margin&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">box-sizing&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">border-box&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">body&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">font-family&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Arial&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">sans-serif&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* Header Styling */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">header&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">position&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">sticky&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">top&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#333&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#fff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">15&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="mi">20&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">z-index&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c">/* Ensure the header stays on top */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">transition&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">background-color&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">padding&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* Navigation Links */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">header&lt;/span> &lt;span class="nt">nav&lt;/span> &lt;span class="nt">a&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#fff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">margin&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">text-decoration&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">none&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">header&lt;/span> &lt;span class="nt">nav&lt;/span> &lt;span class="nt">a&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nd">hover&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">text-decoration&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">underline&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* Scrolled Header Styling (for JavaScript Enhancement) */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">header&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">scrolled&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#222&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="mi">20&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* Main Content */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">main&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">20&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">section&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">margin-bottom&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="kt">vh&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c">/* To create scrollable content */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">section&lt;/span> &lt;span class="nt">h2&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">margin-bottom&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="explanation">&lt;strong>Explanation:&lt;/strong>&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>&lt;code>position: sticky&lt;/code>&lt;/strong>: This positions the header relative to its nearest scrolling ancestor and sticks to the top (&lt;code>top: 0&lt;/code>) when you scroll past it.&lt;/li>
&lt;li>&lt;strong>&lt;code>z-index: 1000&lt;/code>&lt;/strong>: Ensures the header stays above other content.&lt;/li>
&lt;li>&lt;strong>Transition Effects&lt;/strong>: Smoothly transition background color and padding when the header&amp;rsquo;s state changes (useful for JavaScript enhancements).&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="3-javascript-enhancement-scriptjs">&lt;strong>3. JavaScript Enhancement (&lt;code>script.js&lt;/code>)&lt;/strong>&lt;/h2>
&lt;p>While CSS can handle the basic sticky behavior, JavaScript can add more interactive features, such as changing the header&amp;rsquo;s appearance after the user scrolls a certain distance.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Select the header
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">header&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getElementById&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;header&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Function to add or remove the &amp;#39;scrolled&amp;#39; class based on scroll position
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="nx">toggleHeaderScrolled&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scrollY&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">50&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="c1">// You can adjust the value as needed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">header&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">classList&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;scrolled&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">header&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">classList&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">remove&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;scrolled&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Listen to the scroll event
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;scroll&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">toggleHeaderScrolled&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="explanation-1">&lt;strong>Explanation:&lt;/strong>&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Selecting the Header&lt;/strong>: The script selects the header element by its ID.&lt;/li>
&lt;li>&lt;strong>&lt;code>toggleHeaderScrolled&lt;/code> Function&lt;/strong>: Adds the &lt;code>scrolled&lt;/code> class to the header when the user scrolls down more than 50 pixels, and removes it when the user scrolls back up.&lt;/li>
&lt;li>&lt;strong>Event Listener&lt;/strong>: The function is called every time the user scrolls, allowing real-time updates to the header&amp;rsquo;s appearance.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="4-final-result">&lt;strong>4. Final Result&lt;/strong>&lt;/h2>
&lt;p>With the above HTML, CSS, and JavaScript:&lt;/p>
&lt;ul>
&lt;li>The header &lt;strong>locks to the top&lt;/strong> of the viewport when scrolling down.&lt;/li>
&lt;li>When you scroll &lt;strong>more than 50 pixels&lt;/strong>, the header&amp;rsquo;s background color darkens, and padding reduces for a sleek effect.&lt;/li>
&lt;li>Navigation links are styled for better user experience.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="demo">&lt;strong>Demo&lt;/strong>&lt;/h2>
&lt;p>You can see a live demo of the sticky header &lt;a href="https://codepen.io/pen/?template=VwPKvbe">here&lt;/a>.&lt;/p>
&lt;hr>
&lt;h2 id="browser-support">&lt;strong>Browser Support&lt;/strong>&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>&lt;code>position: sticky&lt;/code>&lt;/strong> is supported in most modern browsers, including Chrome, Firefox, Edge, and Safari.&lt;/li>
&lt;li>Always ensure to test across different browsers to verify consistent behavior.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="alternative-approach-using-position-fixed">&lt;strong>Alternative Approach: Using &lt;code>position: fixed&lt;/code>&lt;/strong>&lt;/h2>
&lt;p>If you need broader browser support or different behavior, you can use &lt;code>position: fixed&lt;/code>. However, this method removes the header from the normal document flow, which might require additional styling adjustments.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">header&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">position&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">fixed&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">top&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">width&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="kt">%&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* Other styles remain the same */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Note:&lt;/strong> When using &lt;code>position: fixed&lt;/code>, ensure that the main content has a top margin equal to the header&amp;rsquo;s height to prevent it from being hidden behind the fixed header.&lt;/p>
&lt;hr>
&lt;p>Feel free to customize the styles and JavaScript logic to fit the specific needs and design of your website!&lt;/p>
&lt;/div>
&lt;/details>
&lt;details>
&lt;summary>gpt-4o-mini Response&lt;/summary>
&lt;div>&lt;p>Sure! A sticky header is a common UI feature that remains at the top of the viewport as a user scrolls down the page. Below is a simple example of how you can implement a sticky header using CSS and JavaScript.&lt;/p>
&lt;h3 id="html-structure">HTML Structure&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;!DOCTYPE html&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">html&lt;/span> &lt;span class="na">lang&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;en&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">charset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;UTF-8&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;viewport&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;width=device-width, initial-scale=1.0&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Sticky Header Example&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;styles.css&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">body&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">header&lt;/span> &lt;span class="na">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;header&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Sticky Header&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">header&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">main&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">section&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Welcome to the Sticky Header Example&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Scroll down to see the header stick to the top of the page.&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- Add some dummy content to enable scrolling --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla ac efficitur nulla. Praesent at velit eget justo rhoncus pulvinar. Sed ac posuere lectus, quis pretium ipsum. Etiam eget felis vitae augue elementum ullamcorper a non tortor.&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>(Add a lot more content here to make the page scrollable)&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>...&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="c">&amp;lt;!-- Extend this with more paragraphs to ensure scrolling --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">section&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">main&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;script.js&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">body&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">html&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="css-stylescss">CSS (styles.css)&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">body&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">font-family&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Arial&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">sans-serif&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">margin&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">header&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#333&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">white&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="mi">20&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">text-align&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">center&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">width&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="kt">%&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">transition&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">top&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">main&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">15&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="nc">content&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">height&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1500&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c">/* Set a height for content to ensure the page is scrollable */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="nc">sticky&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">position&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">fixed&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">top&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">left&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">right&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">z-index&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c">/* Ensure the header is on top */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="javascript-scriptjs">JavaScript (script.js)&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Get the header
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">header&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getElementById&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;header&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Get the offset position of the navbar
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">sticky&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">header&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">offsetTop&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Add the sticky class to the header when you reach its scroll position. Remove &amp;#34;sticky&amp;#34; when you leave the scroll position
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="nx">stickHeader&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pageYOffset&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="nx">sticky&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">header&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">classList&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;sticky&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">header&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">classList&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">remove&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;sticky&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// When the user scrolls the page, execute stickHeader
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onscroll&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">stickHeader&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="explanation">Explanation&lt;/h3>
&lt;ol>
&lt;li>&lt;strong>HTML&lt;/strong>: The &lt;code>&amp;lt;header&amp;gt;&lt;/code> element serves as our sticky header. The &lt;code>&amp;lt;main&amp;gt;&lt;/code> section contains the content.&lt;/li>
&lt;li>&lt;strong>CSS&lt;/strong>: The &lt;code>.sticky&lt;/code> class sets the header to fixed positioning at the top of the viewport when scrolling occurs.&lt;/li>
&lt;li>&lt;strong>JavaScript&lt;/strong>: The script checks the scroll position and applies the &lt;code>.sticky&lt;/code> class to the header when the user scrolls past the top position of the header.&lt;/li>
&lt;/ol>
&lt;p>You can add more content inside &lt;code>&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;&lt;/code> to make the page scrollable and demonstrate the sticky header functionality.&lt;/p>
&lt;/div>
&lt;/details>&lt;/p>
&lt;p>Both models provide valid and usable answers, with the o1-mini model offering greater detail and even presenting an alternative approach that users can adopt if they wish to.&lt;/p>
&lt;h4 id="tell-me-a-random-fun-fact-about-the-roman-empire">Tell me a random fun fact about the Roman Empire.&lt;/h4>
&lt;p>&lt;details>
&lt;summary>o1-mini response&lt;/summary>
&lt;div>&lt;p>&lt;strong>Fun Fact about the Roman Empire:&lt;/strong>&lt;/p>
&lt;p>The Romans were pioneers in engineering and construction, and one of their most impressive feats was the creation of &lt;strong>concrete&lt;/strong>. Roman concrete, known as &lt;em>opus caementicium&lt;/em>, was incredibly durable and was used to build iconic structures like the Pantheon and the Colosseum, many of which still stand today. Interestingly, the secret to their concrete&amp;rsquo;s longevity lies in its unique mixture, which included volcanic ash. This ingredient helped the concrete withstand the test of time, especially in marine environments, allowing Roman harbors and buildings to endure for over two millennia. Modern scientists have studied Roman concrete to inspire more sustainable and long-lasting building materials today.&lt;/p>
&lt;/div>
&lt;/details>
&lt;details>
&lt;summary>gpt-4o-mini Response&lt;/summary>
&lt;div>One fun fact about the Roman Empire is that they had a form of postal system known as the &lt;strong>Cursus Publicus&lt;/strong>. Established by Emperor Augustus, it allowed for the efficient transport of messages, documents, and officials across the vast empire. This system used relay stations with fresh horses and riders, enabling messages to travel up to 50 miles a day. This infrastructure was crucial for maintaining communication and governance throughout the sprawling territories of the empire.&lt;/div>
&lt;/details>&lt;/p>
&lt;p>I felt the quality of the responses was so similar that it was hard to tell which model generated the response. The key difference being that the o1-mini model generated a header, while the gpt-4o-mini model did not.&lt;/p>
&lt;h1 id="cost">Cost&lt;/h1>
&lt;p>The cost of running the three prompts against the o1-mini came up to $0.063462. The cost of running the exact same prompts again gpt-4o-mini cost $0.00123075. This makes running queries against the o1-mini model approximately 5056% more expensive than gpt-4o-mini. I was quite surprised to see such a significant cost difference, especially considering the comparable responses generated by both models.&lt;/p>
&lt;h1 id="conclusion">Conclusion&lt;/h1>
&lt;p>According to my findings, the average user would be better off sticking with gpt-4o-mini. According to &lt;a href="https://openai.com/index/openai-o1-mini-advancing-cost-efficient-reasoning/">OpenAI blog post&lt;/a> on the o1-mini model it looks like you could potentially benefit more if you&amp;rsquo;re running STEM related prompts against the model which my test do not cover enough to prove that theory. They did note that the models non-STEM topics knowledge is comparable to gpt-4o-mini.&lt;/p></description></item><item><title>Noteworthy Talks from Ubuntu Summit 2024</title><link>https://www.samtripp.co.uk/posts/noteworthy-talks-from-ubuntu-summit-2024/</link><pubDate>Sat, 23 Nov 2024 10:00:00 +0000</pubDate><guid>https://www.samtripp.co.uk/posts/noteworthy-talks-from-ubuntu-summit-2024/</guid><description>&lt;p>I was fortunate enough to get my first opportunity to attend the &lt;a href="https://events.canonical.com/event/51/">Ubuntu Summit 2024&lt;/a> at the World Forum in the Hague. As someone who has used Linux in some capacity since about 2006 with my first experience of Ubuntu being 8.04 Hardy Heron it was safe to say I was quite excited to attend.&lt;/p>
&lt;p>The conference itself was a great advocacy for the open source ecosystem with attendance from many key vendors and projects in this space. I came away from the conference inspired. It was great to experience how open source has influenced many people&amp;rsquo;s lives, from software engineers to creatives in the arts. I was especially impressed in the variety of talks, it really did have something for everyone.&lt;/p>
&lt;p>Beyond the talks, the hallway track was particularly enjoyable, featuring some excellent booths that allowed me to experience things I hadn&amp;rsquo;t had the privilege to encounter first hand. Framework Laptop, System76 Launch Keyboard, Fairphone, Intel Arc GPU for AI purposes just to name a few things that ended up on my wish list when my current hardware is due a refresh. It was also a pleasure to speak to many like-minded individuals at the booths and throughout the conference.&lt;/p>
&lt;p>I had an excellent time. Should I get the opportunity to do it again you can rest assured I will be there. One of my favourite conferences I&amp;rsquo;ve attended to date. Below are a selection of talks which I thoroughly enjoyed and would recommend checking out.&lt;/p>
&lt;h1 id="no-open-source-didnt-destroy-my-creative-agency---ryan-gorley">No, Open Source Didn&amp;rsquo;t Destroy My Creative Agency - Ryan Gorley&lt;/h1>
&lt;p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/OXocCVpVwUw?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
>&lt;/iframe>
&lt;/div>
&lt;a href="https://www.youtube.com/watch?v=OXocCVpVwUw">Watch on Youtube&lt;/a>&lt;/p>
&lt;p>As someone who is not a creative it was truly inspirational to see Ryan advocating for the open source tools available in this space and is able to utilise them to make a living. The creative space is unfortunately dominated by proprietary offerings some which charge an extortionate amount monthly which can and probably has made creativity unreachable to many. I was able to speak to Ryan and other like minded people after the talk about whats available in this space and this exposed me to the &lt;a href="https://ubuntustudio.org/">Ubuntu Studio&lt;/a> distribution which I will be encouraging my wife who is a creative to try out.&lt;/p>
&lt;h1 id="servo-building-a-browser-rendering-engine-in-rust---rakhi-sharma">Servo: Building a Browser Rendering Engine in Rust - Rakhi Sharma&lt;/h1>
&lt;p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/fp5n7CSvALA?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
>&lt;/iframe>
&lt;/div>
&lt;a href="https://www.youtube.com/watch?v=fp5n7CSvALA">Watch on Youtube&lt;/a>&lt;/p>
&lt;p>This talk was quite conveniently timed as I&amp;rsquo;ve only been aware of Servo since Mozilla once again shot themselves in the foot with their &lt;a href="https://blog.mozilla.org/en/mozilla/improving-online-advertising/">advertising blog post&lt;/a>. Quite a remarkable undertaking to achieve what they have so far with this browser engine. I hope it continues and secures the necessary funding so we can enjoy greater choice in browsers. This talk and Servo itself gives me hope that we will have another choice in the future.&lt;/p>
&lt;h1 id="engineering-cosmic-de-a-vision-realized---carl-richell-victoria-brekenfeld">Engineering COSMIC DE: A Vision Realized - Carl Richell, Victoria Brekenfeld&lt;/h1>
&lt;p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/bwrBKccfYws?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
>&lt;/iframe>
&lt;/div>
&lt;a href="https://www.youtube.com/watch?v=bwrBKccfYws">Watch on Youtube&lt;/a>&lt;/p>
&lt;p>It was great to see what the folks at System76 have been able to achieve with the COSMIC Desktop Environment. For an Alpha product it was quite incredible how stable and usable it was. What I did appreciate about this talk was the honesty that there will be bugs and being upfront about these bugs. Whilst I am very happy with the GNOME Desktop Environment at the moment I would certainly be keeping a close eye on COSMIC and would be tempted to try it out in the near future.&lt;/p>
&lt;h1 id="master-advanced-anti-ban--web-scraping-techniques---fabien-vauchelles">Master Advanced Anti-Ban &amp;amp; Web Scraping Techniques - Fabien Vauchelles&lt;/h1>
&lt;p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/v_QCFThIW3Y?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
>&lt;/iframe>
&lt;/div>
&lt;a href="https://www.youtube.com/watch?v=v_QCFThIW3Y">Watch on Youtube&lt;/a>&lt;/p>
&lt;p>Having dabbled in some scraping myself I have hit a few issues in the past that have blocked me from scraping. This workshop exposed me to &lt;a href="https://scrapoxy.io/">scrapoxy&lt;/a> and I got the chance to try it hands on against a demo site using come common techniques to circumvent some of the common blocks implemented. I also fully appreciated the workshop being available on &lt;a href="https://github.com/fabienvauchelles/scraping-workshop">Github&lt;/a> for future reference.&lt;/p>
&lt;h1 id="open-platform-for-enterprise-ai---christian-holsing">Open Platform for Enterprise AI - Christian Holsing&lt;/h1>
&lt;p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/cTjIYHLtw48?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
>&lt;/iframe>
&lt;/div>
&lt;a href="https://www.youtube.com/watch?v=cTjIYHLtw48">Watch on Youtube&lt;/a>&lt;/p>
&lt;p>As someone who is currently working on an AI project, I fully appreciated what the &lt;a href="https://opea.dev/">OPEA&lt;/a> project is trying to achieve here. Due to the rapid movement of the various AI ecosystem standards are lacking and everyone is implementing their solutions in their own way. The frameworks and blueprints that OPEA are creating is utilising open standards and technologies to create a open platform for GenAI. The project has contributions from some of the biggest industry players involved in AI so its something I will certainly be keeping a close eye on.&lt;/p>
&lt;h1 id="rocm-for-amd-gpus-on-ubuntu---cordell-bloor">ROCm for AMD GPUs on Ubuntu - Cordell Bloor&lt;/h1>
&lt;p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/sUnZDYvfceE?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
>&lt;/iframe>
&lt;/div>
&lt;a href="https://www.youtube.com/watch?v=sUnZDYvfceE">Watch on Youtube&lt;/a>&lt;/p>
&lt;p>This talk introduced me to AMD ROCm and the considerable amount of work being done by Cordell and the community to support a wider range of AMD GPU support for AI/ML workloads. It is excellent to see the work being done here will enable a wider range of devices and as a result will enable greater competition in the GPU market for AI/ML purposes.&lt;/p>
&lt;h1 id="harnessing-htcondor-how-dreamworks-animation-orchestrates-massive-workloads-for-feature-films---mark-jackels">Harnessing HTCondor: How DreamWorks Animation Orchestrates Massive Workloads for Feature Films - Mark Jackels&lt;/h1>
&lt;p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/X3XQPqS_OCU?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
>&lt;/iframe>
&lt;/div>
&lt;a href="https://www.youtube.com/watch?v=X3XQPqS_OCU">Watch on Youtube&lt;/a>&lt;/p>
&lt;p>It was quite impressive to see the insights on how an animation studio structures and distributes its workload across 1800 workstations using HTCondor. Gives me a greater appreciation for the raw power required to produced some of the latest and greatest blockbuster animation movies. Can&amp;rsquo;t see me using this as I do not have a blockbuster budget but nevertheless was incredible to see the capabilities of this piece of free and open source software.&lt;/p>
&lt;h1 id="framework-lightning-talk---nirav-patel">Framework Lightning Talk - Nirav Patel&lt;/h1>
&lt;p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/l6khGznGeyY?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
>&lt;/iframe>
&lt;/div>
&lt;a href="https://www.youtube.com/watch?v=l6khGznGeyY">Watch on Youtube&lt;/a>&lt;/p>
&lt;p>Managing to swap your laptop hardware from x86 to RISC-V architecture in the space of a 5 minute lightning talk. I was amazed that it could be done. A true credit to the Framework brand. Certainly deserved the standing ovation they got for achieving this. They will certainly be on my radar when I am due a laptop refresh.&lt;/p></description></item><item><title>Creating a subscription-free ChatGPT Plus alternative</title><link>https://www.samtripp.co.uk/posts/creating-a-subscription-free-chatgpt-plus-alternative/</link><pubDate>Tue, 30 Jul 2024 08:00:00 +0100</pubDate><guid>https://www.samtripp.co.uk/posts/creating-a-subscription-free-chatgpt-plus-alternative/</guid><description>&lt;p>AI has taken the world by storm over the past 18 months with many people utilising it in many ways. One of the most popular ways of interacting with AI is using ChatGPT. Like most modern software OpenAI&amp;rsquo;s ChatGPT offers a free tier as well as their subscription called ChatGPT Plus which offers more features and a selection of Large Language Models.&lt;/p>
&lt;p>A friend of mine has been using ChatGPT Plus. Key reasons he was paying for the Plus subscription was to ensure he had access to it whenever he needed which is something that is not guaranteed on the free tier. However at $20 a month he is questioning if he used it enough to warrant paying the monthly subscription cost. He said if the cost was lower he would be happy to continue paying for it. I had to agree with him as a casual user myself I could never justify that expense. This is when I went to explore the option of building a ChatGPT Plus like experience at a lower cost.&lt;/p>
&lt;h1 id="how-did-i-do-this">How did I do this&lt;/h1>
&lt;p>To grant ourselves access to our very own ChatGPT like experience we are going to use 2 tools:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://docs.openwebui.com/">Open WebUI&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://platform.openai.com/">OpenAI API&lt;/a>.&lt;/li>
&lt;/ul>
&lt;h2 id="sign-up-to-openai-and-generate-an-api-key">Sign up to OpenAI and generate an API Key&lt;/h2>
&lt;p>If you have not done so already create an account at &lt;a href="https://platform.openai.com/">https://platform.openai.com/&lt;/a>. Once you&amp;rsquo;ve done this navigate to the &lt;a href="https://platform.openai.com/api-keys">API Key Page&lt;/a>. There is a bizarre restriction which means you need to enter a mobile phone number before you can generate your first API Key. As this is only used one time, I purchased a sim card at the supermarket and registered that number against the account. Feel free to use your own personal phone number if you wish. Once this is done generate an API Key.&lt;/p>
&lt;p>You need to go and add credit to your account to actually be able to use the API. To do this navigate to the billing page and add some credit to your account. I decided on just adding the minimum to my account which is $5 + VAT.&lt;/p>
&lt;p>You now have an account and access to OpenAI&amp;rsquo;s API.&lt;/p>
&lt;h2 id="set-up-open-webui">Set up Open WebUI&lt;/h2>
&lt;p>There are a number of ways to set up Open WebUI but the recommended process is using Docker so before you go any further ensure you have got this set up. Once that has been completed run the command below. Make sure you&amp;rsquo;re replacing &lt;strong>&amp;lt;your_generated_api_key&amp;gt;&lt;/strong> with your OpenAI API key that you have generated.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-docker" data-lang="docker">&lt;span class="line">&lt;span class="cl">docker run -d -p 3000:8080 -e &lt;span class="nv">OPENAI_API_KEY&lt;/span>&lt;span class="o">=&lt;/span>&amp;lt;your_generated_api_key&amp;gt; -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="start-prompting">Start prompting&lt;/h2>
&lt;p>Navigate to &lt;a href="http://localhost:3000">http://localhost:3000&lt;/a> in your web browser. Create an account, login, select a model of your choice in the dropdown at the top left of the page and start using it as you would with ChatGPT. At the time of writing the gpt-4o-mini model is OpenAI&amp;rsquo;s most cost effective model.&lt;/p>
&lt;h1 id="how-does-it-compare">How does it compare&lt;/h1>
&lt;h2 id="cost">Cost&lt;/h2>
&lt;p>I created the prompt &amp;ldquo;Can you give me the number of workdays between December and April. I do not work weekends or UK bank holidays.&amp;rdquo; using the gpt-4o model and that costs approximately $0.01. You would have to put 2000 equivalent requests within a month to break even with the ChatGPT Plus subscription cost. If you put the same request through gpt-4o-mini it costs $0.0005 which means you would need to put 40000000 equivalent requests to break even.&lt;/p>
&lt;p>If you&amp;rsquo;re heavy user you may exceed the 2000 requests but if you&amp;rsquo;re a casual user such as myself then the API proves to be considerably better value than ChatGPT Plus. I would be surprised if many exceed the gpt-4o-mini break even cost.&lt;/p>
&lt;h2 id="convenience">Convenience&lt;/h2>
&lt;p>ChatGPT Plus wins here but as you can see it does not take an awful lot of effort to get this up and running. Personally for me the small convenience sacrifice pays off considerably in terms of fiscal savings for the casual user.&lt;/p>
&lt;h2 id="quality-of-response">Quality of response&lt;/h2>
&lt;p>My friend still had access to his ChatGPT Plus subscription so we have done some comparisons to check the quality of the responses from the API against the responses from ChatGPT Plus. All responses below were ran against the gpt-4o model.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>What are 5 creative things I could do with my kids&amp;rsquo; art? I don&amp;rsquo;t want to throw them away, but it&amp;rsquo;s also so much clutter.
&lt;details>
&lt;summary>ChatGPT Plus Response&lt;/summary>
&lt;div>&lt;p>Finding creative ways to preserve and display your kids&amp;rsquo; art can be a wonderful way to honor their creativity while managing clutter. Here are five ideas:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Create a Rotating Gallery: Designate a space in your home, like a wall or a bulletin board, as a rotating gallery for your kids&amp;rsquo; artwork. You can frame a few pieces or use clipboards, wire with clips, or magnetic strips to easily change the display. This way, the art can be regularly updated, and older pieces can be stored or used in other projects.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Make a Digital Archive and Photo Book: Scan or photograph your children&amp;rsquo;s artwork and create a digital archive. You can use these images to make a photo book, which is a compact and organized way to keep the art accessible. Many online services allow you to customize and print these books, making them a wonderful keepsake.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Create Art Collages or Murals: Take pieces of their artwork and cut out favorite sections or elements to create a large collage or mural. This can be a fun project for the whole family and a unique way to blend different pieces into one large, cohesive artwork.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Turn Art into Gifts or Home Décor: Transform their art into gifts or home décor items. You can print their drawings on mugs, t-shirts, cushions, or even create custom puzzles. These make great personalized gifts for family members or unique decorative items for your home.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Make an Art Book or Portfolio: Create a special art book or portfolio for each child where you can store and organize their artwork. This helps in reducing clutter and makes it easy to look back on their creative growth over the years. You can categorize the pieces by date or theme and add notes or stories about each piece.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>These ideas can help you cherish your children&amp;rsquo;s artistic expressions while keeping your home organized and clutter-free.&lt;/p>
&lt;/div>
&lt;/details>
&lt;details>
&lt;summary>OpenAI API Response&lt;/summary>
&lt;div>&lt;p>Absolutely, finding creative ways to preserve and showcase your kids&amp;rsquo; artwork can be both fun and practical. Here are five ideas:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Create a Rotating Gallery:&lt;/strong> Dedicate a wall or a section of a wall in your home to be a gallery for your kids&amp;rsquo; artwork. Use frames, clipboards, or a wire with clothespins to create a rotating display. You can change the displayed pieces every few weeks or months, giving each piece its moment to shine without overwhelming your space.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Make a Photo Book:&lt;/strong> Scan or photograph the artwork and create a professional-looking photo book. There are many online services that allow you to design custom photo books. This way, you can keep the original works stored safely while enjoying the collection in a compact, accessible form.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Turn Art into Gifts:&lt;/strong> Use the artwork to create personalized gifts for family members. Some ideas include:&lt;/p>
&lt;ul>
&lt;li>Printing their art on mugs, t-shirts, or tote bags.&lt;/li>
&lt;li>Using the drawings to create custom calendars or greeting cards.&lt;/li>
&lt;li>Making a set of coasters with their art laminated or encased in resin.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Create a Digital Slideshow:&lt;/strong> Digitize the artwork and set up a digital photo frame or a smart display in your home to show a continuous slideshow of the art. It will free up physical space while still celebrating your child&amp;rsquo;s creativity.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Make Collages or Scrapbooks:&lt;/strong> Combine several pieces of artwork into a larger collage or scrapbook. This can be a seasonal activity where you make collages that reflect the art produced over a certain period. Kids can get involved in the process, helping to cut, arrange, and glue their works, which makes it a fun and engaging family project.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>These creative ideas can help you manage the clutter while still cherishing and celebrating the special art your kids create.&lt;/p>
&lt;/div>
&lt;/details>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Tell me a random fun fact about the Roman Empire
&lt;details>
&lt;summary>ChatGPT Plus Response&lt;/summary>
&lt;div>The Roman Empire was known for its impressive engineering feats, including the construction of a vast network of roads. One fun fact is that the saying &amp;ldquo;All roads lead to Rome&amp;rdquo; originates from the Roman Empire&amp;rsquo;s extensive road system. By the peak of the empire, this network spanned over 400,000 kilometers (approximately 250,000 miles), with around 80,000 kilometers (about 50,000 miles) of paved roads. These roads were crucial for the movement of troops, trade, and communication across the vast territories of the empire.&lt;/div>
&lt;/details>
&lt;details>
&lt;summary>OpenAI API Response&lt;/summary>
&lt;div>Sure! Did you know that the Romans had a highly advanced form of concrete that was incredibly durable? One of the key ingredients they used was volcanic ash, which allowed the concrete to set underwater and contributed to the longevity of many Roman structures. This special mix made their piers, aqueducts, and buildings remarkably resilient, many of which still stand today. This ancient recipe has even been studied by modern engineers to improve contemporary construction materials!&lt;/div>
&lt;/details>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Show me a code snippet of a website&amp;rsquo;s sticky header in CSS and JavaScript.
&lt;details>
&lt;summary>ChatGPT Plus Response&lt;/summary>
&lt;div>&lt;p>To create a sticky header for a website, you can use a combination of CSS and JavaScript. Here&amp;rsquo;s a basic example:
HTML&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;!DOCTYPE html&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">html&lt;/span> &lt;span class="na">lang&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;en&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">charset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;UTF-8&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;viewport&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;width=device-width, initial-scale=1.0&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Sticky Header Example&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;styles.css&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">body&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">header&lt;/span> &lt;span class="na">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;header&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">nav&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">ul&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;#&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Home&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;#&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>About&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;#&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Services&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;#&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Contact&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">ul&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">nav&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">header&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">main&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">section&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Welcome to Our Website&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Content goes here...&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">section&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">main&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;script.js&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">body&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">html&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>CSS (styles.css)&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">body&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nt">html&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">margin&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">font-family&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Arial&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">sans-serif&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">header&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#333&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">white&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">position&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">fixed&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">width&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="kt">%&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">top&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">left&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">z-index&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">transition&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">background-color&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span> &lt;span class="kc">ease&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">header&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">sticky&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#000&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">header&lt;/span> &lt;span class="nt">nav&lt;/span> &lt;span class="nt">ul&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">list-style&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">none&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">margin&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">display&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">flex&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">justify-content&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">center&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">header&lt;/span> &lt;span class="nt">nav&lt;/span> &lt;span class="nt">ul&lt;/span> &lt;span class="nt">li&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">margin&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="mi">15&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">header&lt;/span> &lt;span class="nt">nav&lt;/span> &lt;span class="nt">ul&lt;/span> &lt;span class="nt">li&lt;/span> &lt;span class="nt">a&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">white&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">text-decoration&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">none&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">font-size&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">main&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">margin-top&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">70&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c">/* To avoid content overlap with the sticky header */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">20&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>JavaScript (script.js)&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">Copy&lt;/span> &lt;span class="nx">code&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;scroll&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">header&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getElementById&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;header&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">sticky&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">header&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">offsetTop&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pageYOffset&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="nx">sticky&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">header&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">classList&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;sticky&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">header&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">classList&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">remove&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;sticky&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Explanation&lt;/p>
&lt;ol>
&lt;li>HTML Structure: A basic structure with a header and main content.&lt;/li>
&lt;li>CSS:&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>header is styled to be fixed at the top of the page with a dark background.&lt;/li>
&lt;li>.sticky class changes the background color of the header when it becomes sticky.&lt;/li>
&lt;li>main has a top margin to prevent content from being hidden under the header.&lt;/li>
&lt;/ul>
&lt;ol start="3">
&lt;li>JavaScript:&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>The script adds a sticky class to the header when the user scrolls past the header&amp;rsquo;s initial position.&lt;/li>
&lt;li>This script listens to the scroll event and checks the page&amp;rsquo;s Y offset.&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/details>
&lt;details>
&lt;summary>OpenAI API Response&lt;/summary>
&lt;div>&lt;p>Certainly! Below is a basic example of how to create a sticky header using HTML, CSS, and JavaScript.&lt;/p>
&lt;h3 id="html">HTML&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;!DOCTYPE html&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">html&lt;/span> &lt;span class="na">lang&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;en&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">charset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;UTF-8&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;viewport&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;width=device-width, initial-scale=1.0&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Sticky Header&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;styles.css&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">body&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">header&lt;/span> &lt;span class="na">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;header&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Sticky Header&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">header&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">main&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- Repeat the paragraph or add more content to create a scrollable page --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">main&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;scripts.js&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">body&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">html&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="css-stylescss">CSS (styles.css)&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">body&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">font-family&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Arial&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">sans-serif&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">margin&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">header&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#333&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">white&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">position&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">sticky&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c">/* CSS `position: sticky` makes the header sticky */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">top&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c">/* The header sticks to the top */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">width&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="kt">%&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">z-index&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c">/* Ensures the header is above other content */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">transition&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">top&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">main&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">20&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">p&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">20&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="javascript-scriptsjs">JavaScript (scripts.js)&lt;/h3>
&lt;p>No JS is strictly required for a sticky header, but you might want to add some JS for additional functionality, such as hiding the header on scroll down and showing it on scroll up. Here&amp;rsquo;s an example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;DOMContentLoaded&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">header&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getElementById&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;header&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">lastScrollY&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scrollY&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;scroll&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scrollY&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="nx">lastScrollY&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Scrolling down, hide the header
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">header&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">style&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">top&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;-60px&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Adjust the value to match the header height
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Scrolling up, show the header
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">header&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">style&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">top&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;0&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">lastScrollY&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scrollY&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="explanation">Explanation:&lt;/h3>
&lt;ol>
&lt;li>&lt;strong>HTML&lt;/strong>: Defines the structure with a &lt;code>header&lt;/code> and &lt;code>main&lt;/code> content.&lt;/li>
&lt;li>&lt;strong>CSS&lt;/strong>:
&lt;ul>
&lt;li>&lt;code>position: sticky&lt;/code> with &lt;code>top: 0&lt;/code> makes the header stick to the top of the page.&lt;/li>
&lt;li>&lt;code>z-index: 1000&lt;/code> ensures the header stays above other content.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>JavaScript&lt;/strong>: Adds interactivity to hide the header when scrolling down and show it when scrolling up, using the scroll event listener and comparing the current scroll position with the previous one.&lt;/li>
&lt;/ol>
&lt;p>This is a very basic example. Depending on your project needs, you might have to make additional adjustments.&lt;/p>
&lt;/div>
&lt;/details>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>As you can see whilst the responses are not the same, the responses are of a similar quality. If I did not label which system the output came out of I think you would probably struggle to determine which system produced it.&lt;/p>
&lt;h1 id="conclusion">Conclusion&lt;/h1>
&lt;p>As you can see with very little effort I was able to implement a subscription-free, cost effective ChatGPT Plus alternative using the OpenAI API and Open WebUI. In an ideal world you would run the Open WebUI software on a server that only you had access to which I have done.&lt;/p>
&lt;p>Whilst Open WebUI is a quality piece of software there are plenty of alternatives you could leverage to get a similar experience. The OpenAI API is doing the heavy lifting of answering your prompts whilst Open WebUI is providing you with a good interface to interact with it.&lt;/p></description></item><item><title>3 Weeks island hopping in Thailand</title><link>https://www.samtripp.co.uk/posts/3-weeks-island-hopping-in-thailand/</link><pubDate>Mon, 15 Jul 2024 22:10:00 +0100</pubDate><guid>https://www.samtripp.co.uk/posts/3-weeks-island-hopping-in-thailand/</guid><description>&lt;p>This blog post is just a collection of bullet points regarding mine and my wifes trip around Thailand. There is a lot of information online about this great country but it can be a problem to get accurate answers due to some of the information being quite dated. Hopefully my first hand experience will help someone.&lt;/p>
&lt;h1 id="departing-uk-side">Departing UK side&lt;/h1>
&lt;ul>
&lt;li>Booked to fly with EVA AIR. With it being an overnight flight I was able to take a half day on the Friday ensure we made the most of our time.
&lt;ul>
&lt;li>We had to move our flights from February to May/June due to unforeseen circumstances. I was able to do this online with EVA AIR for the fee of £100.&lt;/li>
&lt;li>We were able to check in at the airport free of charge.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>National Express from Bristol to Heathrow was delayed. Fortunately giving ourselves plenty of time this did not impact us but would certainly give myself plenty of breathing room flying from Heathrow again.&lt;/li>
&lt;li>Heathrow was a well oiled machine. Everything straightforward from check in to boarding the flight. Not my first time flying from Heathrow but always impressed with its operation.&lt;/li>
&lt;li>The Flight
&lt;ul>
&lt;li>A meal was provided shortly after takeoff. Did not expect this.&lt;/li>
&lt;li>We got another meal aproximately a couple of hours prior to landing in Bangkok.&lt;/li>
&lt;li>Seats were quite snug in economy.&lt;/li>
&lt;li>Earplugs &amp;amp; Eyemask I brought with me did help with me getting a few hours sleep on the plane.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h1 id="landing-in-bangkok">Landing in Bangkok&lt;/h1>
&lt;ul>
&lt;li>We were doing a self transfer from Bangkok to a flight to Chiang Mai&lt;/li>
&lt;li>We were through passport control and had our bags within 30 - 40 minutes&lt;/li>
&lt;li>We did not requre to fill out a landing card as British citizens which was a change from our previous visit in 2018&lt;/li>
&lt;li>I was able to pick up a tourist sim card in the baggage collection area.
&lt;ul>
&lt;li>A passport must be provided to get a Sim Card in Thailand. This is practically unavoidable.&lt;/li>
&lt;li>Sim cost approximately £25 for unlimited data with True Mobile for 1 month. Other providers were there but went with True as I could pay for the sim with a credit card.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Once passed through the security checkpoints and in the arrival hall make way to the 4th Floor to get to departures for the next flight.&lt;/li>
&lt;li>Was able to check in immediately and go through domestic departures security checkpoint.&lt;/li>
&lt;li>Once past the security checkpoint your limited to vending machines for any food and beverages so worth keeping in mind if you have time to spare.&lt;/li>
&lt;li>Overall the process was painless and would self transfer again for a domestic flight.&lt;/li>
&lt;/ul>
&lt;h1 id="chiang-mai">Chiang Mai&lt;/h1>
&lt;ul>
&lt;li>Baggage collection again was straightforward.&lt;/li>
&lt;li>Download Grab onto your mobile phone for taxi services in Chiang Mai including getting to and from the airport&lt;/li>
&lt;li>To get to the grab pick up point exit the airport and turn left. Keep walking until you see a grab shelter&lt;/li>
&lt;li>Was picked up in seconds for 150฿&lt;/li>
&lt;li>Stayed &lt;a href="https://www.booking.com/Share-aYjCaF">Glory Suites&lt;/a>
&lt;ul>
&lt;li>Hotel was lovely well furnished, had a rooftop pool and reseraunt which served great food&lt;/li>
&lt;li>Slightly far from the tourist areas but with Grab accessible and affordable this was not much of a problem.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Most of the tourist/expat entertainment happens around Loi Kroh Rd, the night bazaar and near Moon Muang.&lt;/li>
&lt;li>Visited multiple temples in Chiang Mai. If you choose to visit one I would reccomend Wat Chedi Luang. There is a seperate entrance for tourists. Entry cost around 50฿ per person.&lt;/li>
&lt;li>There was an incredible resteraunt across the road from Wat Chedi Luang called Sattaya Izakaya. Would reccomended the skewers as they were excellent.&lt;/li>
&lt;li>Biggest regret was not being more prepared for Chiang Mai. Whilst there is plenty to keep you busy for a couple of days in Chiang Mai city there is a lot more offered further out of the city. Would reccomend others to look up some excursions before arriving. Plenty of tour operators to book from inside Chiang Mai so booking online in advance may not be nessecary.&lt;/li>
&lt;/ul>
&lt;h1 id="koh-samui">Koh Samui&lt;/h1>
&lt;ul>
&lt;li>Grab is not an option from the airport. When you collect your luggage you can (and probably should) book a taxi at the desk in the luggage collection area. SmackONE I believe the company was called. A taxi to Chewang cost 400฿ for 2 people.&lt;/li>
&lt;li>Stayed &lt;a href="https://www.booking.com/Share-j6CH8H">J4 Samui Hotel&lt;/a>.&lt;/li>
&lt;li>As a guest at this hotel you get to utilise the pool and beach at Chewang Gardens which is right next to J4.&lt;/li>
&lt;li>Room is quite close to the strip in Chewang and is right next to ARKBar. Whilst we found the rooms adequately soundproofed if you&amp;rsquo;re sensitive to sound you may want to use some earplugs.&lt;/li>
&lt;li>If you like to do a bit of shopping Chewang has a large shopping center with a lot of large well known brand and a great food court.&lt;/li>
&lt;li>Thai massages and various beauty treatments are plentiful in Chewang so be sure to utilise this whilst you&amp;rsquo;re here.&lt;/li>
&lt;li>A large number of places will take Mastercard but fully expect to pay a minimum of 3% extra in most places. I believe this is to cover Mastercard fees.&lt;/li>
&lt;li>Despite a lot of reports saying this is not the place to go and see tropical fish we saw a fair amount when we went snorkeling on the beach.&lt;/li>
&lt;li>The beach in Chewang was delightful and we spent a lot of our time here. - The staff at Chewang gardens were very friendly and do go above and beyond. Worth considering staying there for the excellent service alone.&lt;/li>
&lt;li>When departing to go to Koh Phanagan we used Grab to take us to Bangrak pier which was the nearest ferry port to Chewang.
&lt;ul>
&lt;li>Due to weather conditions we had to sail from Nanthon pier but the transfer to there was provided by Lompayah free of charge.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h1 id="koh-pha-ngan">Koh Pha-ngan&lt;/h1>
&lt;ul>
&lt;li>This is a notorious party island and home of the infamous full moon party and many other parties in between. Despite this if you&amp;rsquo;re not looking to party like us you can completely avoid it. You probably would want to avoid Haad Rin though if you do not intend to do a lot of partying.&lt;/li>
&lt;li>We stayed in the Baan Tai area of the island&lt;/li>
&lt;li>Speedboat to Koh Pha-ngan cost 350฿ each booked directly via &lt;a href="https://lomprayah.com/">Lomprayah&lt;/a>&lt;/li>
&lt;li>Taxi cost 400฿ from Thong Sala port to hotel. We captured ours from the car park outside the port.&lt;/li>
&lt;li>Stayed &lt;a href="https://www.booking.com/Share-1UZKiRv">Pratchee Resort&lt;/a>.
&lt;ul>
&lt;li>We were the only people staying on the resort during our entire stay. It was a tranqual peaceful paradise which was something I did not expect from this island.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Did not roam far from this area of the island. Managed to get sunburnt and didn&amp;rsquo;t fancy making the problem any worse.&lt;/li>
&lt;li>If you did wish to roam the island moped rental shops are plentiful.&lt;/li>
&lt;li>Nearest 7/11 was aproximately 7 minute walk from hotel.&lt;/li>
&lt;li>Baan Tai Night Market catered to most of our entertainment needs whilst we were here. Plenty of food to sample here along with some excellent cocktails. Live music was on each night also.&lt;/li>
&lt;li>Cash is king on the island. With the exception of 7/11 and the hotel I did not notice card being accepted. Fortunately there are multiple ATMs near the 7/11.&lt;/li>
&lt;li>Krungsai Bank ATM fee was 220฿. Probably the cheapest you will find in the area.&lt;/li>
&lt;/ul>
&lt;h1 id="koh-tao">Koh Tao&lt;/h1>
&lt;ul>
&lt;li>Ferry to Koh Tao cost 600฿ each booked directly via &lt;a href="https://lomprayah.com/">Lomprayah&lt;/a>&lt;/li>
&lt;li>Getting off the boat was quite chaotic as we had to get the bags from the front of the boat and then exit the back but there was no structure to this and it was a complete free for all.&lt;/li>
&lt;li>To enter Koh Tao you need to pay a 20฿ pier fee. This usually is not included in your ferry fee.&lt;/li>
&lt;li>Our accomodation offered a free transfer to the hotel. It appeared many others did also so worth considering taking up their offer if availible.&lt;/li>
&lt;li>We stayed at &lt;a href="https://www.booking.com/Share-79ChBA">Koh Tao Beach Club&lt;/a> on Mae Head Beach.
&lt;ul>
&lt;li>We have a Beach Bungalow. It was lovely to be on the front of the beach.&lt;/li>
&lt;li>Internet access was very unreliable and when it did work was very poor. Mobile signal also was nonexistant so tethering was not an option.&lt;/li>
&lt;li>We were able to get clothes washed with a 24h turnaround arranged by the hotel. Cost was 50฿ per kg.&lt;/li>
&lt;li>If you paid for breakfast in your room rate be sure to get your coupons on check in.&lt;/li>
&lt;li>Would be happy enough to stay there again if the price was right however would consider staying on Sairee Beach if there was a next time as that appears to be a very popular location on the island.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>We booked a snorkeling excursion with &lt;a href="https://www.oxygentourskohtao.com/">Oxygen&lt;/a>. They did 5 stops in great scuba locations around the island. The tour was well ran and all the staff we&amp;rsquo;re very helpful. We managed to swin with giant turtles at one of the locations. We were able to book this via our hotel but you do see a lot of advertisements of theirs along Sairee beach&lt;/li>
&lt;li>I did a try dive with &lt;a href="https://www.phoenixdiverskohtao.com">Phoenix Divers&lt;/a>. It cost 3500฿ for 2 dive try dive, diving theory and a practice in their pool.
&lt;ul>
&lt;li>I was able to arrange the majority of this via Whatsapp. They were very responsive. I just needed to pop down to their site on Sairee Beach to fill out paperwork and pay.&lt;/li>
&lt;li>I loved this experienced and I am hooked. Regretted not booking the Open Water Diving course but unfortunately time was not on my side. I will be doing this in the near future.&lt;/li>
&lt;li>Koh Tao is renound for diving globally so if you are curious about diving this is certainly the place to try it.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>We had plenty of nice food in Koh Tao but the one place that stood out was Sandbar. They did a BBQ platter which was absolutely fantastic.&lt;/li>
&lt;/ul>
&lt;h1 id="bangkok">Bangkok&lt;/h1>
&lt;ul>
&lt;li>To get back to Bangkok it was a ferry back to Chumpthon and then a night train&lt;/li>
&lt;li>We booked a ferry/taxi combined ticket via &lt;a href="https://12go.asia/">12go.asia&lt;/a> as there appeared no way to do this online via Lomprayah.&lt;/li>
&lt;li>We booked the night train back from Chumpthon to Krung Thep Aphiwat (Bangkok) via the &lt;a href="https://www.dticket.railway.co.th/">State Railway of Thailand D-Ticket site&lt;/a>
&lt;ul>
&lt;li>First class private room cost 2180฿ for the two of us.&lt;/li>
&lt;li>You can only book these tickets usually 1 month in advance.&lt;/li>
&lt;li>The D-Ticket website was incredibly slow to operate from the UK so patience would be needed. It was however very fast when accessed in Thailand.&lt;/li>
&lt;li>Was able to get some sleep. Was not the most comfortable journey in the world but beats an 8 hour coach to Bangkok&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>We probably gave ourselves far too much time to be on the safe side. If I was to do this again I would probably book on the first night train availible after arriving via boat to Chumpthon&lt;/li>
&lt;li>We arrived in Bangkok at 6:30.&lt;/li>
&lt;li>We got a Grab to our accomodation.&lt;/li>
&lt;li>We stayed at &lt;a href="https://www.booking.com/Share-H6eqVu">Villa de Khaosan&lt;/a> which is on the corner of the famous backpackers street of Khaosan Road.
&lt;ul>
&lt;li>Would stay again accomodation was comfortable and location was perfect.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>We visited Jim Thompsons House Museum whilst we stayed.
&lt;ul>
&lt;li>Jim Thompson was a famous silk merchant based in Thailand who was largely considered to have saved the Thai silk industry&lt;/li>
&lt;li>This was a very insightful and informative tour which I was glad to do. My wife was especially happy to see this being a keen textile artist.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>We paid a visit to the MBK Shopping Center as it was near Jim Thompsons House.
&lt;ul>
&lt;li>Given its size and boasting about a considerable sum of shops I was not particularly impressed. Felt more like a bazaar over a western standard shopping mall.&lt;/li>
&lt;li>Food Court was good and had plenty of choice if you did want to eat nearby.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Grab back to Khaosan road was pretty slow due to Bangkok traffic.&lt;/li>
&lt;/ul>
&lt;h1 id="back-to-the-uk">Back to the UK&lt;/h1>
&lt;ul>
&lt;li>Got a Grab from the accomodation to airport. Worth noting that your quoted price does not include tolls but the driver can and will add them at the end of your journey. The driver was upfront about this.&lt;/li>
&lt;li>Check in and security was seamless and nothing of noteworthy.&lt;/li>
&lt;li>We were able to get a tax refund on some items. If you&amp;rsquo;re buying any expensive products this is worth considering. The refund took a couple of minutes to process. Refund was paid in cash.&lt;/li>
&lt;li>Managed to get emergency exit seats on the way back. On a 12 hour flight the extra legroom is a blessing.&lt;/li>
&lt;li>When landing in Heathrow boarder control queue was quite long but didn&amp;rsquo;t take much longer than 10 minutes to pass through.&lt;/li>
&lt;li>Baggage collection was quite slow.&lt;/li>
&lt;li>We were able to get on the National Express an hour earlier than planned thanks to the very friendly driver letting us on. Will be booking National Express to Heathrow next time just because of that alone.&lt;/li>
&lt;/ul></description></item><item><title>Terraforming my Cloudflare configuration</title><link>https://www.samtripp.co.uk/posts/terraforming-my-cloudflare-configuration/</link><pubDate>Mon, 29 Apr 2024 16:10:00 +0100</pubDate><guid>https://www.samtripp.co.uk/posts/terraforming-my-cloudflare-configuration/</guid><description>&lt;p>A couple of weeks ago I was having a conversation with a friend regarding Cloudflare. He was saying how it is not uncommon for people to make configuration changes using the Cloudflare UI potentially introducing breaking changes that could go unnoticed. I meantioned that with Infrastructure-as-Code implemented there is no excuse for this to happen. I also came to a realisation that I do not practice what I preached so to rectify this I have decided to capture my Cloudflare configuration into my Infrastructure-as-Code tool of choice Terraform.&lt;/p>
&lt;p>Cloudflare handles a number of things for me on this domain but most notably it handles my DNS records for this website and my emails. An outages of either of these would not be ideal for me so to avoid this we are going to use &lt;a href="https://github.com/cloudflare/cf-terraforming">cf-terraforming&lt;/a> to generate Terraform code based on my current DNS configuration in the Cloudflare UI and to generate the terraform import commands so I can import my Cloudflare configuration into my Terraform state.&lt;/p>
&lt;h1 id="building-the-application">Building the application&lt;/h1>
&lt;p>Unfortunately cf-terraforming is not packaged in the Debian repositories and there is no flatpak or snap availible for it so we do have to resort to building it. If you&amp;rsquo;re following along on a Macbook there is a brew package for this which would save some time. Fortunately building this was incredibly straigtforward:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git clone https://github.com/cloudflare/cf-terraforming.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> cf-terraforming
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">make build
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>There may be some dependancies that need to be installed such as the Go programming language but I already had this on my laptop. Once the make command has successfully ran a binary file will appear in the same directory called cf-terraforming. I was able to interact with this binary in the terminal by running &lt;code>./cf-terraforming&lt;/code>&lt;/p>
&lt;h1 id="create-cloudflare-api-token-with-sufficiant-permissions">Create Cloudflare API token with sufficiant permissions&lt;/h1>
&lt;p>I needed to create a Cloudflare API token so cf-terraforming and Terraform can interact with Cloudflare. To do this I navigate to the &lt;a href="https://dash.cloudflare.com/profile/api-tokens">Cloudflare API Token page&lt;/a>, Select create token and then create custom token. Give the token sufficiant privalages to do what I needed it to do. For the purpose of DNS I selected Zone DNS Edit from the permissions dropdown and under the Zone Resource dropdown I selected Include Specific Zone samtripp.co.uk. I then followed the rest of the wizard to create my API Token.&lt;/p>
&lt;p>Once this was created I was able to export my API Token along with my zone id which can be found on the Cloudflare dashboard onto my laptop as an environment varible in my terminal&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">CLOUDFLARE_API_TOKEN&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;YOURAPITOKEN&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">CLOUDFLARE_ZONE_ID&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;YOURZONEID&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="initialise-terraform-working-directory-with-cloudflare-module-present">Initialise Terraform working directory with Cloudflare module present&lt;/h1>
&lt;p>To start using the cf-terraforming tool I need to add the &lt;a href="https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs">Cloudflare provider&lt;/a> to my terraform code. I created a &lt;code>cloudflare.tf&lt;/code> file and added the following&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="line">&lt;span class="cl">&lt;span class="k">terraform&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">required_providers&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> cloudflare&lt;/span> &lt;span class="o">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> source&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;cloudflare/cloudflare&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> version&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;~&amp;gt; 4.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Once this was added I ran a &lt;code>terraform init&lt;/code> to initialise the terraform working directory.&lt;/p>
&lt;h1 id="generating-the-cloudflare-terraform-code">Generating the Cloudflare terraform code&lt;/h1>
&lt;p>This is where the magic happens. I was able to generate the Cloudflare terraform code from the terraform working directory by running the following command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cf-terraforming generate --resource-type &lt;span class="s2">&amp;#34;cloudflare_record&amp;#34;&lt;/span> --zone &lt;span class="nv">$CLOUDFLARE_ZONE_ID&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This outputted:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="line">&lt;span class="cl">&lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;cloudflare_record&amp;#34; &amp;#34;terraform_managed_resource_05e4f175dfbb21e818e9ff4e30e75796&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;samtripp.co.uk&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> proxied&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kt">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> ttl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> type&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;CNAME&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;dnsrecordvalue.co.uk&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> zone_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;myzoneid&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Perfect that worked as expected. I copied the output into a new file called &lt;code>cloudflare-dns.tf&lt;/code>. I ran a &lt;code>terraform plan&lt;/code> and saw that terraform wants to create the infrastructure. I need to import this resource into the terraform state to avoid destroying anything already configured. To do this I ran the following command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cf-terraforming import --resource-type &lt;span class="s2">&amp;#34;cloudflare_record&amp;#34;&lt;/span> --zone &lt;span class="nv">$CLOUDFLARE_ZONE_ID&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This outputted:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">terraform import cloudflare_record.terraform_managed_resource_05e4f175dfbb21e818e9ff4e30e75796 myzoneid/05e4f175dfbb21e818e9ff4e30e75796
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This command would be sufficiant to import the Cloudflare DNS record into terraform state. One thing I was not happy about was the names of these resources. As this was not in state yet I was able to manually edit the resource name to something more meaningful.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="line">&lt;span class="cl">&lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;cloudflare_record&amp;#34; &amp;#34;cname_root&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Once renamed I ran the terraform import command with the ammended name&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">terraform import cloudflare_record.cname_root myzoneid/05e4f175dfbb21e818e9ff4e30e75796
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The Cloudflare DNS record is now in state. I was able to run a &lt;code>terraform plan&lt;/code> and see there are no changes to be made to my infrastructure. If you have multiple DNS records like I did you may want to run import and then plan each time to ensure no issues. You will see the number of changes to be made decrease after each successful import.&lt;/p>
&lt;h1 id="add-cloudflare-api-key-to-github-actions">Add Cloudflare API key to Github Actions&lt;/h1>
&lt;p>Github Actions provides me with my all my Continuious Intergration and Continuous Delivery needs. I use &lt;a href="https://github.com/dflook/terraform-github-actions">dflook/terraform-github-actions&lt;/a> to give me the functionality of running a terraform plan and apply when I make infrastructure changes. For Github Actions to interact with Cloudflare via Terraform I will need to add my API key to Github Actions. I managed this by navigating to my Github repository, select Settings &amp;gt; Secrets and Varibles &amp;gt; Actions &amp;gt; New Repository Secret. I named the secret CLOUDFLARE_API_TOKEN and then added the API token that I generated earlier. Once thats done I added the following lines to my Github actions .yaml file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">env:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> CLOUDFLARE_API_TOKEN: &lt;span class="si">${&lt;/span>&lt;span class="p">{ secrets.CLOUDFLARE_API_TOKEN &lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="conclusion">Conclusion&lt;/h1>
&lt;p>The cf-terraforming tool was straightforward to use and made the process of importing some of my Cloudflare configuration into Terraform a pain-free experience. Credit to all the developers involved in making this tool possible. I have also been able to import my page rules successfully following the same process as above. I will continued to use this tool to capure the rest of my Cloudflare configuration into Infrastructure-as-Code.&lt;/p>
&lt;p>It does not support every resource type in the Cloudflare provider currently but it at least covers some of the critical ones that could be disruptive without being able to import it into my Terraform state.&lt;/p></description></item><item><title>Migrate from LineageOS for microG to LineageOS with microG</title><link>https://www.samtripp.co.uk/posts/migrate-from-lineageos-for-microg-to-lineageos-with-microg/</link><pubDate>Fri, 12 Apr 2024 08:00:00 +0100</pubDate><guid>https://www.samtripp.co.uk/posts/migrate-from-lineageos-for-microg-to-lineageos-with-microg/</guid><description>&lt;p>I&amp;rsquo;ve decided to migrate my phones OS from &lt;a href="https://lineage.microg.org/">LineageOS for microG&lt;/a> to &lt;a href="https://lineageos.org/">LineageOS&lt;/a> with &lt;a href="https://microg.org/">microG&lt;/a> and I&amp;rsquo;ve documented my process of doing this.&lt;/p>
&lt;h1 id="why-use-lineageos">Why use LineageOS&lt;/h1>
&lt;p>I&amp;rsquo;ve ran LineageOS back when it was called Cynogenmod on my OnePlusOne and I&amp;rsquo;ve stuck to it ever since on my Android phones. I liked how it was a clean consistent Android OS which was well supported across many phones meaning if my device broke and was beyond economical repair I can get a new supported device and enjoy the same experience that I had before. It also prolongs the life of my phones as the majority of phones will get 2 to 3 years OS updates from manufacturers whereas LineageOS is still providing updates for my previous phone, a Xiaomi Pocophone F1 which was released in 2018. This allows me to run a system with the latest android securirty patches and reduces the creation of unnessecary e-waste.&lt;/p>
&lt;h1 id="why-use-microg">Why use microG&lt;/h1>
&lt;p>It may be no great surprise to anyone how much information Google collects on any given person but people may overlook how much data the Google Play services collect on you. I can&amp;rsquo;t tell you exactly how much they collect or what they collect because it is not open source. That is a problem in its own right as I can&amp;rsquo;t audit what the Play Services are collecting on me. Sadly using Android without the Play Services whilst not impossible is a less desirable experience. A lot of developers utilise a lot of the Google Play API&amp;rsquo;s in their apps which will mean that some apps will either malfunction or not behave as designed when Google Play services are not installed. This is where microG enters the arena. It bridges the gap between usability and open. Google may still get some metrics whilst using microG but its just enough to ensure that the various apps I want to use now work. Its not perfect but a pragmatic compromise.&lt;/p>
&lt;h1 id="why-did-i-use-lineageos-for-microg">Why did I use LineageOS for microG&lt;/h1>
&lt;p>The official LineageOS team took a stance that they would not allow signature spoofing. This was required by microG to pretend to be the Google Play Services so apps called its reimplemented API&amp;rsquo;s. This was largely driven by security concerns which is somewhat fair however with this being a nessecity for microG an unofficial fork was created and the &lt;a href="https://lineage.microg.org/">LineageOS for microG&lt;/a> custom ROM was born. This ROM was LineageOS with MicroG built in and was arguably the easiest way for me to get both LineageOS and microG in one convenient package.&lt;/p>
&lt;h1 id="why-migrate-away-from-lineageos-for-microg">Why migrate away from LineageOS for microG&lt;/h1>
&lt;p>&lt;a href="https://review.lineageos.org/c/LineageOS/android_frameworks_base/+/383574">LineageOS/android_frameworks_base change 383574&lt;/a> has now allowed the microG to signature spoof. This has made LineageOS for microG custom rom redundant. Whilst they&amp;rsquo;ve not stopped releases immediately I am predicting in the near future they may do this.&lt;/p>
&lt;p>Other than speculation there are a couple of minor benefits to moving back to the official LineageOS release, most notably frequent updates. For my device there is a weekly release for the official LineageOS custom ROM. As a result of this I will get security fixes and any other changes quicker than LineageOS for microG custom ROM which was a monthly release.&lt;/p>
&lt;h1 id="how-am-i-migrating">How am I migrating&lt;/h1>
&lt;p>Inconveniently its not as simple as installing the official LineageOS release over LineageOS for MicroG so the plan is as follows:&lt;/p>
&lt;ol>
&lt;li>Take a full nandroid backup just incase it goes horribly wrong and I need to revert&lt;/li>
&lt;li>Backup all applications and their data&lt;/li>
&lt;li>Install latest phone firmware&lt;/li>
&lt;li>Install LineageOS Recovery&lt;/li>
&lt;li>Install LineageOS&lt;/li>
&lt;li>Restore all applications and their data&lt;/li>
&lt;li>Install microG&lt;/li>
&lt;/ol>
&lt;h1 id="prerequisite">Prerequisite&lt;/h1>
&lt;p>adb and fastboot needs to be installed on the computer. On a Debian based system this is as simple as running the following command in the terminal.&lt;/p>
&lt;pre tabindex="0">&lt;code>sudo apt install adb fastboot
&lt;/code>&lt;/pre>&lt;h1 id="take-full-nandriod">Take full nandriod&lt;/h1>
&lt;p>To do this I will be using &lt;a href="https://xdaforums.com/t/recovery-12-13-14-official-orangefox-recovery-project-r11-1.4472617/">OrangeFox recovery&lt;/a>. I followed &lt;a href="https://wiki.orangefox.tech/en/guides/installing_orangefox">this guide&lt;/a> to get OrangeFox recovery installed.&lt;/p>
&lt;p>Once booted into OrangeFox recovery I then took a backup of boot, data &amp;amp; super partitions. This should hopefully be enough for me to abort and revert if nessecary.&lt;/p>
&lt;p>Once this has done I took a copy of the Fox directory from the phone onto the computer.&lt;/p>
&lt;h1 id="backup-all-applications-and-their-data">Backup all applications and their data.&lt;/h1>
&lt;p>To do this we are utilising the built in backup functionality in LineageOS which is &lt;a href="https://github.com/seedvault-app/seedvault">seedvault&lt;/a>. This is not a perfect solution as we&amp;rsquo;re depending on app makers to implement backups the Android standard way. As a result of doing this I am expecting to lose my Whatsapp chat history. I could backup to Google Drive if I really was that precious over the chat history but there is nothing that I need to keep there and I was not willing to backup to Google Drive.&lt;/p>
&lt;p>To turn on seedvault backups I did this in Settings &amp;gt; System &amp;gt; Backup &amp;gt; Seedvault Backup&lt;/p>
&lt;p>Once I completed my backup I took a copy of the .SeedVaultAndroidBackup directory onto the computer.&lt;/p>
&lt;h1 id="install-latest-phone-firmware">Install latest phone firmware&lt;/h1>
&lt;p>As I have been running LineageOS for quite a while there is a possibility that my phone firmware is out of date. On Xiaomi phones these usually get delivered as part of the MIUI update. Fortunately the kind folks at &lt;a href="https://xiaomifirmwareupdater.com/">Xiaomi Firmware Updater&lt;/a> provide flashable firmware meaning you do not need to install MIUI to get firmware updates. Firmware updates can bring driver fixes and security updates to your device so there is value to doing this periodically. Now seems a perfect time given we&amp;rsquo;re doing a full system refresh.&lt;/p>
&lt;p>To do this I downloaded the latest firmware from Xiaomi Firmware Updater. I then rebooted my device into recovery. Once in recovery I went to Apply Update &amp;gt; Apply from ADB&lt;/p>
&lt;p>Once ADB is enabled I should be able to run this command from the directory where I downloaded the firmware from inside my terminal:&lt;/p>
&lt;pre tabindex="0">&lt;code>adb sideload fw_alioth_miui_ALIOTHEEAGlobal_V14.0.11.0.TKHEUXM_067e9d6f67_13.0.zip
&lt;/code>&lt;/pre>&lt;p>A popup on the phone will say Signiture verification Failed install anyway? I selected yes here. This is due to the flashable firmware not being signed by the LineageOS Team.&lt;/p>
&lt;h1 id="install-lineageos-recovery-and-lineageos">Install LineageOS Recovery and LineageOS&lt;/h1>
&lt;p>To avoid reinventing the wheel the folks over at LineageOS have a very good guide on how to install LineageOS for the Xiaomi Poco F3 on &lt;a href="https://wiki.lineageos.org/devices/alioth/install/variant1/">their website&lt;/a>.&lt;/p>
&lt;p>If you&amp;rsquo;re coming from a custom rom before some of these steps are not nessecary such as unlocking bootloaders. It is worth noting at this point I will be resetting the phone to factory settings so be sure to take a copy of anything important as data will be lost beyond this point.&lt;/p>
&lt;p>Once those steps are complete I rebooted the device and follow the wizard to setup the device for the first time.&lt;/p>
&lt;h1 id="restoring-applications-and-data">Restoring applications and data&lt;/h1>
&lt;p>As a result of resetting the phone I need to copy our backup onto the phone. To do this I copied the .SeedVaultAndroidBackup from the computer back onto the phone. Once thats completed open the phone dialer and type &lt;code>*#*#7378673#*#*&lt;/code>. This will open up the seedvault restore process.&lt;/p>
&lt;p>I was plesantly surprised at what a good job seedvault did at restoring my apps. Only a couple had issues being restored. One being F-Droid and the others being microG components. Both will be installed in due course.&lt;/p>
&lt;p>The rest of my restore was internal storage items such as photos etc which I was able to move onto the device via the file explorer on my computer.&lt;/p>
&lt;h1 id="install-microg">Install microG&lt;/h1>
&lt;p>The final piece of the jigsaw. To install microG I need to install &lt;a href="https://f-droid.org/">F-Droid from the projects homepage&lt;/a>. Once this is done I can go to settings in F-droid and add the microG repository which can be found on the &lt;a href="https://microg.org/download.html">microG project website&lt;/a>. Once I&amp;rsquo;ve added this I then installed microG Service, microG Service Framework Proxy &amp;amp; microG companion from the F-droid repository. Once completed I rebooted the phone as I patched some key system components. Once rebooted I navigate to microG setting and select self check. I saw a lot of unchecked boxes so click on them to give microG the nessecary permissions to function. Once this has been completed I have done the minimum to get get microG running.&lt;/p>
&lt;h1 id="optional-tasks-that-would-be-worth-doing">Optional tasks that would be worth doing&lt;/h1>
&lt;p>If you&amp;rsquo;re following along with what I did and this is your first time running microG you may want to consider these extra tasks:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>You may want to return to the microG settings and turn on Cloud Messaging. This is used by a lot of applications to handle push notifications.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>You may also want to install a location backend to improve the quality of your location services. There are many backend you can choose for this. I personally use the &lt;a href="https://f-droid.org/en/packages/org.microg.nlp.backend.ichnaea/">MozillaNlpBackend&lt;/a> but you can find a list of them on the &lt;a href="https://github.com/microg/UnifiedNlp?tab=readme-ov-file#usage">UnifiedNlp GitHub repository&lt;/a> which is maintained by the microG project.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>You may also want to install the &lt;a href="https://f-droid.org/packages/com.aurora.store/">Aurora Store&lt;/a>. This is an open source Google Play client that you can use to install your apps. Considerably more privacy respecting in comparision to the official Google Play client.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h1 id="conclusion">Conclusion&lt;/h1>
&lt;p>This was a relatively uneventful OS swap. All worked fairly seamlessly with the exception of losing Whatsapp chat history which could have been avoided if I was willing to back it up to Google Drive. A successful change which will help to prolong the life of my Xiaomi Poco F3. The device has proven to be good value for money up to this point and I will probably stick with it for the foreseeable especially when the current phone market is not offering me much more than what I&amp;rsquo;ve got.&lt;/p></description></item><item><title>A selection of my favorite EuroPython 2023 talks</title><link>https://www.samtripp.co.uk/posts/a-selection-of-my-favorite-europython-2023-talks/</link><pubDate>Sun, 03 Mar 2024 22:00:00 +0000</pubDate><guid>https://www.samtripp.co.uk/posts/a-selection-of-my-favorite-europython-2023-talks/</guid><description>&lt;p>Back in July I attended my first Software Engineering conference &lt;a href="https://ep2023.europython.eu/">EuroPython 2023&lt;/a>. If you&amp;rsquo;re holding off of booking your first conference for various reasons or are unsure of which one to attend I found EuroPython a very friendly and welcoming conference, that is tailored to the developer. I found the conference fairly priced for the value that I got out of it. Unfortunately I can&amp;rsquo;t comment on the tutorials or the sprints as I did not attend them.&lt;/p>
&lt;p>&lt;strong>Would I go to another conference after that experience&lt;/strong>: Yes - I went to &lt;a href="https://fosdem.org/2024">FOSDEM 24&lt;/a>&lt;/p>
&lt;p>&lt;strong>Would I go to EuroPython again&lt;/strong>: Yes, I will give it a couple of years before going again as I&amp;rsquo;ve now got a taste for attending Software Engineering conferences so I would like to check others out first but no doubt I will return in the future.&lt;/p>
&lt;p>The key purpose to this blog post is to reflect on a selection of talks that I found valuable or enjoyed during the conference. There was a wide selection of talks to attend although sadly I could not attend them all. Most of the talks I attended were aimed towards a DevOps audience.&lt;/p>
&lt;h1 id="cli-application-development-made-easier-with-typer--vinícius-gubiani-ferreira">CLI application development made easier with typer — Vinícius Gubiani Ferreira&lt;/h1>
&lt;p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/bLTwNZiMLBE?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
>&lt;/iframe>
&lt;/div>
&lt;a href="https://www.youtube.com/watch?v=bLTwNZiMLBE">Watch on Youtube&lt;/a>&lt;/p>
&lt;p>This talk introduced me to the Typer library. As someone who has built CLI&amp;rsquo;s in the past using such libraries as Argparse and Fire, I quickly discovered that Typer is a game changer. You can get a CLI app running in no time at all using this. I created a proof of concept CLI thanks to this talk. If I was to make a CLI application in the future this would be my go to library.&lt;/p>
&lt;h1 id="optimizing-your-ci-pipelines--sebastian-witowski">Optimizing Your CI Pipelines — Sebastian Witowski&lt;/h1>
&lt;p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/goWcDalv_FQ?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
>&lt;/iframe>
&lt;/div>
&lt;a href="https://www.youtube.com/watch?v=goWcDalv_FQ">Watch on Youtube&lt;/a>&lt;/p>
&lt;p>This talk could not have come at a more appropriate time for me, as when I returned to work I was going to have to spend a lot of time creating pipelines. Some solid advice and recommendations around pipeline creation that I was able to utilise immediately.&lt;/p>
&lt;h1 id="pip-install-malware--max-kahan">pip install malware — Max Kahan&lt;/h1>
&lt;p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/hfoCh4yuJpo?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
>&lt;/iframe>
&lt;/div>
&lt;a href="https://www.youtube.com/watch?v=hfoCh4yuJpo">Watch on Youtube&lt;/a>&lt;/p>
&lt;p>Nothing in this talk was a great shock to me. Plenty of bad actors are deploying malicious packages on PyPI. That being said it was a very well presented talk and is a great reminder to not just blindly install anything off the internet. The key takeway could extend beyond the Python ecosystem.&lt;/p>
&lt;h1 id="designing-a-human-friendly-cli-for-api-driven-infrastructure--oliver-rew">Designing a Human-Friendly CLI for API-Driven Infrastructure — Oliver Rew&lt;/h1>
&lt;p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/6LeB5j331bU?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
>&lt;/iframe>
&lt;/div>
&lt;a href="https://www.youtube.com/watch?v=6LeB5j331bU">Watch on Youtube&lt;/a>&lt;/p>
&lt;p>A great talk discussing closely coupled API&amp;rsquo;s and autogenerated CLI&amp;rsquo;s based off the API documentation. I can think of plenty of times in the past where I would have benefitted from such tooling. The only downside is that this tool is not publicly available, however the insight into well designed tooling was valuable.&lt;/p>
&lt;h1 id="from-idea-to-production--honza-král">From idea to production — Honza Král&lt;/h1>
&lt;p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/02XHM_XvsWs?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
>&lt;/iframe>
&lt;/div>
&lt;a href="https://www.youtube.com/watch?v=02XHM_XvsWs">Watch on Youtube&lt;/a>&lt;/p>
&lt;p>A self declared opinionated checklist for going from an idea into production. The example given was for a Django website but I would say it could be applied to most development projects. Key point to note is setting ideas up with the necessary tooling to succeed from the beginning will save a lot of pain retrofitting it once you&amp;rsquo;ve gone to production.&lt;/p>
&lt;h1 id="honerable-meantion-for-lighting-talk-fk-it---how-to-fix-production-code-in-5-minutes--sebastien-crocquevieille">Honerable meantion for lighting talk F**k it - How to Fix Production Code in 5 Minutes — Sebastien Crocquevieille&lt;/h1>
&lt;p>&lt;a href="https://www.youtube.com/watch?v=IQ9UFhmGC9k&amp;amp;list=PL8uoeex94UhFcwvAfWHybD7SfNgIUBRo-&amp;amp;index=65&amp;amp;t=2919s">Watch on Youtube&lt;/a>&lt;/p>
&lt;p>Bit of light humour to finish off the conference. Content Advisory as you can probably work out there is some bad language in this lightning talk.&lt;/p></description></item><item><title>Digital Security 101: Reinforce you password security with two-factor authentication</title><link>https://www.samtripp.co.uk/posts/digital-security-101-reinforce-you-password-security-with-two-factor-authentication/</link><pubDate>Fri, 09 Feb 2024 12:30:00 +0000</pubDate><guid>https://www.samtripp.co.uk/posts/digital-security-101-reinforce-you-password-security-with-two-factor-authentication/</guid><description>&lt;p>Congratulations, If you&amp;rsquo;re following on from my previous post you have taken the first steps into improving your digital security. Unfortunately what you probably won&amp;rsquo;t want to hear is that strong passwords, whilst an excellent start are just not enough on there own. To ensure you&amp;rsquo;re account is even more secure it is strongly enouraged that you apply two-factor authentication to your accounts wherever this is availible.&lt;/p>
&lt;h1 id="why-should-i-do-it">Why should I do it&lt;/h1>
&lt;p>Imagine if you were at top of the list of people who have had their accounts compromised which account are they likely to start with. Two-factor authentication offers you a second line of defence and could potentially buy you some time to change your password before someone compromises your account. If you enable two-factor authentication you are protecting your account with something you know (your password) and something you have (your device containing your two-factor authentication codes).&lt;/p>
&lt;h1 id="what-are-my-options">What are my options&lt;/h1>
&lt;p>There are many ways to implement two-factor authentication and below I&amp;rsquo;ve listed a selection of the most common and convenient ways of implementing this. Whilst some of these methods are better than others it is always worth keeping in mind that any two-factor authentication is better than no two-factor authentication at all.&lt;/p>
&lt;p>SMS One Time Passwords are quite a common way of implementing two-factor authentication. I would discourage against this unless there is no other option. If someone was able trick your mobile phone operator to issue a Port Authorisation Code, they could move your mobile number to a phone which they possess and would be able to receive your One Time Passwords. You may be thinking whats the probability of this happening, unfortunately it is common enough to have earned the term Simjacking.&lt;/p>
&lt;p>There is a good chance that your password manager has the capabilities of handling your two-factor authentication. &lt;a href="https://keepassxc.org/">KeepassXC&lt;/a> &amp;amp; &lt;a href="https://1password.com/">1Password&lt;/a> are just an example of a couple of password managers that have that capability. Whilst a very convenient way of using two-factor authentication, if someone unfortunately gains access to your password manager they will have everything they need to access your accounts.&lt;/p>
&lt;p>Then there is the Mobile Phone App solution. You may have seen in the security settings of the service that you use &amp;ldquo;protect your account with Google Authenticator&amp;rdquo;. Google Authenticator uses 2 standards called Time-based One Time Passwords and HMAC-based One Time Passwords to generate new codes every 30 seconds to grant you access to an account. Once setup and you&amp;rsquo;ve entered your username and password on a service that you use, you will then be taken to a page where you are asked to enter this code from your device. As you can imagine with this changing every 30 seconds it makes a breach of your account very difficult.&lt;/p>
&lt;p>Which app should you choose? As we just stated above &lt;a href="https://apps.apple.com/gb/app/google-authenticator/id388497605">Google Authenticator&lt;/a> is completely capable of handling your two-factor authentication but there are plenty more applications that can and that are open source, something which Google Authenticator has not been since version 2.21. I was a previous user of Google Authenticator so should you wish to use it then its better than nothing however I personally believe there are better apps that offer a greater feature set.&lt;/p>
&lt;p>I&amp;rsquo;ve tried a few apps for Android such as &lt;a href="https://play.google.com/store/apps/details?id=org.liberty.android.freeotpplus">FreeOTP+&lt;/a>, &lt;a href="https://play.google.com/store/apps/details?id=com.beemdevelopment.aegis">Aegis Authenticator&lt;/a> but the one I did settled on was &lt;a href="https://play.google.com/store/apps/details?id=org.shadowice.flocke.andotp">andOTP&lt;/a>. It&amp;rsquo;s a free and Open Source app that has all the capabilites of Google Authenticator and the extras like backup your one time passcodes and password protect your one time password. I have noticed that andOTP is now unmaintained so you may want to consider one of the other two apps as no doubt I will migrate to one of them soon.&lt;/p>
&lt;h1 id="dont-lock-yourself-out">Don&amp;rsquo;t lock yourself out&lt;/h1>
&lt;p>You may want to take some precautionary measures to ensure you don&amp;rsquo;t accidently lock yourself out of your own accounts. As you can imagine storing these passcodes on a device like your mobile phone is excellent until you lose or break your mobile phone. You could create a new vault in KeepassXC and scan the QR Code (or enter the secret key) in that vault. That way if you lose access to your device you still have the same codes availible to you. Whilst convenient some may advise against this as you have muliple copies of these one time codes now. If you feel that is to much of a security risk then you&amp;rsquo;ll be glad to know there is an alternative and they are called backup codes.&lt;/p>
&lt;p>I can not stress how important backup codes are. If a service offers you backup codes then be sure to take note of them. By taking note of the backup codes these single use passcodes will allow you to regain access to your account allowing you to setup your One Time Passcodes on a new device. The recommended storage method for these codes are on paper and store them in a fire proof safe. I can imagine some people do not have a safe in their house so the next best thing would be to store them in an encrypted method like in a Keepass Vault and store that Password Vault offline.&lt;/p>
&lt;h1 id="how-do-i-do-it">How do I do it&lt;/h1>
&lt;p>I take the same approach for setting up two-factor authentication as I do with my passwords. I protect the most important services I use or the ones that will cause you the most problems if I were breached. You can see if the website/services you use support two-factor authentication by visiting the website &lt;a href="https://2fa.directory">2fa.directory&lt;/a>. You then visit the service you want to set up two-factor authentication on. Scan the QR Code or enter the Secret Key into your app and then enter the passcode to verify the code is working.&lt;/p>
&lt;p>Congratulations you have just taken the next step in protecting yourself with two-factor authentication.&lt;/p>
&lt;h1 id="tldr-too-long-didnt-read">TL;DR (Too Long Didn&amp;rsquo;t Read)&lt;/h1>
&lt;ul>
&lt;li>Use two-factor authentication wherever possible&lt;/li>
&lt;li>Some two-factor authentication is better than no two-factor authentication&lt;/li>
&lt;li>Google Authenticator is not your only choice of app&lt;/li>
&lt;li>Make sure you take note of your backup codes&lt;/li>
&lt;/ul></description></item><item><title>Digital Security 101: Time to start using a password manager</title><link>https://www.samtripp.co.uk/posts/digital-security-101-time-to-start-using-a-password-manager/</link><pubDate>Sun, 28 Mar 2021 11:45:00 +0000</pubDate><guid>https://www.samtripp.co.uk/posts/digital-security-101-time-to-start-using-a-password-manager/</guid><description>&lt;p>I&amp;rsquo;ve written this post as a result of my frustrations with people from all walks of life using terrible password practice. If this post ends up changing one persons way of managing their passwords then it has served its purpose.&lt;/p>
&lt;p>It&amp;rsquo;s time to take back control of your internet security. Breaches are becoming more common and one day you will get caught. You may see it as insignificant as an old email address you used at secondary school or an old social media account that you&amp;rsquo;ve not logged onto for about 10 years but I will go on to explain that it is much more significant than you might think, hopefully before it becomes an invasion of your privacy.&lt;/p>
&lt;p>These breaches are currently worth the hackers time and this is largely down to peoples poor password practices. Whilst you may think your password is a good password all it takes is one breach to make that password a bad password. You probably use that password everywhere from your online banking to your social media accounts. You may think having two or three passwords will outsmart them but I can assure it will not. There is a practice called credential stuffing where hackers will use your username and password from a breach and test it against multiple websites to gain unauthorized access to your accounts. You can check if you&amp;rsquo;ve been part of any breach using &lt;a href="https://haveibeenpwned.com/">HaveIBeenPwned&lt;/a>. Don&amp;rsquo;t be alarmed if you find you&amp;rsquo;re part of a breach just ensure you take the necessary action to protect yourself now you know your password could be exposed.&lt;/p>
&lt;p>The best practice is to use one password per account built up of letters, numbers and symbols. The longer the password the better. Now this is absolutely impossible to remember for every single account you own and this is where a tool like a password manager proves to be very useful. You use a password manager to generate strong passwords for your multiple accounts and store them in an encrypted password database. Think of it as a vault for all your passwords. This vault is protected by a strong master password which you can remember. Make sure this password is strong and is only used for your password vault. &lt;a href="https://www.youtube.com/watch?v=yzGzB-yYKcc">This interview with Edward Snowden on Last Week Tonight with John Oliver&lt;/a> will show you how to think of a strong master password.&lt;/p>
&lt;p>Now the choice of password management software. There are plenty of choices to go with here. Some examples of such popular commercial offerings are &lt;a href="https://1password.com/">1Password&lt;/a> and &lt;a href="https://bitwarden.com/">Bitwarden&lt;/a> with their cloud based solutions meaning you can access your password from anywhere on any device so long as you have internet connectivity. These are certainly viable solutions and are worth considering if you&amp;rsquo;re looking for convenience.&lt;/p>
&lt;p>I personally use &lt;a href="https://keepassxc.org/">KeepassXC&lt;/a> on my laptop and &lt;a href="https://www.keepassdx.com/">KeepassDX&lt;/a> on my Android mobile phone. These are both compatible with accessing my Keepass password vault. I find Keepass a very good solutions due to its optional two step verification meaning you can protect your vault not only with a strong password but also with a key file. The key file is a separate file which you would need to provide to access your password vault. This would mean if your strong password ever got breached they should not be able to access your password vault without the key file and vice versa. Keepass is also an offline password manager so should you find yourself without any internet connectivity or a service is down you can still access your passwords. Also with a password manager being offline this means the likelihood of you losing your password vault as part of a breach online is incredibly low. KeepassXC is also open source meaning that you can verify yourself that there is no malicious code going into your password management solution by reading the software&amp;rsquo;s source code should you wish. Also should the project get abandoned or development halts someone can fork the software and continue to contribute to the application as part of a separate project, after all KeepassXC is a fork of KeepassX when development stalled in 2016. KeepassXC is also excellent value for money as there is no charge for usage. Its also worth noting that if you are an iOS user there are plenty of Keepass compatible password managers in the app store, however as I do not use this on any iOS device I can not comment on the usability of the applications.&lt;/p>
&lt;p>Whilst the idea of keeping your passwords offline is great from a security standpoint there are some inconveniences, such as accessing my most up to date password vault on the go. You can achieve similar results to the cloud based password managers with Keepass by storing your vault in a cloud storage solution such as Dropbox, Google Drive, Microsoft OneDrive etc. Now doing this does increase the risk of unauthorized access to your vault but the risk is reduced by using a strong password and a key file to your vault. &lt;strong>Under no circumstances should you store the keyfile in the cloud&lt;/strong>, only store your vault there. Keep the keyfile local on your laptop and devices and transfer it locally using a USB Cable or another solution that does not involve the internet. Its also strongly encouraged that you keep this backed up somewhere safe. Data corruption can happen and it can be hard work regaining access to all your accounts.&lt;/p>
&lt;p>Now to begin on changing all your passwords. I would start off with all the accounts that could impact your life should unauthorized access happen such as internet banking, email, social media etc. There is no question that this is a tedious task and whilst its strongly encouraged to change all of your passwords right away, it may also not be feasible. Get the critical ones done first and then just change the rest when you can. I also strongly advise whilst changing your password that you enable two factor authentication where you can for that extra bit of security. I hope to write a post on this subject in the near future.&lt;/p>
&lt;p>Now when the next breach occurs no matter how big or small you&amp;rsquo;re prepared. You will just need to change that one exposed password instead of every password you have. This one initial effort of changing all your passwords has now paid off saving you time, effort and stress.&lt;/p>
&lt;hr>
&lt;p>&lt;small>&lt;em>Update on 9th August 2025: A small update to this blog post was made to refresh the commercial offerings and correct some spelling mistakes. The majority of the content has not changed since the original blog post on 28th March 2021.&lt;/em>&lt;/small>&lt;/p></description></item></channel></rss>