<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: web-components</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/web-components.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2026-06-17T03:56:10+00:00</updated><author><name>Simon Willison</name></author><entry><title>&lt;click-to-play&gt; — a still that plays</title><link href="https://simonwillison.net/2026/Jun/17/click-to-play-component/#atom-tag" rel="alternate"/><published>2026-06-17T03:56:10+00:00</published><updated>2026-06-17T03:56:10+00:00</updated><id>https://simonwillison.net/2026/Jun/17/click-to-play-component/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Tool:&lt;/strong&gt; &lt;a href="https://tools.simonwillison.net/click-to-play-component"&gt;&amp;lt;click-to-play&amp;gt; — a still that plays&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;A progressive enchantment Web Component that turns this markup:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;click-to-play&amp;gt;
  &amp;lt;a href="URL to GIF"&amp;gt;
    &amp;lt;img src="URL to first frame" alt="..."&amp;gt;
  &amp;lt;/a&amp;gt;
&amp;lt;/click-to-play&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Into a still frame with a click to play button which loads the GIF on demand. For when you don't want big GIFs to be loaded unless people want to play them.&lt;/p&gt;
&lt;p&gt;Here's &lt;a href="https://simonwillison.net/2026/Jun/16/datasette/"&gt;an example&lt;/a&gt; that demonstrates the new row editing tools in Datasette - in fact I built this Web Component for that post.&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/gif"&gt;gif&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/progressive-enhancement"&gt;progressive-enhancement&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="gif"/><category term="javascript"/><category term="progressive-enhancement"/><category term="web-components"/></entry><entry><title>Gemini 3 Flash</title><link href="https://simonwillison.net/2025/Dec/17/gemini-3-flash/#atom-tag" rel="alternate"/><published>2025-12-17T22:44:52+00:00</published><updated>2025-12-17T22:44:52+00:00</updated><id>https://simonwillison.net/2025/Dec/17/gemini-3-flash/#atom-tag</id><summary type="html">
    &lt;p&gt;It continues to be a busy December, if not quite as busy &lt;a href="https://simonwillison.net/2024/Dec/20/december-in-llms-has-been-a-lot/"&gt;as last year&lt;/a&gt;. Today's big news is &lt;a href="https://blog.google/technology/developers/build-with-gemini-3-flash/"&gt;Gemini 3 Flash&lt;/a&gt;, the latest in Google's "Flash" line of faster and less expensive models.&lt;/p&gt;
&lt;p&gt;Google are emphasizing the comparison between the new Flash and their previous generation's top model Gemini 2.5 Pro:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Building on 3 Pro’s strong multimodal, coding and agentic features, 3 Flash offers powerful performance at less than a quarter the cost of 3 Pro, along with higher rate limits. The new 3 Flash model surpasses 2.5 Pro across many benchmarks while delivering faster speeds.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Gemini 3 Flash's characteristics are almost identical to Gemini 3 Pro: it accepts text, image, video, audio, and PDF, outputs only text, handles 1,048,576 maximum input tokens and up to 65,536 output tokens, and has the same knowledge cut-off date of January 2025 (also shared with the Gemini 2.5 series).&lt;/p&gt;
&lt;p&gt;The benchmarks look good. The cost is appealing: 1/4 the price of Gemini 3 Pro ≤200k and 1/8 the price of Gemini 3 Pro &amp;gt;200k, and it's nice not to have a price increase for the new Flash at larger token lengths.&lt;/p&gt;
&lt;p&gt;It's a little &lt;em&gt;more&lt;/em&gt; expensive than previous Flash models - Gemini 2.5 Flash was $0.30/million input tokens and $2.50/million on output, Gemini 3 Flash is $0.50/million and $3/million respectively.&lt;/p&gt;
&lt;p&gt;Google &lt;a href="https://blog.google/products/gemini/gemini-3-flash/"&gt;claim&lt;/a&gt; it may still end up cheaper though, due to more efficient output token usage:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&gt; Gemini 3 Flash is able to modulate how much it thinks. It may think longer for more complex use cases, but it also uses 30% fewer tokens on average than 2.5 Pro.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Here's &lt;a href="https://www.llm-prices.com/#it=100000&amp;amp;ot=10000&amp;amp;sel=gemini-3-flash-preview%2Cgemini-3-pro-preview%2Cgemini-3-pro-preview-200k%2Cgpt-5.2%2Cclaude-opus-4-5%2Cclaude-sonnet-4.5%2Cclaude-4.5-haiku%2Cgemini-2.5-flash%2Cgpt-5-mini"&gt;a more extensive price comparison&lt;/a&gt; on my &lt;a href="https://www.llm-prices.com/"&gt;llm-prices.com&lt;/a&gt; site.&lt;/p&gt;
&lt;h4 id="generating-some-svgs-of-pelicans"&gt;Generating some SVGs of pelicans&lt;/h4&gt;
&lt;p&gt;I released &lt;a href="https://github.com/simonw/llm-gemini/releases/tag/0.28"&gt;llm-gemini 0.28&lt;/a&gt; this morning with support for the new model. You can try it out like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;llm install -U llm-gemini
llm keys set gemini # paste in key
llm -m gemini-3-flash-preview "Generate an SVG of a pelican riding a bicycle"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;According to &lt;a href="https://ai.google.dev/gemini-api/docs/gemini-3#thinking_level"&gt;the developer docs&lt;/a&gt; the new model supports four different thinking level options: &lt;code&gt;minimal&lt;/code&gt;, &lt;code&gt;low&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt;, and &lt;code&gt;high&lt;/code&gt;. This is different from Gemini 3 Pro, which only supported &lt;code&gt;low&lt;/code&gt; and &lt;code&gt;high&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can run those like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;llm -m gemini-3-flash-preview --thinking-level minimal "Generate an SVG of a pelican riding a bicycle"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here are four pelicans, for thinking levels &lt;a href="https://gist.github.com/simonw/8047c805a4a1df7fd4e854b18e7482d9"&gt;minimal&lt;/a&gt;, &lt;a href="https://gist.github.com/simonw/fb61686a1f915e3777b4a40e2df41068"&gt;low&lt;/a&gt;, &lt;a href="https://gist.github.com/simonw/190c3ce82cd8976827139bbc4dcc2d19"&gt;medium&lt;/a&gt;, and &lt;a href="https://gist.github.com/simonw/da66ffce135359161996e41e50e32ec3"&gt;high&lt;/a&gt;:&lt;/p&gt;
&lt;image-gallery width="4"&gt;
    &lt;img src="https://static.simonwillison.net/static/2025/gemini-3-flash-preview-thinking-level-minimal-pelican-svg.jpg" alt="A minimalist vector illustration of a stylized white bird with a long orange beak and a red cap riding a dark blue bicycle on a single grey ground line against a plain white background." /&gt;
    &lt;img src="https://static.simonwillison.net/static/2025/gemini-3-flash-preview-thinking-level-low-pelican-svg.jpg" alt="Minimalist illustration: A stylized white bird with a large, wedge-shaped orange beak and a single black dot for an eye rides a red bicycle with black wheels and a yellow pedal against a solid light blue background." /&gt;
    &lt;img src="https://static.simonwillison.net/static/2025/gemini-3-flash-preview-thinking-level-medium-pelican-svg.jpg" alt="A minimalist illustration of a stylized white bird with a large yellow beak riding a red road bicycle in a racing position on a light blue background." /&gt;
    &lt;img src="https://static.simonwillison.net/static/2025/gemini-3-flash-preview-thinking-level-high-pelican-svg.jpg" alt="Minimalist line-art illustration of a stylized white bird with a large orange beak riding a simple black bicycle with one orange pedal, centered against a light blue circular background." /&gt;
&lt;/image-gallery&gt;
&lt;h4 id="i-built-the-gallery-component-with-gemini-3-flash"&gt;I built the gallery component with Gemini 3 Flash&lt;/h4&gt;
&lt;p&gt;The gallery above uses a new Web Component which I built using Gemini 3 Flash to try out its coding abilities. The code on the page looks like this:&lt;/p&gt;
&lt;div class="highlight highlight-text-html-basic"&gt;&lt;pre&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;image-gallery&lt;/span&gt; &lt;span class="pl-c1"&gt;width&lt;/span&gt;="&lt;span class="pl-s"&gt;4&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;img&lt;/span&gt; &lt;span class="pl-c1"&gt;src&lt;/span&gt;="&lt;span class="pl-s"&gt;https://static.simonwillison.net/static/2025/gemini-3-flash-preview-thinking-level-minimal-pelican-svg.jpg&lt;/span&gt;" &lt;span class="pl-c1"&gt;alt&lt;/span&gt;="&lt;span class="pl-s"&gt;A minimalist vector illustration of a stylized white bird with a long orange beak and a red cap riding a dark blue bicycle on a single grey ground line against a plain white background.&lt;/span&gt;" &lt;span class="pl-kos"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;img&lt;/span&gt; &lt;span class="pl-c1"&gt;src&lt;/span&gt;="&lt;span class="pl-s"&gt;https://static.simonwillison.net/static/2025/gemini-3-flash-preview-thinking-level-low-pelican-svg.jpg&lt;/span&gt;" &lt;span class="pl-c1"&gt;alt&lt;/span&gt;="&lt;span class="pl-s"&gt;Minimalist illustration: A stylized white bird with a large, wedge-shaped orange beak and a single black dot for an eye rides a red bicycle with black wheels and a yellow pedal against a solid light blue background.&lt;/span&gt;" &lt;span class="pl-kos"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;img&lt;/span&gt; &lt;span class="pl-c1"&gt;src&lt;/span&gt;="&lt;span class="pl-s"&gt;https://static.simonwillison.net/static/2025/gemini-3-flash-preview-thinking-level-medium-pelican-svg.jpg&lt;/span&gt;" &lt;span class="pl-c1"&gt;alt&lt;/span&gt;="&lt;span class="pl-s"&gt;A minimalist illustration of a stylized white bird with a large yellow beak riding a red road bicycle in a racing position on a light blue background.&lt;/span&gt;" &lt;span class="pl-kos"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;img&lt;/span&gt; &lt;span class="pl-c1"&gt;src&lt;/span&gt;="&lt;span class="pl-s"&gt;https://static.simonwillison.net/static/2025/gemini-3-flash-preview-thinking-level-high-pelican-svg.jpg&lt;/span&gt;" &lt;span class="pl-c1"&gt;alt&lt;/span&gt;="&lt;span class="pl-s"&gt;Minimalist line-art illustration of a stylized white bird with a large orange beak riding a simple black bicycle with one orange pedal, centered against a light blue circular background.&lt;/span&gt;" &lt;span class="pl-kos"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;image-gallery&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Those alt attributes are all generated by Gemini 3 Flash as well, using this recipe:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;llm -m gemini-3-flash-preview --system &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;You write alt text for any image pasted in by the user. Alt text is always presented in a&lt;/span&gt;
&lt;span class="pl-s"&gt;fenced code block to make it easy to copy and paste out. It is always presented on a single&lt;/span&gt;
&lt;span class="pl-s"&gt;line so it can be used easily in Markdown images. All text on the image (for screenshots etc)&lt;/span&gt;
&lt;span class="pl-s"&gt;must be exactly included. A short note describing the nature of the image itself should go first.&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt; \
-a https://static.simonwillison.net/static/2025/gemini-3-flash-preview-thinking-level-high-pelican-svg.jpg&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can see the code that powers the image gallery Web Component &lt;a href="https://github.com/simonw/simonwillisonblog/blob/31651b3a527011d1c971d4256c1c9f61ef378d23/static/image-gallery.js"&gt;here on GitHub&lt;/a&gt;. I built it by prompting Gemini 3 Flash via &lt;a href="https://llm.datasette.io/"&gt;LLM&lt;/a&gt; like this:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;llm -m gemini-3-flash-preview &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;Build a Web Component that implements a simple image gallery. Usage is like this:&lt;/span&gt;
&lt;span class="pl-s"&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;&amp;lt;image-gallery width="5"&amp;gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;  &amp;lt;img src="image1.jpg" alt="Image 1"&amp;gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;  &amp;lt;img src="image2.jpg" alt="Image 2" data-thumb="image2-thumb.jpg"&amp;gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;  &amp;lt;img src="image3.jpg" alt="Image 3"&amp;gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;&amp;lt;/image-gallery&amp;gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;If an image has a data-thumb= attribute that one is used instead, other images are scaled down. &lt;/span&gt;
&lt;span class="pl-s"&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;The image gallery always takes up 100% of available width. The width="5" attribute means that five images will be shown next to each other in each row. The default is 3. There are gaps between the images. When an image is clicked it opens a modal dialog with the full size image.&lt;/span&gt;
&lt;span class="pl-s"&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;Return a complete HTML file with both the implementation of the Web Component several example uses of it. Use https://picsum.photos/300/200 URLs for those example images.&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It took a few follow-up prompts using &lt;code&gt;llm -c&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;llm -c &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;Use a real modal such that keyboard shortcuts and accessibility features work without extra JS&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;

llm -c &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;Use X for the close icon and make it a bit more subtle&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;

llm -c &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;remove the hover effect entirely&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;

llm -c &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;I want no border on the close icon even when it is focused&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here's &lt;a href="https://gist.github.com/simonw/09f63a49f29620d4cbbfd383cfee1db3"&gt;the full transcript&lt;/a&gt;, exported using &lt;code&gt;llm logs -cue&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Those five prompts took:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;225 input, 3,269 output&lt;/li&gt;
&lt;li&gt;2,243 input, 2,908 output&lt;/li&gt;
&lt;li&gt;4,319 input, 2,516 output&lt;/li&gt;
&lt;li&gt;6,376 input, 2,094 output&lt;/li&gt;
&lt;li&gt;8,151 input, 1,806 output&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Added together that's 21,314 input and 12,593 output for a grand total &lt;a href="https://www.llm-prices.com/#it=21314&amp;amp;ot=12593&amp;amp;sel=gemini-3-flash-preview"&gt;of 4.8436 cents&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The guide to &lt;a href="https://ai.google.dev/gemini-api/docs/gemini-3#migrating_from_gemini_25"&gt;migrating from Gemini 2.5&lt;/a&gt; reveals one disappointment:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Image segmentation:&lt;/strong&gt; Image segmentation capabilities (returning pixel-level masks for objects) are not supported in Gemini 3 Pro or Gemini 3 Flash. For workloads requiring native image segmentation, we recommend continuing to utilize Gemini 2.5 Flash with thinking turned off or &lt;a href="https://ai.google.dev/gemini-api/docs/robotics-overview"&gt;Gemini Robotics-ER 1.5&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I &lt;a href="https://simonwillison.net/2025/Apr/18/gemini-image-segmentation/"&gt;wrote about this capability in Gemini 2.5&lt;/a&gt; back in April. I hope they come back in future models - they're a really neat capability that is unique to Gemini.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/google"&gt;google&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llm"&gt;llm&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/gemini"&gt;gemini&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llm-pricing"&gt;llm-pricing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pelican-riding-a-bicycle"&gt;pelican-riding-a-bicycle&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llm-release"&gt;llm-release&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="google"/><category term="ai"/><category term="web-components"/><category term="generative-ai"/><category term="llms"/><category term="llm"/><category term="gemini"/><category term="llm-pricing"/><category term="pelican-riding-a-bicycle"/><category term="llm-release"/></entry><entry><title>&lt;model-viewer&gt; Web Component by Google</title><link href="https://simonwillison.net/2024/Dec/13/model-viewer/#atom-tag" rel="alternate"/><published>2024-12-13T18:46:13+00:00</published><updated>2024-12-13T18:46:13+00:00</updated><id>https://simonwillison.net/2024/Dec/13/model-viewer/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://modelviewer.dev/"&gt;&amp;lt;model-viewer&amp;gt; Web Component by Google&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I learned about this Web Component from Claude when looking for options to render a &lt;a href="https://en.wikipedia.org/wiki/GlTF"&gt;.glb file&lt;/a&gt; on a web page. It's very pleasant to use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;model-viewer style="width: 100%; height: 200px"
  src="https://static.simonwillison.net/static/cors-allow/2024/a-pelican-riding-a-bicycle.glb"
  camera-controls="1" auto-rotate="1"
&amp;gt;&amp;lt;/model-viewer&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here it is showing a 3D pelican on a bicycle I created while trying out &lt;a href="https://www.blendergpt.org/"&gt;BlenderGPT&lt;/a&gt;, a new prompt-driven 3D asset creating tool (my prompt was "a pelican riding a bicycle"). There's &lt;a href="https://news.ycombinator.com/item?id=42398913#42400537"&gt;a comment&lt;/a&gt; from BlenderGPT's creator on Hacker News explaining that it's currently using Microsoft's &lt;a href="https://github.com/microsoft/TRELLIS"&gt;TRELLIS model&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;model-viewer style="width: 100%; height: 200px"
  src="https://static.simonwillison.net/static/cors-allow/2024/a-pelican-riding-a-bicycle.glb"
  camera-controls="1" auto-rotate="1"&gt;&lt;/model-viewer&gt;&lt;/p&gt;
&lt;script type="module" src="https://cdnjs.cloudflare.com/ajax/libs/model-viewer/3.3.0/model-viewer.min.js"&gt;&lt;/script&gt;

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://gist.github.com/simonw/64a33cd6af819674defddb92f5f2e713"&gt;Claude: options for displaying a glb file on a web page&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/3d"&gt;3d&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/google"&gt;google&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/microsoft"&gt;microsoft&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude"&gt;claude&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/blender"&gt;blender&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pelican-riding-a-bicycle"&gt;pelican-riding-a-bicycle&lt;/a&gt;&lt;/p&gt;



</summary><category term="3d"/><category term="google"/><category term="microsoft"/><category term="ai"/><category term="web-components"/><category term="generative-ai"/><category term="claude"/><category term="blender"/><category term="pelican-riding-a-bicycle"/></entry><entry><title>HTML for People</title><link href="https://simonwillison.net/2024/Oct/11/html-for-people/#atom-tag" rel="alternate"/><published>2024-10-11T01:51:43+00:00</published><updated>2024-10-11T01:51:43+00:00</updated><id>https://simonwillison.net/2024/Oct/11/html-for-people/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://htmlforpeople.com/"&gt;HTML for People&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Blake Watson's brand new HTML tutorial, presented as a free online book (CC BY-NC-SA 4.0, &lt;a href="https://github.com/blakewatson/htmlforpeople"&gt;on GitHub&lt;/a&gt;). This seems very modern and well thought-out to me. It focuses exclusively on HTML, skipping JavaScript entirely and teaching with &lt;a href="https://simplecss.org/"&gt;Simple.css&lt;/a&gt; to avoid needing to dig into CSS while still producing sites that are pleasing to look at. It even touches on Web Components (described as &lt;a href="https://htmlforpeople.com/adding-a-fun-page/#custom-html-tags"&gt;Custom HTML tags&lt;/a&gt;) towards the end.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=41801334"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/css"&gt;css&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/html"&gt;html&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;&lt;/p&gt;



</summary><category term="css"/><category term="html"/><category term="web-components"/></entry><entry><title>Quoting Jim Simon</title><link href="https://simonwillison.net/2024/Oct/1/jim-simon/#atom-tag" rel="alternate"/><published>2024-10-01T00:09:29+00:00</published><updated>2024-10-01T00:09:29+00:00</updated><id>https://simonwillison.net/2024/Oct/1/jim-simon/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://twitter.com/jimsimon_/status/1840905389384007906"&gt;&lt;p&gt;[Reddit is] mostly ported over entirely to Lit now. There are a few straggling pages that we're still working on, but most of what everyday typical users see and use is now entirely Lit based. This includes both logged out and logged in experiences.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://twitter.com/jimsimon_/status/1840905389384007906"&gt;Jim Simon&lt;/a&gt;, Reddit&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/reddit"&gt;reddit&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/lit-html"&gt;lit-html&lt;/a&gt;&lt;/p&gt;



</summary><category term="javascript"/><category term="reddit"/><category term="web-components"/><category term="lit-html"/></entry><entry><title>My approach to HTML web components</title><link href="https://simonwillison.net/2024/Apr/30/approach-to-html-web-components/#atom-tag" rel="alternate"/><published>2024-04-30T11:02:48+00:00</published><updated>2024-04-30T11:02:48+00:00</updated><id>https://simonwillison.net/2024/Apr/30/approach-to-html-web-components/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://adactio.com/journal/21078"&gt;My approach to HTML web components&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Some neat patterns here from Jeremy Keith, who is using Web Components extensively for progressive enhancement of existing markup.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The reactivity you get with full-on frameworks [like React and Vue] isn’t something that web components offer. But I do think web components can replace jQuery and other approaches to scripting the DOM.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Jeremy likes naming components with their element as a prefix (since all element names must contain at least one hyphen), and suggests building components under the single responsibility principle - so you can do things like &lt;code&gt;&amp;lt;button-confirm&amp;gt;&amp;lt;button-clipboard&amp;gt;&amp;lt;button&amp;gt;...&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;He configures these buttons with &lt;code&gt;data-&lt;/code&gt; attributes and has them communicate with each other using custom events.&lt;/p&gt;
&lt;p&gt;Something I hadn't realized is that since the &lt;code&gt;connectedCallback&lt;/code&gt; function on a custom element is fired any time that element is attached to a page you can &lt;code&gt;fetch()&lt;/code&gt; and then &lt;code&gt;insertHTML&lt;/code&gt; content that includes elements and know that they will initialize themselves without needing any extra logic - great for the kind of pattern encourages by systems such as &lt;a href="https://htmx.org/"&gt;HTMX&lt;/a&gt;.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jeremy-keith"&gt;jeremy-keith&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/progressive-enhancement"&gt;progressive-enhancement&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;&lt;/p&gt;



</summary><category term="javascript"/><category term="jeremy-keith"/><category term="progressive-enhancement"/><category term="web-components"/></entry><entry><title>Introducing Enhance WASM</title><link href="https://simonwillison.net/2024/Apr/8/enhance-wasm/#atom-tag" rel="alternate"/><published>2024-04-08T19:44:20+00:00</published><updated>2024-04-08T19:44:20+00:00</updated><id>https://simonwillison.net/2024/Apr/8/enhance-wasm/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://begin.com/blog/posts/2024-04-08-introducing-enhance-wasm"&gt;Introducing Enhance WASM&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
“Backend agnostic server-side rendering (SSR) for Web Components”—fascinating new project from Brian LeRoux and Begin.&lt;/p&gt;

&lt;p&gt;The idea here is to provide server-side rendering of Web Components using WebAssembly that can run on any platform that is supported within the Extism WASM ecosystem.&lt;/p&gt;

&lt;p&gt;The key is the enhance-ssr.wasm bundle, a 4.1MB WebAssembly version of the enhance-ssr JavaScript library, compiled using the Extism JavaScript PDK (Plugin Development Kit) which itself bundles a WebAssembly version of QuickJS.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://indieweb.social/@enhance_dev@fosstodon.org/112237109269781175"&gt;@enhance_dev@fosstodon.org&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webassembly"&gt;webassembly&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quickjs"&gt;quickjs&lt;/a&gt;&lt;/p&gt;



</summary><category term="javascript"/><category term="web-components"/><category term="webassembly"/><category term="quickjs"/></entry><entry><title>Cally: Accessibility statement</title><link href="https://simonwillison.net/2024/Apr/2/cally-accessibility-statement/#atom-tag" rel="alternate"/><published>2024-04-02T19:38:19+00:00</published><updated>2024-04-02T19:38:19+00:00</updated><id>https://simonwillison.net/2024/Apr/2/cally-accessibility-statement/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://wicky.nillia.ms/cally/accessibility/"&gt;Cally: Accessibility statement&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Cally is a neat new open source date (and date range) picker Web Component by Nick Williams.&lt;/p&gt;

&lt;p&gt;It’s framework agnostic and weighs less than 9KB grilled, but the best feature is this detailed page of documentation covering its accessibility story, including how it was tested—in JAWS, NVDA and VoiceOver.&lt;/p&gt;

&lt;p&gt;I’d love to see other open source JavaScript libraries follow this example.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/wickynilliams/status/1775208741392052299"&gt;@wickynilliams&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/accessibility"&gt;accessibility&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/open-source"&gt;open-source&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="javascript"/><category term="open-source"/><category term="web-components"/></entry><entry><title>Coroutines and web components</title><link href="https://simonwillison.net/2024/Mar/9/coroutines-and-web-components/#atom-tag" rel="alternate"/><published>2024-03-09T03:38:53+00:00</published><updated>2024-03-09T03:38:53+00:00</updated><id>https://simonwillison.net/2024/Mar/9/coroutines-and-web-components/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://lorenzofox.dev/posts/component-as-infinite-loop/"&gt;Coroutines and web components&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I like using generators in Python but I rarely knowingly use them in JavaScript—I’m probably most exposed to them by Observable, which uses then extensively under the hood as a mostly hidden implementation detail.&lt;/p&gt;

&lt;p&gt;Laurent Renard here shows some absolutely ingenious tricks with them as a way of building stateful Web Components.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=39646749"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/observable"&gt;observable&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;&lt;/p&gt;



</summary><category term="javascript"/><category term="observable"/><category term="web-components"/></entry><entry><title>Streaming HTML out of order without JavaScript</title><link href="https://simonwillison.net/2024/Mar/1/streaming-html-out-of-order-without-javascript/#atom-tag" rel="alternate"/><published>2024-03-01T16:59:54+00:00</published><updated>2024-03-01T16:59:54+00:00</updated><id>https://simonwillison.net/2024/Mar/1/streaming-html-out-of-order-without-javascript/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://lamplightdev.com/blog/2024/01/10/streaming-html-out-of-order-without-javascript/"&gt;Streaming HTML out of order without JavaScript&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
A really interesting new browser capability. If you serve the following HTML:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;template shadowrootmode="open"&amp;gt;
  &amp;lt;slot name="item-1"&amp;gt;Loading...&amp;lt;/slot&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then later in the same page stream an element specifying that slot:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;span slot="item-1"&amp;gt;Item number 1&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The previous slot will be replaced while the page continues to load.&lt;/p&gt;
&lt;p&gt;I tried the demo in the most recent Chrome, Safari and Firefox (and Mobile Safari) and it worked in all of them.&lt;/p&gt;
&lt;p&gt;The key feature is &lt;code&gt;shadowrootmode=open&lt;/code&gt;, which looks like it was added to Firefox 123 on February 19th 2024 - the other two browsers are listed on caniuse.com as gaining it around March last year.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=39560180"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/browsers"&gt;browsers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/html"&gt;html&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;&lt;/p&gt;



</summary><category term="browsers"/><category term="html"/><category term="web-components"/></entry><entry><title>Portable EPUBs</title><link href="https://simonwillison.net/2024/Jan/25/portable-epubs/#atom-tag" rel="alternate"/><published>2024-01-25T20:32:38+00:00</published><updated>2024-01-25T20:32:38+00:00</updated><id>https://simonwillison.net/2024/Jan/25/portable-epubs/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://willcrichton.net/notes/portable-epubs/"&gt;Portable EPUBs&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Will Crichton digs into the reasons people still prefer PDF over HTML as a format for sharing digital documents, concluding that the key issues are that HTML documents are not fully self-contained and may not be rendered consistently.&lt;/p&gt;

&lt;p&gt;He proposes “Portable EPUBs” as the solution, defining a subset of the existing EPUB standard with some additional restrictions around avoiding loading extra assets over a network, sticking to a smaller (as-yet undefined) subset of HTML and encouraging interactive components to be built using self-contained Web Components.&lt;/p&gt;

&lt;p&gt;Will also built his own lightweight EPUB reading system, called Bene—which is used to render this Portable EPUBs article. It provides a “download” link in the top right which produces the .epub file itself.&lt;/p&gt;

&lt;p&gt;There’s a lot to like here. I’m constantly infuriated at the number of documents out there that are PDFs but really should be web pages (academic papers are a particularly bad example here), so I’m very excited by any initiatives that might help push things in the other direction.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/html"&gt;html&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pdf"&gt;pdf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;&lt;/p&gt;



</summary><category term="html"/><category term="pdf"/><category term="web-components"/></entry><entry><title>HTML Web Components: An Example</title><link href="https://simonwillison.net/2023/Nov/17/html-web-components-an-example/#atom-tag" rel="alternate"/><published>2023-11-17T16:33:24+00:00</published><updated>2023-11-17T16:33:24+00:00</updated><id>https://simonwillison.net/2023/Nov/17/html-web-components-an-example/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://blog.jim-nielsen.com/2023/html-web-components-an-example/"&gt;HTML Web Components: An Example&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Jim Nielsen provides a clear example illustrating the idea of the recently coined "HTML Web Components" pattern. It's Web Components as progressive enhancement: in this example a &lt;code&gt;&amp;lt;user-avatar&amp;gt;&lt;/code&gt; custom element wraps a regular image, then JavaScript defines a Web Component that enhances that image. If the JavaScript fails to load the image still displays.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=38298694"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/progressive-enhancement"&gt;progressive-enhancement&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;&lt;/p&gt;



</summary><category term="javascript"/><category term="progressive-enhancement"/><category term="web-components"/></entry><entry><title>Web Components Will Outlive Your JavaScript Framework</title><link href="https://simonwillison.net/2023/Oct/25/web-components-will-outlive-your-javascript-framework/#atom-tag" rel="alternate"/><published>2023-10-25T17:19:40+00:00</published><updated>2023-10-25T17:19:40+00:00</updated><id>https://simonwillison.net/2023/Oct/25/web-components-will-outlive-your-javascript-framework/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://jakelazaroff.com/words/web-components-will-outlive-your-javascript-framework/"&gt;Web Components Will Outlive Your JavaScript Framework&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
A really clear explanation of the benefit of Web Components built using dependency-free vanilla JavaScript, specifically for interactive components that you might want to embed in something like a blog post. Includes a very neat minimal example component.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=38012662"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;&lt;/p&gt;



</summary><category term="javascript"/><category term="web-components"/></entry><entry><title>Quoting Alex Russell</title><link href="https://simonwillison.net/2023/Aug/15/alex-russell/#atom-tag" rel="alternate"/><published>2023-08-15T21:15:16+00:00</published><updated>2023-08-15T21:15:16+00:00</updated><id>https://simonwillison.net/2023/Aug/15/alex-russell/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://toot.cafe/@slightlyoff/110512103005532169"&gt;&lt;p&gt;Someone asked me today if there was a case for using React in a new app that doesn't need to support IE.&lt;/p&gt;
&lt;p&gt;I could not come up with a single reason to prefer it over Preact or (better yet) any of the modern reactive Web Components systems (FAST, Lit, Stencil, etc.).&lt;/p&gt;
&lt;p&gt;One of the constraints is that the team wanted to use an existing library of Web Components, but React made it hard. This is probably going to cause them to favour Preact for the bits of the team that want React-flavoured modern webdev.&lt;/p&gt;
&lt;p&gt;It's astonishing how antiquated React is.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://toot.cafe/@slightlyoff/110512103005532169"&gt;Alex Russell&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/alex-russell"&gt;alex-russell&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/react"&gt;react&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;&lt;/p&gt;



</summary><category term="alex-russell"/><category term="javascript"/><category term="react"/><category term="web-components"/></entry><entry><title>Shoelace</title><link href="https://simonwillison.net/2022/Aug/20/shoelace/#atom-tag" rel="alternate"/><published>2022-08-20T20:57:16+00:00</published><updated>2022-08-20T20:57:16+00:00</updated><id>https://simonwillison.net/2022/Aug/20/shoelace/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://shoelace.style/"&gt;Shoelace&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Saw this for the first time today: it’s a relatively new library of framework-agnostic Web Components, built on lit-html and covering a huge array of common functionality: buttons and sliders and dialogs and drawer interfaces and dropdown menus and so on. The design is very clean, the documentation is superb—and it looks like you can cherry pick just the components you are using for a pretty lean addition to your page weight. So refreshing to see libraries like this that really take advantage of modern web standards.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/AdamRackis/status/1561075648143278080"&gt;Adam Rackis&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/css"&gt;css&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-standards"&gt;web-standards&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/lit-html"&gt;lit-html&lt;/a&gt;&lt;/p&gt;



</summary><category term="css"/><category term="javascript"/><category term="web-standards"/><category term="web-components"/><category term="lit-html"/></entry><entry><title>PyScript demos</title><link href="https://simonwillison.net/2022/Apr/30/pyscript-demos/#atom-tag" rel="alternate"/><published>2022-04-30T21:50:53+00:00</published><updated>2022-04-30T21:50:53+00:00</updated><id>https://simonwillison.net/2022/Apr/30/pyscript-demos/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://pyscript.net/examples/"&gt;PyScript demos&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
PyScript was announced at PyCon this morning. It’s a new open source project that provides Web Components built on top of Pyodide, allowing you to use Python directly within your HTML pages in a way that is executed using a WebAssembly copy of Python running in your browser. These demos really help illustrate what it can do—it’s a fascinating new piece of the Python web ecosystem.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/simonw/status/1520452635886641153"&gt;@simonw&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webassembly"&gt;webassembly&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pyodide"&gt;pyodide&lt;/a&gt;&lt;/p&gt;



</summary><category term="python"/><category term="web-components"/><category term="webassembly"/><category term="pyodide"/></entry><entry><title>Web Components as Progressive Enhancement</title><link href="https://simonwillison.net/2022/Apr/21/web-components-as-progressive-enhancement/#atom-tag" rel="alternate"/><published>2022-04-21T21:33:15+00:00</published><updated>2022-04-21T21:33:15+00:00</updated><id>https://simonwillison.net/2022/Apr/21/web-components-as-progressive-enhancement/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://cloudfour.com/thinks/web-components-as-progressive-enhancement/"&gt;Web Components as Progressive Enhancement&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I think this is a key aspect of Web Components I had been missing: since they default to rendering their contents, you can use them as a wrapper around regular HTML elements that can then be progressively enhanced once the JavaScript has loaded.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/slightlylate/status/1517221802941976576"&gt;Alex Russell&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;&lt;/p&gt;



</summary><category term="web-components"/></entry><entry><title>lite-youtube-embed</title><link href="https://simonwillison.net/2022/Mar/8/lite-youtube-embed/#atom-tag" rel="alternate"/><published>2022-03-08T21:13:39+00:00</published><updated>2022-03-08T21:13:39+00:00</updated><id>https://simonwillison.net/2022/Mar/8/lite-youtube-embed/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/paulirish/lite-youtube-embed"&gt;lite-youtube-embed&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Handy Web Component wrapper around the standard YouTube iframe embed which knocks over 500KB of JavaScript off the initial page load—I just added this to the datasette.io homepage and increased the Lighthouse performance score from 51 to 93!

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://github.com/simonw/datasette.io/issues/93"&gt;datasette.io/issues/93&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/iframes"&gt;iframes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/paul-irish"&gt;paul-irish&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/youtube"&gt;youtube&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-performance"&gt;web-performance&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;&lt;/p&gt;



</summary><category term="iframes"/><category term="paul-irish"/><category term="youtube"/><category term="web-performance"/><category term="web-components"/></entry><entry><title>SQLime:  SQLite Playground</title><link href="https://simonwillison.net/2022/Jan/17/sqlime/#atom-tag" rel="alternate"/><published>2022-01-17T19:08:12+00:00</published><updated>2022-01-17T19:08:12+00:00</updated><id>https://simonwillison.net/2022/Jan/17/sqlime/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://sqlime.org/"&gt;SQLime:  SQLite Playground&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Anton Zhiyanov built this useful mobile-friendly online playground for trying things out it SQLite. It uses the sql.js library which compiles SQLite to WebAssembly, so it runs everything in the browser—but it also supports saving your work to Gists via the GitHub API. The JavaScript source code is fun to read: the site doesn’t use npm or Webpack or similar, opting instead to implement everything library-free using modern JavaScript modules and Web Components.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/simonw/status/1482845419637903366"&gt;Anton Zhiyanov&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite"&gt;sqlite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webassembly"&gt;webassembly&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/anton-zhiyanov"&gt;anton-zhiyanov&lt;/a&gt;&lt;/p&gt;



</summary><category term="javascript"/><category term="sqlite"/><category term="web-components"/><category term="webassembly"/><category term="anton-zhiyanov"/></entry><entry><title>Weeknotes: Shaving some beautiful yaks</title><link href="https://simonwillison.net/2021/Dec/1/beautiful-yaks/#atom-tag" rel="alternate"/><published>2021-12-01T03:43:18+00:00</published><updated>2021-12-01T03:43:18+00:00</updated><id>https://simonwillison.net/2021/Dec/1/beautiful-yaks/#atom-tag</id><summary type="html">
    &lt;p&gt;I've been mostly &lt;a href="https://en.wiktionary.org/wiki/yak_shaving"&gt;shaving yaks&lt;/a&gt; this week - two in particular: the Datasette table refactor and the next release of &lt;a href="https://datasette.io/tools/git-history"&gt;git-history&lt;/a&gt;. I also built and released my first Web Component!&lt;/p&gt;
&lt;h4&gt;A Web Component for embedding Datasette tables&lt;/h4&gt;
&lt;p&gt;A longer term goal that I have for Datasette is to figure out a good way of using it to build dashboards, tying together summaries and visualizations of the latest data from a bunch of different sources.&lt;/p&gt;
&lt;p&gt;I'm excited about the potential of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components"&gt;Web Components&lt;/a&gt; to help solve this problem.&lt;/p&gt;
&lt;p&gt;My &lt;a href="https://github.com/simonw/datasette-notebook"&gt;datasette-notebook&lt;/a&gt; project is a &lt;em&gt;very&lt;/em&gt; early experiment in this direction: it's a Datasette notebook that provides a Markdown wiki (persisted to SQLite) to which I plan to add the ability to embed tables and visualizations in wiki pages - forming a hybrid of a wiki, dashboarding system and Notion/Airtable-style database.&lt;/p&gt;
&lt;p&gt;It does almost none of those things right now, which is why I've not really talked about it here.&lt;/p&gt;
&lt;p&gt;Web Components offer a standards-based mechanism for creating custom HTML tags. Imagine being able to embed a Datasette table on a page by adding the following to your HTML:&lt;/p&gt;
&lt;div class="highlight highlight-text-html-basic"&gt;&lt;pre&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;datasette-table&lt;/span&gt;
    &lt;span class="pl-c1"&gt;url&lt;/span&gt;="&lt;span class="pl-s"&gt;https://global-power-plants.datasettes.com/global-power-plants/global-power-plants.json&lt;/span&gt;"
&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;datasette-table&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That's exactly what &lt;a href="https://github.com/simonw/datasette-table"&gt;datasette-table&lt;/a&gt; lets you do! Here's &lt;a href="https://simonw.github.io/datasette-table/"&gt;a demo&lt;/a&gt; of it in action.&lt;/p&gt;
&lt;p&gt;This is version 0.1.0 - it works, but I've not even started to flesh it out.&lt;/p&gt;
&lt;p&gt;I did learn a bunch of things building it though: it's my first Web Component, my first time using &lt;a href="https://lit.dev/"&gt;Lit&lt;/a&gt;, my first time using &lt;a href="https://vitejs.dev/"&gt;Vite&lt;/a&gt; and the first JavaScript library I've ever packaged and &lt;a href="https://www.npmjs.com/package/datasette-table"&gt;published to npm&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here's a detailed TIL on &lt;a href="https://til.simonwillison.net/npm/publish-web-component"&gt;Publishing a Web Component to npm&lt;/a&gt; encapsulating everything I've learned from this project so far.&lt;/p&gt;
&lt;p&gt;This is also my first piece of yak shaving this week: I built this partly to make progress on &lt;code&gt;datasette-notebook&lt;/code&gt;, but also because my big Datasette refactor involves finalizing the design of the JSON API for version 1.0. I realized that I don't actually have a project that makes full use of that API, which has been hindering my attempts to redesign it. Having one or more Web Components that consume the API will be a fantastic way for me to eat my own dog food.&lt;/p&gt;
&lt;h4&gt;Link: rel="alternate" for Datasette tables&lt;/h4&gt;
&lt;p&gt;Here's an interesting problem that came up while I was working on the &lt;code&gt;datasette-table&lt;/code&gt; component.&lt;/p&gt;
&lt;p&gt;As designed right now, you need to figure out the JSON URL for a table and pass that to the component.&lt;/p&gt;
&lt;p&gt;This is &lt;em&gt;usually&lt;/em&gt; a case of adding &lt;code&gt;.json&lt;/code&gt; to the path, while preserving any query string parameters - but there's a nasty edge-case: if your SQLite table itself ends with the string &lt;code&gt;.json&lt;/code&gt; (which could happen! Especially since Datasette promises to work with any existing SQLite database) the URL becomes this instead:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/mydb/table.json?_format=json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Telling users of my component that they need to first construct the JSON URL for their page isn't the best experience: I'd much rather let people paste in the URL to the HTML version and derive the JSON from that.&lt;/p&gt;
&lt;p&gt;This is made more complex by the fact that, thanks to &lt;code&gt;--cors&lt;/code&gt;, the Web Component can be embedded on any page. And for &lt;code&gt;datasette-notebook&lt;/code&gt; I'd like to provide a feature where any URLs to Datasette instances - no matter where they are hosted - are turned into embedded tables automatically.&lt;/p&gt;
&lt;p&gt;To do this, I need an efficient way to tell that an arbitrary URL corresponds to a Datasette table.&lt;/p&gt;
&lt;p&gt;My latest idea here is to use a combination of HTTP &lt;code&gt;HEAD&lt;/code&gt; requests and a &lt;code&gt;Link: rel="alternate"&lt;/code&gt; header - something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;~ % curl -I 'https://latest.datasette.io/fixtures/compound_three_primary_keys'
HTTP/1.1 200 OK
date: Sat, 27 Nov 2021 20:09:36 GMT
server: uvicorn
Link: https://latest.datasette.io/fixtures/compound_three_primary_keys.json; rel="alternate"; type="application/datasette+json"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This would allow a (hopefully fast) &lt;code&gt;fetch()&lt;/code&gt; call from JavaScript to confirm that a URL is a Datasette table, and get back the JSON that should be fetched by the component in order to render it on the page.&lt;/p&gt;
&lt;p&gt;I have a prototype of this in &lt;a href="https://github.com/simonw/datasette/issues/1533"&gt;Datasette issue #1533&lt;/a&gt;. I think it's a promising approach!&lt;/p&gt;
&lt;p&gt;It's also now part of the ever-growing table refactor. Adding custom headers to page responses is currently far harder than it should be.&lt;/p&gt;
&lt;h4&gt;sqlite-utils STRICT tables&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://www.sqlite.org/releaselog/3_37_0.html"&gt;SQLite 3.37.0&lt;/a&gt; came out at the weekend with a long-awaited feature: &lt;a href="https://www.sqlite.org/stricttables.html"&gt;STRICT tables&lt;/a&gt;, which enforce column types such that you get an error if you try to insert a string into an integer column.&lt;/p&gt;
&lt;p&gt;(This has been a long-standing complaint about SQLite by people who love strong typing, and D. Richard Hipp finally shipped the change for them with some salty release notes saying it's "for developers who prefer that kind of thing.")&lt;/p&gt;
&lt;p&gt;I started researching how to add support for this to my &lt;a href="https://sqlite-utils.datasette.io/en/stable/python-api.html"&gt;sqlite-utils Python library&lt;/a&gt;. You can follow my thinking in &lt;a href="https://github.com/simonw/sqlite-utils/issues/344"&gt;sqlite-utils issue #344&lt;/a&gt; - I'm planning to add a &lt;code&gt;strict=True&lt;/code&gt; option to methods that create tables, but for the moment I've shipped &lt;a href="https://github.com/simonw/sqlite-utils/commit/e3f108e0f339e3d87ce48541bbca8f891bfaf040"&gt;new introspection properties&lt;/a&gt; for seeing if a table uses strict mode or not.&lt;/p&gt;
&lt;h4&gt;git-history update&lt;/h4&gt;
&lt;p&gt;My other big yak this week has been work on &lt;a href="https://github.com/simonw/git-history"&gt;git-history&lt;/a&gt;. I'm determined to get it into a stable state such that I can write it up, produce a tutorial and maybe produce a video demonstration as well - but I keep on finding things I want to change about how it works.&lt;/p&gt;
&lt;p&gt;The big challenge is how to most effectively represent the history of a bunch of different items over time in a relational database schema.&lt;/p&gt;
&lt;p&gt;I started with a &lt;code&gt;item&lt;/code&gt; table that presents just the most recent version of each item, and an &lt;code&gt;item_version&lt;/code&gt; table with a row for every subsequent version.&lt;/p&gt;
&lt;p&gt;That table got pretty big, with vast amounts of duplicated data in it.&lt;/p&gt;
&lt;p&gt;So I've been working on an optimization where columns are only included in an &lt;code&gt;item_version&lt;/code&gt; row &lt;a href="https://github.com/simonw/git-history/issues/21"&gt;if they have changed since the previous version&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The problem there is what to do about &lt;code&gt;null&lt;/code&gt; - does &lt;code&gt;null&lt;/code&gt; mean "this column didn't change" or does it mean "this column was set from some other value back to &lt;code&gt;null&lt;/code&gt;"?&lt;/p&gt;
&lt;p&gt;After a few different attempts I've decided to solve this with a many-to-many table, so for any row in the &lt;code&gt;item_version&lt;/code&gt; table you can see which columns were explicitly changed by that version.&lt;/p&gt;
&lt;p&gt;This is all working pretty nicely now, but still needs documentation, and tests, and then a solid write-up and tutorial and demos and a video... hopefully tomorrow!&lt;/p&gt;
&lt;p&gt;One of my design decisions for this tool has been to use an underscore prefix for "reserved columns", such that non-reserved columns can be safely used by the arbitrary data that is being tracked by the tool.&lt;/p&gt;
&lt;p&gt;Having columns with names like &lt;code&gt;_id&lt;/code&gt; and &lt;code&gt;_item&lt;/code&gt; has highlighted several bugs with Datasette's handling of these column names, since Datasette itself tries to use things like &lt;code&gt;?_search=&lt;/code&gt; for special query string parameters. I released &lt;a href="https://docs.datasette.io/en/stable/changelog.html#v0-59-4"&gt;Datasette 0.59.4&lt;/a&gt; with some relevant fixes.&lt;/p&gt;
&lt;h4&gt;A beautiful yak&lt;/h4&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2021/yak.jpg" alt="A very beautiful yak" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;p&gt;As a consumate yak shaver this beautiful yak that &lt;a href="https://www.reddit.com/r/interestingasfuck/comments/qtpm0x/this_white_yak_in_tibet/"&gt;showed up on Reddit&lt;/a&gt; a few weeks ago has me absolutely delighted.  I've not been able to determine the photography credit.&lt;/p&gt;
&lt;h4&gt;Releases this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/s3-credentials"&gt;s3-credentials&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/s3-credentials/releases/tag/0.7"&gt;0.7&lt;/a&gt; - (&lt;a href="https://github.com/simonw/s3-credentials/releases"&gt;7 releases total&lt;/a&gt;) - 2021-11-30
&lt;br /&gt;A tool for creating credentials for accessing S3 buckets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette"&gt;datasette&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/datasette/releases/tag/0.59.4"&gt;0.59.4&lt;/a&gt; - (&lt;a href="https://github.com/simonw/datasette/releases"&gt;102 releases total&lt;/a&gt;) - 2021-11-30
&lt;br /&gt;An open source multi-tool for exploring and publishing data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-table"&gt;datasette-table&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/datasette-table/releases/tag/0.1.0"&gt;0.1.0&lt;/a&gt; - 2021-11-28
&lt;br /&gt;A Web Component for embedding a Datasette table on a page&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;TIL this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/caddy/pause-retry-traffic"&gt;Pausing traffic and retrying in Caddy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/npm/publish-web-component"&gt;Publishing a Web Component to npm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/datasette/reuse-click-for-register-commands"&gt;Reusing an existing Click tool with register_commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/python/ignore-both-flake8-and-mypy"&gt;Ignoring a line in both flake8 and mypy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/npm"&gt;npm&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/git-scraping"&gt;git-scraping&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="projects"/><category term="npm"/><category term="datasette"/><category term="web-components"/><category term="weeknotes"/><category term="git-scraping"/></entry><entry><title>Weeknotes: Learning Kubernetes, learning Web Components</title><link href="https://simonwillison.net/2021/Oct/28/weeknotes-kubernetes-web-components/#atom-tag" rel="alternate"/><published>2021-10-28T02:44:33+00:00</published><updated>2021-10-28T02:44:33+00:00</updated><id>https://simonwillison.net/2021/Oct/28/weeknotes-kubernetes-web-components/#atom-tag</id><summary type="html">
    &lt;p&gt;I've been mainly climbing the learning curve for Kubernetes and Web Components this week. I also released Datasette 0.59.1 with Python 3.10 compatibility and an updated Docker image.&lt;/p&gt;
&lt;h4&gt;Datasette 0.59.1&lt;/h4&gt;
&lt;p&gt;A few weeks ago I wrote about &lt;a href="https://simonwillison.net/2021/Oct/9/finding-and-reporting-a-bug/"&gt;finding and reporting an asyncio bug in Python 3.10&lt;/a&gt; that I discovered while trying to get Datasette to work on the latest release of Python.&lt;/p&gt;
&lt;p&gt;Łukasz Langa &lt;a href="https://bugs.python.org/msg403506"&gt;offered a workaround&lt;/a&gt; which I submitted as &lt;a href="https://github.com/aio-libs/janus/pull/359"&gt;a PR to the Janus library&lt;/a&gt; that Datasette depends on.&lt;/p&gt;
&lt;p&gt;Andrew Svetlov landed and shipped that fix, which unblocked me from releasing &lt;a href="https://docs.datasette.io/en/stable/changelog.html#v0-59-1"&gt;Datasette 0.59.1&lt;/a&gt; that works with Python 3.10.&lt;/p&gt;
&lt;p&gt;The last step of the Datasette release process, after the package has been released to PyPI, is to build a new Docker image and publish it &lt;a href="https://hub.docker.com/r/datasetteproject/datasette"&gt;to Docker Hub&lt;/a&gt;. Here's the &lt;a href="https://github.com/simonw/datasette/blob/0.59.1/.github/workflows/publish.yml#L100-L119"&gt;GitHub Actions workflow&lt;/a&gt; that does that.&lt;/p&gt;
&lt;p&gt;It turns out this stopped working when I released Datasette 0.59! I was getting this cryptic error message half way through the image build process:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;/usr/bin/perl: error while loading shared libraries: libcrypt.so.1: cannot open shared object file: No such file or directory&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I &lt;a href="https://github.com/simonw/datasette/issues/1497"&gt;opened an issue for myself&lt;/a&gt; and started investigating.&lt;/p&gt;
&lt;p&gt;The culprit was this section of the Datasette Dockerfile:&lt;/p&gt;
&lt;div class="highlight highlight-source-dockerfile"&gt;&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; software-properties-common provides add-apt-repository &lt;/span&gt;
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; which we need in order to install a more recent release &lt;/span&gt;
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; of libsqlite3-mod-spatialite from the sid distribution &lt;/span&gt;
&lt;span class="pl-k"&gt;RUN&lt;/span&gt; apt-get update &amp;amp;&amp;amp; &lt;span class="pl-c1"&gt;\ &lt;/span&gt;
    apt-get -y --no-install-recommends install software-properties-common &amp;amp;&amp;amp; &lt;span class="pl-c1"&gt;\ &lt;/span&gt;
    add-apt-repository &lt;span class="pl-s"&gt;"deb http://httpredir.debian.org/debian sid main"&lt;/span&gt; &amp;amp;&amp;amp; &lt;span class="pl-c1"&gt;\ &lt;/span&gt;
    apt-get update &amp;amp;&amp;amp; &lt;span class="pl-c1"&gt;\ &lt;/span&gt;
    apt-get -t sid install -y --no-install-recommends libsqlite3-mod-spatialite &amp;amp;&amp;amp; &lt;span class="pl-c1"&gt;\ &lt;/span&gt;
    apt-get remove -y software-properties-common &amp;amp;&amp;amp; &lt;span class="pl-c1"&gt;\ &lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This was a hack I introduced seven months ago in order to &lt;a href="https://github.com/simonw/datasette/issues/1249"&gt;upgrade the bundled SpatiaLite to version 5.0&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;SpatiaLite 5.0 wasn't yet available in Debian stable back then, so I used the above convoluted hack to install it from Debian unstable ("Sid") instead.&lt;/p&gt;
&lt;p&gt;When the latest stable version of Debian, &lt;a href="https://www.debian.org/releases/bullseye/"&gt;Debian Bullseye&lt;/a&gt;, came out on October 9th my hack stopped working! I guess that's what I get for messing around with unstable software.&lt;/p&gt;
&lt;p&gt;Thankfully, Bullseye now bundles SpatiaLite 5, so the hack I was using is no longer necessary. I upgraded the Datasette base image from &lt;code&gt;python:3.9.2-slim-buster&lt;/code&gt; to &lt;code&gt;3.9.7-slim-bullseye&lt;/code&gt;, installed SpatialLite the non-hacky way and fixed the issue.&lt;/p&gt;
&lt;p&gt;Doing so also dropped the size of the compressed Datasette image from 94.37MB to 78.94MB, which is nice.&lt;/p&gt;
&lt;h4&gt;Learning Kubernetes&lt;/h4&gt;
&lt;p&gt;Datasette has been designed to run in containers from the very start. I have dozens of instances running on Google Cloud Run, and I've done a bunch of work with Docker as well, including trying out mechanisms to programatically launch new Datasette containers via the Docker API.&lt;/p&gt;
&lt;p&gt;I've dragged my heels on really getting into Kubernetes due to the infamously tough learning curve, but I think it's time to dig in, figure out how to use it and work out what new abilities it can provide me.&lt;/p&gt;
&lt;p&gt;I've spun up small a Kubernetes cluster &lt;a href="https://www.digitalocean.com/products/kubernetes/"&gt;on Digital Ocean&lt;/a&gt;, mainly because I trust their UI to help me not spend hundreds of dollars by mistake. Getting the initial cluster running was very pleasant.&lt;/p&gt;
&lt;p&gt;Now I'm figuring out how to do things with it.&lt;/p&gt;
&lt;p&gt;DigitalOcean's &lt;a href="https://github.com/digitalocean/Kubernetes-Starter-Kit-Developers"&gt;Operations-ready DigitalOcean Kubernetes (DOKS) for Developers&lt;/a&gt; course (which started as a webinar) starts OK and then gets quite complicated quite fast.&lt;/p&gt;
&lt;p&gt;I got Paul Bouwer's &lt;a href="https://github.com/paulbouwer/hello-kubernetes"&gt;hello-kubernetes&lt;/a&gt; demo app working - it introduced me to &lt;a href="https://helm.sh/"&gt;Helm&lt;/a&gt;, but that operates at a higher level than I'm comfortable with - learning my way around &lt;code&gt;kubectl&lt;/code&gt; and Kubernetes YAML is enough of a mental load already without adding an extra abstraction on top.&lt;/p&gt;
&lt;p&gt;I'm reading &lt;a href="https://www.oreilly.com/library/view/kubernetes-up-and/9781492046523/"&gt;Kubernetes: Up and Running&lt;/a&gt; which is promising so far.&lt;/p&gt;
&lt;p&gt;My current goal is to figure out how to run a Datasette instance in a Kubernetes container with an attached persistent volume, so it can handle SQLite writes as well as reads. It looks like &lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/"&gt;StatefulSets&lt;/a&gt; will be key to getting that to work. (Update: apparently not! Graham Dumpleton and Frank Wiles &lt;a href="https://twitter.com/GrahamDumpleton/status/1453554279420084225"&gt;assure me&lt;/a&gt; that I can do this with just a regular Deployment.)&lt;/p&gt;
&lt;p&gt;I'll be sure to write this up as &lt;a href="https://til.simonwillison.net/"&gt;a TIL&lt;/a&gt; once I get it working.&lt;/p&gt;
&lt;h4&gt;Learning Web Components&lt;/h4&gt;
&lt;p&gt;Datasette's visualization plugins - in particular &lt;a href="https://datasette.io/plugins/datasette-vega"&gt;datasette-vega&lt;/a&gt; - are long overdue for some upgrades.&lt;/p&gt;
&lt;p&gt;I've been trying to find a good pattern for writing plugins that avoids too much (ideally any) build tool complexity, and that takes advantage of modern JavaScript - in particular JavaScript modules, which Datasette has supported since &lt;a href="https://simonwillison.net/2021/Jan/25/datasette/#javascript-module-support"&gt;Datasette 0.54&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As such, I'm deeply intrigued by &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components"&gt;Web Components&lt;/a&gt; - which had a big moment this week when it was revealed that Adobe had used them extensively &lt;a href="https://web.dev/ps-on-the-web/"&gt;for Photoshop on the web&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One of my goals for Datasette visualization plugins is for them to be usable on other external pages - since Datasette can expose JSON data over CORS, being able to drop a visualization into an HTML page would be really neat (especially for newsroom purposes).&lt;/p&gt;
&lt;p&gt;Imagine being able to import a JavaScript module and add something like this to get a map of all of the power plants in Portugal:&lt;/p&gt;
&lt;div class="highlight highlight-text-html-basic"&gt;&lt;pre&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;datasette-cluster-map&lt;/span&gt;
  &lt;span class="pl-c1"&gt;data&lt;/span&gt;="&lt;span class="pl-s"&gt;https://global-power-plants.datasettes.com/global-power-plants/global-power-plants.json?country_long=Portugal&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;datasette-cluster-map&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I'm hoping to be able to build components using regular, unadorned modern JavaScript, without the complexity of a build step.&lt;/p&gt;
&lt;p&gt;As such, I've been exploring &lt;a href="https://www.skypack.dev/"&gt;Skypack&lt;/a&gt; (&lt;a href="https://til.simonwillison.net/javascript/lit-with-skypack"&gt;TIL&lt;/a&gt;) and &lt;a href="https://www.snowpack.dev/"&gt;Snowpack&lt;/a&gt; which help bridge the gap between build-tooling-dependent npm packages and the modern world of native browser &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules"&gt;ES modules&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I was also impressed this week by &lt;a href="https://tonicframework.dev/"&gt;Tonic&lt;/a&gt;, a framework for building components without a build step that weighs in at just 350 lines of code and makes extremely clever use of tagged template literals and async generators.&lt;/p&gt;
&lt;p&gt;This morning I saw this clever example of a &lt;a href="https://gist.github.com/kristoferjoseph/c4e47389ae0f0447db175b914e471628"&gt;Single File Web Component&lt;/a&gt; by Kristofer Joseph - I ended up creating my own annotated version of his code which &lt;a href="https://til.simonwillison.net/web-components/understanding-single-file-web-component"&gt;I shared in this TIL&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next step: I need to write some web components of my own!&lt;/p&gt;
&lt;h4&gt;Releases this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette"&gt;datasette&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/datasette/releases/tag/0.59.1"&gt;0.59.1&lt;/a&gt; - (&lt;a href="https://github.com/simonw/datasette/releases"&gt;99 releases total&lt;/a&gt;) - 2021-10-24
&lt;br /&gt;An open source multi-tool for exploring and publishing data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-hello-world"&gt;datasette-hello-world&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/datasette-hello-world/releases/tag/0.1"&gt;0.1&lt;/a&gt; - 2021-10-21
&lt;br /&gt;The hello world of Datasette plugins&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;TIL this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/git/remove-commit-and-force-push"&gt;Removing a git commit and force pushing to remove it from history&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/web-components/understanding-single-file-web-component"&gt;Understanding Kristofer Joseph's Single File Web Component&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/docker"&gt;docker&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/kubernetes"&gt;kubernetes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="docker"/><category term="datasette"/><category term="kubernetes"/><category term="web-components"/><category term="weeknotes"/></entry><entry><title>Tonic</title><link href="https://simonwillison.net/2021/Oct/23/tonic/#atom-tag" rel="alternate"/><published>2021-10-23T05:21:54+00:00</published><updated>2021-10-23T05:21:54+00:00</updated><id>https://simonwillison.net/2021/Oct/23/tonic/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://tonicframework.dev/"&gt;Tonic&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Really interesting library for building Web Components: it’s tiny (just 350 lines of code), works directly in browsers without any compile or build step and makes very creative use of modern JavaScript features such as async generators.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/slightlylate/status/1451654821082120192"&gt;Alex Russell&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;&lt;/p&gt;



</summary><category term="javascript"/><category term="web-components"/></entry><entry><title>matthewp/haunted: React's Hooks API implemented for web components</title><link href="https://simonwillison.net/2018/Oct/31/haunted/#atom-tag" rel="alternate"/><published>2018-10-31T01:04:33+00:00</published><updated>2018-10-31T01:04:33+00:00</updated><id>https://simonwillison.net/2018/Oct/31/haunted/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/matthewp/haunted"&gt;matthewp/haunted: React&amp;#x27;s Hooks API implemented for web components&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
It’s been fascinating over the past few days watching various frontend web stacks start playing with the new ideas introduced by the proposed React hooks API. lit-html is one of my favourite React alternatives—it’s built on web components and makes really clever use of ES6 template literals (in place of React’s JSX, which requires an additional compilation step). With Haunted Matthew Phillips explores the combination of lit-html, web components and hooks-style state management.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/matthewcp/status/1057078826277261312"&gt;@matthewcp&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/react"&gt;react&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-components"&gt;web-components&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/lit-html"&gt;lit-html&lt;/a&gt;&lt;/p&gt;



</summary><category term="javascript"/><category term="react"/><category term="web-components"/><category term="lit-html"/></entry></feed>