<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: accessibility</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/accessibility.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2025-10-21T18:45:13+00:00</updated><author><name>Simon Willison</name></author><entry><title>Introducing ChatGPT Atlas</title><link href="https://simonwillison.net/2025/Oct/21/introducing-chatgpt-atlas/#atom-tag" rel="alternate"/><published>2025-10-21T18:45:13+00:00</published><updated>2025-10-21T18:45:13+00:00</updated><id>https://simonwillison.net/2025/Oct/21/introducing-chatgpt-atlas/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://openai.com/index/introducing-chatgpt-atlas/"&gt;Introducing ChatGPT Atlas&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Last year OpenAI &lt;a href="https://www.searchenginejournal.com/openai-hires-former-chrome-engineer-eyes-browser-battle/533533/"&gt;hired Chrome engineer Darin Fisher&lt;/a&gt;, which sparked speculation they might have their own browser in the pipeline. Today it arrived.&lt;/p&gt;
&lt;p&gt;ChatGPT Atlas is a Mac-only web browser with a variety of ChatGPT-enabled features. You can bring up a chat panel next to a web page, which will automatically be populated with the context of that page.&lt;/p&gt;
&lt;p&gt;The "browser memories" feature is particularly notable, &lt;a href="https://help.openai.com/en/articles/12591856-chatgpt-atlas-release-notes"&gt;described here&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you turn on browser memories, ChatGPT will remember key details from your web browsing to improve chat responses and offer smarter suggestions—like retrieving a webpage you read a while ago. Browser memories are private to your account and under your control. You can view them all in settings, archive ones that are no longer relevant, and clear your browsing history to delete them. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Atlas also has an experimental "agent mode" where ChatGPT can take over navigating and interacting with the page for you, accompanied by a weird sparkle overlay effect:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of Simon Willison's Weblog showing search results for &amp;quot;browser agents&amp;quot; with 38 results on page 1 of 2. The first result is titled &amp;quot;Agentic Browser Security: Indirect Prompt Injection in Perplexity Comet&amp;quot; and discusses security vulnerabilities in LLM-powered browser extensions. A tooltip shows &amp;quot;Opening the first result&amp;quot; and on the right side is a ChatGPT interface panel titled &amp;quot;Simon Willison's Weblog&amp;quot; with text explaining &amp;quot;Use agent mode search this site for browser agents&amp;quot; and &amp;quot;Opening the first result&amp;quot; with a description of the research intent. At the bottom of the screen is a browser notification showing &amp;quot;browser agents&amp;quot; in posts with &amp;quot;Take control&amp;quot; and &amp;quot;Stop&amp;quot; buttons." src="https://static.simonwillison.net/static/2025/chatgpt-atlas.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Here's how the &lt;a href="https://help.openai.com/en/articles/12591856-chatgpt-atlas-release-notes"&gt;help page&lt;/a&gt; describes that mode:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In agent mode, ChatGPT can complete end to end tasks for you like researching a meal plan, making a list of ingredients, and adding the groceries to a shopping cart ready for delivery. You're always in control: ChatGPT is trained to ask before taking many important actions, and you can pause, interrupt, or take over the browser at any time.&lt;/p&gt;
&lt;p&gt;Agent mode runs also operates under boundaries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;System access: Cannot run code in the browser, download files, or install extensions.&lt;/li&gt;
&lt;li&gt;Data access: Cannot access other apps on your computer or your file system, read or write ChatGPT memories, access saved passwords, or use autofill data.&lt;/li&gt;
&lt;li&gt;Browsing activity: Pages ChatGPT visits in agent mode are not added to your browsing history.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can also choose to run agent in logged out mode, and ChatGPT won't use any pre-existing cookies and won't be logged into any of your online accounts without your specific approval.&lt;/p&gt;
&lt;p&gt;These efforts don't eliminate every risk; users should still use caution and monitor ChatGPT activities when using agent mode.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I continue to find this entire category of &lt;a href="https://simonwillison.net/tags/browser-agents/"&gt;browser agents&lt;/a&gt; &lt;em&gt;deeply&lt;/em&gt; confusing.&lt;/p&gt;
&lt;p&gt;The security and privacy risks involved here still feel insurmountably high to me - I certainly won't be trusting any of these products until a bunch of security researchers have given them a very thorough beating.&lt;/p&gt;
&lt;p&gt;I'd like to see a &lt;em&gt;deep&lt;/em&gt; explanation of the steps Atlas takes to avoid prompt injection attacks. Right now it looks like the main defense is expecting the user to carefully watch what agent mode is doing at all times!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update&lt;/strong&gt;: OpenAI's CISO Dane Stuckey provided exactly that &lt;a href="https://simonwillison.net/2025/Oct/22/openai-ciso-on-atlas/"&gt;the day after the launch&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;/p&gt;
&lt;p&gt;I also find these products pretty unexciting to use. I tried out agent mode and it was like watching a first-time computer user painstakingly learn to use a mouse for the first time. I have yet to find my own use-cases for when this kind of interaction feels useful to me, though I'm not ruling that out.&lt;/p&gt;
&lt;p&gt;There was one other detail in the announcement post that caught my eye:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Website owners can also add &lt;a href="https://help.openai.com/en/articles/12627856-publishers-and-developers-faq#h_30e9aae450"&gt;ARIA&lt;/a&gt; tags to improve how ChatGPT agent works for their websites in Atlas.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Which links to this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ChatGPT Atlas uses ARIA tags---the same labels and roles that support screen readers---to interpret page structure and interactive elements. To improve compatibility, follow &lt;a href="https://www.w3.org/WAI/ARIA/apg/"&gt;WAI-ARIA best practices&lt;/a&gt; by adding descriptive roles, labels, and states to interactive elements like buttons, menus, and forms. This helps ChatGPT recognize what each element does and interact with your site more accurately.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A neat reminder that AI "agents" share many of the characteristics of assistive technologies, and benefit from the same affordances.&lt;/p&gt;
&lt;p&gt;The Atlas user-agent is &lt;code&gt;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36&lt;/code&gt; - identical to the user-agent I get for the latest Google Chrome on macOS.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=45658479"&gt;Hacker News&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/aria"&gt;aria&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/browsers"&gt;browsers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/privacy"&gt;privacy&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/openai"&gt;openai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/prompt-injection"&gt;prompt-injection&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/ai-agents"&gt;ai-agents&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/browser-agents"&gt;browser-agents&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="aria"/><category term="browsers"/><category term="chrome"/><category term="privacy"/><category term="security"/><category term="ai"/><category term="openai"/><category term="prompt-injection"/><category term="generative-ai"/><category term="ai-agents"/><category term="browser-agents"/></entry><entry><title>Should form labels be wrapped or separate?</title><link href="https://simonwillison.net/2025/Oct/17/form-labels/#atom-tag" rel="alternate"/><published>2025-10-17T18:25:45+00:00</published><updated>2025-10-17T18:25:45+00:00</updated><id>https://simonwillison.net/2025/Oct/17/form-labels/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.tpgi.com/should-form-labels-be-wrapped-or-separate/"&gt;Should form labels be wrapped or separate?&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
James Edwards notes that wrapping a form input in a label event like this has a significant downside:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;label&amp;gt;Name &amp;lt;input type="text"&amp;gt;&amp;lt;/label&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It turns out both Dragon Naturally Speaking for Windows and Voice Control for macOS and iOS fail to understand this relationship!&lt;/p&gt;
&lt;p&gt;You need to use the explicit &lt;code&gt;&amp;lt;label for="element_id"&amp;gt;&lt;/code&gt; syntax to ensure those screen readers correctly understand the relationship between label and form field. You can still nest the input inside the label if you like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;label for="idField"&amp;gt;Name
  &amp;lt;input id="idField" type="text"&amp;gt;
&amp;lt;/label&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://gomakethings.com/implicit-labels-arent/"&gt;Chris Ferdinandi&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/html"&gt;html&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/screen-readers"&gt;screen-readers&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="html"/><category term="screen-readers"/></entry><entry><title>New dashboard: alt text for all my images</title><link href="https://simonwillison.net/2025/Apr/28/dashboard-alt-text/#atom-tag" rel="alternate"/><published>2025-04-28T01:22:27+00:00</published><updated>2025-04-28T01:22:27+00:00</updated><id>https://simonwillison.net/2025/Apr/28/dashboard-alt-text/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://simonwillison.net/dashboard/alt-text/"&gt;New dashboard: alt text for all my images&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I got curious today about how I'd been using alt text for images on my blog, and realized that since I have &lt;a href="https://django-sql-dashboard.datasette.io/"&gt;Django SQL Dashboard&lt;/a&gt; running on this site and PostgreSQL is capable of &lt;a href="https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454"&gt;parsing HTML with regular expressions&lt;/a&gt; I could probably find out using a SQL query.&lt;/p&gt;
&lt;p&gt;I pasted &lt;a href="https://simonwillison.net/dashboard/schema/"&gt;my PostgreSQL schema&lt;/a&gt; into Claude and gave it a pretty long prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Give this PostgreSQL schema I want a query that returns all of my images and their alt text. Images are sometimes stored as HTML image tags and other times stored in markdown.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;blog_quotation.quotation&lt;/code&gt;, &lt;code&gt;blog_note.body&lt;/code&gt; both contain markdown. &lt;code&gt;blog_blogmark.commentary&lt;/code&gt; has markdown if &lt;code&gt;use_markdown&lt;/code&gt; is true or HTML otherwise. &lt;code&gt;blog_entry.body&lt;/code&gt; is always HTML&lt;/p&gt;
&lt;p&gt;Write me a SQL query to extract all of my images and their alt tags using regular expressions. In HTML documents it should look for either &lt;code&gt;&amp;lt;img .* src="..." .* alt="..."&lt;/code&gt; or &lt;code&gt;&amp;lt;img alt="..." .* src="..."&lt;/code&gt; (images may be self-closing XHTML style in some places). In Markdown they will always be &lt;code&gt;![alt text](url)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I want the resulting table to have three columns: URL, alt_text, src - the URL column needs to be constructed as e.g. &lt;code&gt;/2025/Feb/2/slug&lt;/code&gt; for a record where created is on 2nd feb 2025 and the &lt;code&gt;slug&lt;/code&gt; column contains &lt;code&gt;slug&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Use CTEs and unions where appropriate&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It almost got it right on the first go, and with &lt;a href="https://claude.ai/share/e3b996d3-b480-436d-aa40-9caa7609474f"&gt;a couple of follow-up prompts&lt;/a&gt; I had the query I wanted. I also added the option to &lt;a href="https://simonwillison.net/dashboard/alt-text/?search=pelican"&gt;search&lt;/a&gt; my alt text / image URLs, which has already helped me hunt down and fix a few old images on expired domain names. Here's a copy of &lt;a href="https://gist.github.com/simonw/5b44a662354e124e33cc1d4704cdb91a"&gt;the finished 100 line SQL query&lt;/a&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/alt-text"&gt;alt-text&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/postgresql"&gt;postgresql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sql"&gt;sql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django-sql-dashboard"&gt;django-sql-dashboard&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/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude"&gt;claude&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="alt-text"/><category term="postgresql"/><category term="sql"/><category term="ai"/><category term="django-sql-dashboard"/><category term="generative-ai"/><category term="llms"/><category term="ai-assisted-programming"/><category term="claude"/></entry><entry><title>Notes from my Accessibility and Gen AI podcast appearance</title><link href="https://simonwillison.net/2025/Mar/2/accessibility-and-gen-ai/#atom-tag" rel="alternate"/><published>2025-03-02T14:51:43+00:00</published><updated>2025-03-02T14:51:43+00:00</updated><id>https://simonwillison.net/2025/Mar/2/accessibility-and-gen-ai/#atom-tag</id><summary type="html">
    &lt;p&gt;I was a guest on &lt;a href="https://accessibility-and-gen-ai.simplecast.com/episodes/ep-6-simon-willison-datasette"&gt;the most recent episode&lt;/a&gt; of the &lt;a href="https://linktr.ee/a11ygenai"&gt;Accessibility + Gen AI Podcast&lt;/a&gt;, hosted by Eamon McErlean and Joe Devon. We had a really fun, wide-ranging conversation about a host of different topics. I've extracted a few choice quotes from the transcript.&lt;/p&gt;

&lt;p&gt;&lt;lite-youtube videoid="zoxpEM6TLEU" js-api="js-api"
  title="Ep 6 - Simon Willison - Creator, Datasette"
  playlabel="Play: Ep 6 - Simon Willison - Creator, Datasette"
&gt; &lt;/lite-youtube&gt;&lt;/p&gt;

&lt;h4 id="alt-text"&gt;LLMs for drafting alt text&lt;/h4&gt;

&lt;p&gt;I use LLMs for the first draft of my alt text (&lt;a href="https://www.youtube.com/watch?v=zoxpEM6TLEU&amp;amp;t=22m10s"&gt;22:10&lt;/a&gt;):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I actually use Large Language Models for most of my alt text these days. Whenever I tweet an image or whatever, I've got a Claude project called Alt text writer. It's got a prompt and an example. I dump an image in and it gives me the alt text.&lt;/p&gt;
&lt;p&gt;I very rarely just use it because that's rude, right? You should never dump text onto people that you haven't reviewed yourself. But it's always a good starting point.&lt;/p&gt;
&lt;p&gt;Normally I'll edit a tiny little bit. I'll delete an unimportant detail or I'll bulk something up. And then I've got alt text that works.&lt;/p&gt;
&lt;p&gt;Often it's actually got really good taste. A great example is if you've got a screenshot of an interface, there's a lot of words in that screenshot and most of them don't matter.&lt;/p&gt;
&lt;p&gt;The message you're trying to give in the alt text is that it's two panels on the left, there's a conversation on the right, there's a preview of the SVG file or something. My alt text writer normally gets that right.&lt;/p&gt;
&lt;p&gt;It's even good at summarizing tables of data where it will notice that actually what really matters is that Gemini got a score of 57 and Nova got a score of 53 - so it will pull those details out and ignore [irrelevant columns] like the release dates and so forth.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here's the current custom instructions prompt I'm using for that Claude Project:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;You write alt text for any image pasted in by the user. Alt text is always presented in a fenced code block to make it easy to copy and paste out. It is always presented on a single line so it can be used easily in Markdown images. All text on the image (for screenshots etc) must be exactly included. A short note describing the nature of the image itself should go first.&lt;/code&gt;&lt;/p&gt;&lt;/blockquote&gt;

&lt;h4 id="ethics"&gt;Is it ethical to build unreliable accessibility tools?&lt;/h4&gt;

&lt;p&gt;On the ethics of building accessibility tools on top of inherently unreliable technology (&lt;a href="https://www.youtube.com/watch?v=zoxpEM6TLEU&amp;amp;t=5m35s"&gt;5:33&lt;/a&gt;):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Some people I've talked to have been skeptical about the accessibility benefits because their argument is that if you give somebody unreliable technology that might hallucinate and make things up, surely that's harming them.&lt;/p&gt;
&lt;p&gt;I don't think that's true. I feel like people who use screen readers are used to unreliable technology.&lt;/p&gt;
&lt;p&gt;You know, if you use a guide dog - it's a wonderful thing and a &lt;em&gt;very&lt;/em&gt; unreliable piece of technology.&lt;/p&gt;
&lt;p&gt;When you consider that people with accessibility needs have agency, they can understand the limitations of the technology they're using. I feel like giving them a tool where they can point their phone at something and it can describe it to them is a world away from accessibility technology just three or four years ago.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="not-a-threat"&gt;Why I don't feel threatened as a software engineer&lt;/h4&gt;
&lt;p&gt;This is probably my most coherent explanation yet of why I don't see generative AI as a threat to my career as a software engineer (&lt;a href="https://www.youtube.com/watch?v=zoxpEM6TLEU&amp;amp;t=33m51s"&gt;33:49&lt;/a&gt;):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;My perspective on this as a developer who's been using these systems on a daily basis for a couple of years now is that I find that they enhance my value. I am so much more competent and capable as a developer because I've got these tools assisting me. I can write code in dozens of new programming languages that I never learned before.&lt;/p&gt;
&lt;p&gt;But I still get to benefit from my 20 years of experience.&lt;/p&gt;
&lt;p&gt;Take somebody off the street who's never written any code before and ask them to build an iPhone app with ChatGPT. They are going to run into so many pitfalls, because programming isn't just about can you write code - it's about thinking through the problems, understanding what's possible and what's not, understanding how to QA, what good code is, having good taste.&lt;/p&gt;
&lt;p&gt;There's so much depth to what we do as software engineers.&lt;/p&gt;
&lt;p&gt;I've said before that generative AI probably gives me like two to five times productivity boost on the part of my job that involves typing code into a laptop. But that's only 10 percent of what I do. As a software engineer, most of my time isn't actually spent with the typing of the code. It's all of those other activities.&lt;/p&gt;
&lt;p&gt;The AI systems help with those other activities, too. They can help me think through architectural decisions and research library options and so on. But I still have to have that agency to understand what I'm doing.&lt;/p&gt;
&lt;p&gt;So as a software engineer, I don't feel threatened. My most optimistic view of this is that the cost of developing software goes down because an engineer like myself can be more ambitious, can take on more things. As a result, demand for software goes up - because if you're a company that previously would never have dreamed of building a custom CRM for your industry because it would have taken 20 engineers a year before you got any results... If it now takes four engineers three months to get results, maybe you're in the market for software engineers now that you weren't before.&lt;/p&gt;
&lt;/blockquote&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/alt-text"&gt;alt-text&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/podcasts"&gt;podcasts&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&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/podcast-appearances"&gt;podcast-appearances&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="accessibility"/><category term="alt-text"/><category term="podcasts"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="podcast-appearances"/></entry><entry><title>Why are my live regions not working?</title><link href="https://simonwillison.net/2025/Jan/8/why-are-my-live-regions-not-working/#atom-tag" rel="alternate"/><published>2025-01-08T03:54:21+00:00</published><updated>2025-01-08T03:54:21+00:00</updated><id>https://simonwillison.net/2025/Jan/8/why-are-my-live-regions-not-working/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://tetralogical.com/blog/2024/05/01/why-are-my-live-regions-not-working/"&gt;Why are my live regions not working?&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Useful article to help understand &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions"&gt;ARIA live regions&lt;/a&gt;. Short version: you can add a live region to your page like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id="notification" aria-live="assertive"&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then any time you use JavaScript to modify the text content in that element it will be announced straight away by any screen readers - that's the "assertive" part. Using "polite" instead will cause the notification to be queued up for when the user is idle instead.&lt;/p&gt;
&lt;p&gt;There are quite a few catches. Most notably, the contents of an &lt;code&gt;aria-live&lt;/code&gt; region will usually NOT be spoken out loud when the page first loads, or when that element is added to the DOM. You need to ensure the element is available and &lt;em&gt;not hidden&lt;/em&gt; before updating it for the effect to work reliably across different screen readers.&lt;/p&gt;
&lt;p&gt;I got Claude Artifacts &lt;a href="https://gist.github.com/simonw/50946b742ef5da7d0435c341b2d6fa8b"&gt;to help me&lt;/a&gt; build a demo for this, which is now available at &lt;a href="https://tools.simonwillison.net/aria-live-regions"&gt;tools.simonwillison.net/aria-live-regions&lt;/a&gt;. The demo includes instructions for turning VoiceOver on and off on both iOS and macOS to help try that out.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=42613221#42618062"&gt;Comment on Hacker News&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/aria"&gt;aria&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/screen-readers"&gt;screen-readers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude-artifacts"&gt;claude-artifacts&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/prompt-to-app"&gt;prompt-to-app&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="aria"/><category term="javascript"/><category term="screen-readers"/><category term="ai-assisted-programming"/><category term="claude-artifacts"/><category term="prompt-to-app"/></entry><entry><title>Clay UI library</title><link href="https://simonwillison.net/2024/Dec/21/clay-ui-library/#atom-tag" rel="alternate"/><published>2024-12-21T23:12:17+00:00</published><updated>2024-12-21T23:12:17+00:00</updated><id>https://simonwillison.net/2024/Dec/21/clay-ui-library/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.nicbarker.com/clay"&gt;Clay UI library&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Fascinating project by Nic Barker, who describes Clay like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;His &lt;a href="https://www.youtube.com/watch?v=DYWTw19_8r4"&gt;intro video&lt;/a&gt; to the library is outstanding: I learned a ton about how UI layout works from this, and the animated visual explanations are clear, tasteful and really helped land the different concepts:&lt;/p&gt;
&lt;p&gt;&lt;lite-youtube videoid="DYWTw19_8r4"
  title="Introducing Clay - High Performance UI Layout in C"
  playlabel="Play: Introducing Clay - High Performance UI Layout in C"
&gt; &lt;/lite-youtube&gt;&lt;/p&gt;

&lt;p&gt;Clay is a C library delivered in a single ~2000 line &lt;a href="https://github.com/nicbarker/clay/blob/main/clay.h"&gt;clay.h&lt;/a&gt; dependency-free header file. It only handles layout calculations: if you want to render the result you need to add an additional rendering layer.&lt;/p&gt;
&lt;p&gt;In a fascinating demo of the library, the &lt;a href="https://www.nicbarker.com/clay"&gt;Clay site itself&lt;/a&gt; is rendered using Clay C compiled to WebAssembly! You can even switch between the default HTML renderer and an alternative based on Canvas.&lt;/p&gt;
&lt;p&gt;This isn't necessarily a great idea: because the layout is entirely handled using &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements positioned using &lt;code&gt;transform: translate(0px, 70px)&lt;/code&gt; style CSS attempting to select text across multiple boxes behaves strangely, and it's not clear to me what the accessibility implications are.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: &lt;a href="https://toot.cafe/@matt/113693374074675126"&gt;Matt Campbell&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The accessibility implications are as serious as you might guess. The links aren't properly labeled, there's no semantic markup such as headings, and since there's a div for every line, continuous reading with a screen reader is choppy, that is, it pauses at the end of every physical line.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It does make for a very compelling demo of what Clay is capable of though, especially when you resize your browser window and the page layout is recalculated in real-time via the Clay WebAssembly bridge.&lt;/p&gt;
&lt;p&gt;You can hit "D" on the website and open up a custom Clay debugger showing the hierarchy of layout elements on the page:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Clay website on the left, on the right is a panel showing a tree of UI layout elements, one has been selected and is showing details in a box at the bottom of the panel: Bounding Box: { x: 278, y: 13, width: 101, height: 24}, Layout Direction: LEFT_TO_RIGHT, Sizing: width: FITQ, height: FITQ, Padding: {x:8,uy:0}" src="https://static.simonwillison.net/static/2024/clay-debug.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;This also means that the entire page is defined using C code! Given that, I find the code itself &lt;a href="https://github.com/nicbarker/clay/blob/35d72e5fba6872be48d15ed9d84269a86cd72b4e/examples/clay-official-website/main.c#L124-L139"&gt;surprisingly readable&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight highlight-source-c"&gt;&lt;pre&gt;&lt;span class="pl-smi"&gt;void&lt;/span&gt; &lt;span class="pl-en"&gt;DeclarativeSyntaxPageDesktop&lt;/span&gt;() {
  &lt;span class="pl-en"&gt;CLAY&lt;/span&gt;(&lt;span class="pl-en"&gt;CLAY_ID&lt;/span&gt;(&lt;span class="pl-s"&gt;"SyntaxPageDesktop"&lt;/span&gt;), &lt;span class="pl-en"&gt;CLAY_LAYOUT&lt;/span&gt;({ .&lt;span class="pl-s1"&gt;sizing&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; { &lt;span class="pl-en"&gt;CLAY_SIZING_GROW&lt;/span&gt;(), &lt;span class="pl-en"&gt;CLAY_SIZING_FIT&lt;/span&gt;({ .&lt;span class="pl-s1"&gt;min&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;windowHeight&lt;/span&gt; &lt;span class="pl-c1"&gt;-&lt;/span&gt; &lt;span class="pl-c1"&gt;50&lt;/span&gt; }) }, .&lt;span class="pl-s1"&gt;childAlignment&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; {&lt;span class="pl-c1"&gt;0&lt;/span&gt;, &lt;span class="pl-c1"&gt;CLAY_ALIGN_Y_CENTER&lt;/span&gt;}, .&lt;span class="pl-s1"&gt;padding&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; {.&lt;span class="pl-s1"&gt;x&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;50&lt;/span&gt;} })) {
    &lt;span class="pl-c1"&gt;CLAY&lt;/span&gt;(&lt;span class="pl-en"&gt;CLAY_ID&lt;/span&gt;(&lt;span class="pl-s"&gt;"SyntaxPage"&lt;/span&gt;), &lt;span class="pl-c1"&gt;CLAY_LAYOUT&lt;/span&gt;({ .&lt;span class="pl-s1"&gt;sizing&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; { &lt;span class="pl-en"&gt;CLAY_SIZING_GROW&lt;/span&gt;(), &lt;span class="pl-en"&gt;CLAY_SIZING_GROW&lt;/span&gt;() }, .&lt;span class="pl-s1"&gt;childAlignment&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; { &lt;span class="pl-c1"&gt;0&lt;/span&gt;, &lt;span class="pl-c1"&gt;CLAY_ALIGN_Y_CENTER&lt;/span&gt; }, .&lt;span class="pl-s1"&gt;padding&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; { &lt;span class="pl-c1"&gt;32&lt;/span&gt;, &lt;span class="pl-c1"&gt;32&lt;/span&gt; }, .&lt;span class="pl-s1"&gt;childGap&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;32&lt;/span&gt; }), &lt;span class="pl-en"&gt;CLAY_BORDER&lt;/span&gt;({ .&lt;span class="pl-s1"&gt;left&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; { &lt;span class="pl-c1"&gt;2&lt;/span&gt;, &lt;span class="pl-c1"&gt;COLOR_RED&lt;/span&gt; }, .&lt;span class="pl-s1"&gt;right&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; { &lt;span class="pl-c1"&gt;2&lt;/span&gt;, &lt;span class="pl-c1"&gt;COLOR_RED&lt;/span&gt; } })) {
      &lt;span class="pl-c1"&gt;CLAY&lt;/span&gt;(&lt;span class="pl-en"&gt;CLAY_ID&lt;/span&gt;(&lt;span class="pl-s"&gt;"SyntaxPageLeftText"&lt;/span&gt;), &lt;span class="pl-c1"&gt;CLAY_LAYOUT&lt;/span&gt;({ .&lt;span class="pl-s1"&gt;sizing&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; { &lt;span class="pl-en"&gt;CLAY_SIZING_PERCENT&lt;/span&gt;(&lt;span class="pl-c1"&gt;0.5&lt;/span&gt;) }, .&lt;span class="pl-c1"&gt;layoutDirection&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;CLAY_TOP_TO_BOTTOM&lt;/span&gt;, .&lt;span class="pl-c1"&gt;childGap&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;8&lt;/span&gt; })) {
        &lt;span class="pl-en"&gt;CLAY_TEXT&lt;/span&gt;(&lt;span class="pl-en"&gt;CLAY_STRING&lt;/span&gt;(&lt;span class="pl-s"&gt;"Declarative Syntax"&lt;/span&gt;), &lt;span class="pl-en"&gt;CLAY_TEXT_CONFIG&lt;/span&gt;({ .&lt;span class="pl-s1"&gt;fontSize&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;52&lt;/span&gt;, .&lt;span class="pl-c1"&gt;fontId&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;FONT_ID_TITLE_56&lt;/span&gt;, .&lt;span class="pl-c1"&gt;textColor&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;COLOR_RED&lt;/span&gt; }));
        &lt;span class="pl-en"&gt;CLAY&lt;/span&gt;(&lt;span class="pl-en"&gt;CLAY_ID&lt;/span&gt;(&lt;span class="pl-s"&gt;"SyntaxSpacer"&lt;/span&gt;), &lt;span class="pl-en"&gt;CLAY_LAYOUT&lt;/span&gt;({ .&lt;span class="pl-s1"&gt;sizing&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; { &lt;span class="pl-en"&gt;CLAY_SIZING_GROW&lt;/span&gt;({ .&lt;span class="pl-s1"&gt;max&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;16&lt;/span&gt; }) } })) {}
        &lt;span class="pl-en"&gt;CLAY_TEXT&lt;/span&gt;(&lt;span class="pl-en"&gt;CLAY_STRING&lt;/span&gt;(&lt;span class="pl-s"&gt;"Flexible and readable declarative syntax with nested UI element hierarchies."&lt;/span&gt;), &lt;span class="pl-en"&gt;CLAY_TEXT_CONFIG&lt;/span&gt;({ .&lt;span class="pl-s1"&gt;fontSize&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;28&lt;/span&gt;, .&lt;span class="pl-c1"&gt;fontId&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;FONT_ID_BODY_36&lt;/span&gt;, .&lt;span class="pl-c1"&gt;textColor&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;COLOR_RED&lt;/span&gt; }));
        &lt;span class="pl-en"&gt;CLAY_TEXT&lt;/span&gt;(&lt;span class="pl-en"&gt;CLAY_STRING&lt;/span&gt;(&lt;span class="pl-s"&gt;"Mix elements with standard C code like loops, conditionals and functions."&lt;/span&gt;), &lt;span class="pl-en"&gt;CLAY_TEXT_CONFIG&lt;/span&gt;({ .&lt;span class="pl-s1"&gt;fontSize&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;28&lt;/span&gt;, .&lt;span class="pl-c1"&gt;fontId&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;FONT_ID_BODY_36&lt;/span&gt;, .&lt;span class="pl-c1"&gt;textColor&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;COLOR_RED&lt;/span&gt; }));
        &lt;span class="pl-en"&gt;CLAY_TEXT&lt;/span&gt;(&lt;span class="pl-en"&gt;CLAY_STRING&lt;/span&gt;(&lt;span class="pl-s"&gt;"Create your own library of re-usable components from UI primitives like text, images and rectangles."&lt;/span&gt;), &lt;span class="pl-en"&gt;CLAY_TEXT_CONFIG&lt;/span&gt;({ .&lt;span class="pl-s1"&gt;fontSize&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;28&lt;/span&gt;, .&lt;span class="pl-c1"&gt;fontId&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;FONT_ID_BODY_36&lt;/span&gt;, .&lt;span class="pl-c1"&gt;textColor&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;COLOR_RED&lt;/span&gt; }));
      }
      &lt;span class="pl-en"&gt;CLAY&lt;/span&gt;(&lt;span class="pl-en"&gt;CLAY_ID&lt;/span&gt;(&lt;span class="pl-s"&gt;"SyntaxPageRightImage"&lt;/span&gt;), &lt;span class="pl-en"&gt;CLAY_LAYOUT&lt;/span&gt;({ .&lt;span class="pl-s1"&gt;sizing&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; { &lt;span class="pl-en"&gt;CLAY_SIZING_PERCENT&lt;/span&gt;(&lt;span class="pl-c1"&gt;0.50&lt;/span&gt;) }, .&lt;span class="pl-c1"&gt;childAlignment&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; {.&lt;span class="pl-s1"&gt;x&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;CLAY_ALIGN_X_CENTER&lt;/span&gt;} })) {
        &lt;span class="pl-c1"&gt;CLAY&lt;/span&gt;(&lt;span class="pl-en"&gt;CLAY_ID&lt;/span&gt;(&lt;span class="pl-s"&gt;"SyntaxPageRightImageInner"&lt;/span&gt;), &lt;span class="pl-en"&gt;CLAY_LAYOUT&lt;/span&gt;({ .&lt;span class="pl-s1"&gt;sizing&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; { &lt;span class="pl-en"&gt;CLAY_SIZING_GROW&lt;/span&gt;({ .&lt;span class="pl-s1"&gt;max&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;568&lt;/span&gt; }) } }), &lt;span class="pl-c1"&gt;CLAY_IMAGE&lt;/span&gt;({ .&lt;span class="pl-s1"&gt;sourceDimensions&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; {&lt;span class="pl-c1"&gt;1136&lt;/span&gt;, &lt;span class="pl-c1"&gt;1194&lt;/span&gt;}, .&lt;span class="pl-s1"&gt;sourceURL&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;CLAY_STRING&lt;/span&gt;(&lt;span class="pl-s"&gt;"/clay/images/declarative.png"&lt;/span&gt;) })) {}
      }
    }
  }
}&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I'm not ready to ditch HTML and CSS for writing my web pages in C compiled to WebAssembly just yet, but as an exercise in understanding layout engines (and a potential tool for building non-web interfaces in the future) this is a really interesting project to dig into.&lt;/p&gt;
&lt;p&gt;To clarify here: I don't think the web layout / WebAssembly thing is the key idea behind Clay at all - I think it's a neat demo of the library, but it's not what Clay is &lt;em&gt;for&lt;/em&gt;. It's certainly an interesting way to provide a demo of a layout library!&lt;/p&gt;
&lt;p&gt;Nic &lt;a href="https://bsky.app/profile/nicbarker.com/post/3ldu44rxyx22h"&gt;confirms&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You totally nailed it, the fact that you can compile to wasm and run in HTML stemmed entirely from a “wouldn’t it be cool if…” It was designed for my C projects first and foremost!&lt;/p&gt;
&lt;/blockquote&gt;

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=42463123"&gt;Hacker News&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/c"&gt;c&lt;/a&gt;, &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/webassembly"&gt;webassembly&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="c"/><category term="css"/><category term="html"/><category term="webassembly"/></entry><entry><title>Themes from DjangoCon US 2024</title><link href="https://simonwillison.net/2024/Sep/27/themes-from-djangocon-us-2024/#atom-tag" rel="alternate"/><published>2024-09-27T23:36:02+00:00</published><updated>2024-09-27T23:36:02+00:00</updated><id>https://simonwillison.net/2024/Sep/27/themes-from-djangocon-us-2024/#atom-tag</id><summary type="html">
    &lt;p&gt;I just arrived home from a trip to Durham, North Carolina for DjangoCon US 2024. I’ve already written &lt;a href="https://simonwillison.net/2024/Sep/25/djp-a-plugin-system-for-django/"&gt;about my talk where I announced a new plugin system for Django&lt;/a&gt;; here are my notes on some of the other themes that resonated with me during the conference.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2024/Sep/27/themes-from-djangocon-us-2014/#growing-the-django-software-foundation-dsf-"&gt;Growing the Django Software Foundation (DSF)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2024/Sep/27/themes-from-djangocon-us-2014/#could-we-fund-a-django-lts-accessibility-audit-"&gt;Could we fund a Django LTS accessibility audit?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2024/Sep/27/themes-from-djangocon-us-2014/#django-fellows-continue-to-provide-outstanding-value"&gt;Django fellows continue to provide outstanding value&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2024/Sep/27/themes-from-djangocon-us-2014/#django-needs-feature-champions"&gt;Django needs feature champions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2024/Sep/27/themes-from-djangocon-us-2014/#htmx-fits-django-really-well"&gt;htmx fits Django really well&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2024/Sep/27/themes-from-djangocon-us-2014/#django-ninja-has-positive-buzz"&gt;Django Ninja has positive buzz&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2024/Sep/27/themes-from-djangocon-us-2014/#valkey-as-a-last-minute-sponsor"&gt;Valkey as a last-minute sponsor&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2024/Sep/27/themes-from-djangocon-us-2014/#durham-has-a-world-class-collection-of-tubas"&gt;Durham has a world-class collection of tubas&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id="growing-the-django-software-foundation-dsf-"&gt;Growing the Django Software Foundation (DSF)&lt;/h4&gt;
&lt;p&gt;Jacob Kaplan-Moss gave &lt;a href="https://2024.djangocon.us/talks/if-we-had-1000000-what-could-the-dsf-do-with-4x-its-budget/"&gt;my favorite talk&lt;/a&gt; of the conference, asking what the Django Software Foundation could do if it quadrupled its annual income from $250,000 to $1 million dollars, and then mapping out a convincing path to get there.&lt;/p&gt;
&lt;p&gt;I really liked this diagram Jacob provided summarizing the foundation’s current income and expenditures. It’s pretty cool that $90,000 of annual income comes from individual donors, over a third of the total since corporate donors provide $160,000.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2024/dsf-diagram.jpg" alt="Financial breakdown diagram with the following numbers:  PLATINUM &amp;amp; GOLD: $125,000 CORPORATE DONORS: $160,000 BUDGET: $255,000 SILVER &amp;amp; BELOW: $35,000 INDIVIDUAL DONORS: $90,000  Spending:  WAGES (FELLOWS): $200,000 GRANTS: $35,000 OTHER: $5,000 FEES/HOSTING: $10,000 SURPLUS: $10,000​​​​​​​​​​​​​​​​" style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;Top priority would be hiring an Executive Director for the foundation, which is currently lead entirely by an elected, volunteer board. I’ve seen how useful a professional ED is from my own experiences &lt;a href="https://simonwillison.net/2024/Sep/18/board-of-the-python-software-foundation/"&gt;on the Python Software Foundation board&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Having someone working full time on the foundation outside of our current fellows - who have more than enough on their plates already - would enable the foundation to both take on more ambitious goals and also raise more money with which to tackle them.&lt;/p&gt;
&lt;p&gt;A line that Jacob used repeatedly in his talk about funding the foundation was this: if you or your organization &lt;em&gt;wouldn’t&lt;/em&gt; want to sponsor Django, he’d love to know why that is - understanding those blockers right now is almost as valuable as receiving actual cash. You can reach out to him at &lt;code&gt;jacob at djangoproject.com&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id="could-we-fund-a-django-lts-accessibility-audit-"&gt;Could we fund a Django LTS accessibility audit?&lt;/h4&gt;
&lt;p&gt;Django fellows and the &lt;a href="https://github.com/django/deps/blob/main/final/0011-accessibility-team.rst"&gt;Django Accessibility Team&lt;/a&gt; have been focusing significant effort on the accessibility of the Django admin. I found this very inspiring, and in combination with the talk of more funding for the foundation it put an idea in my head: what if every Django LTS release (once every two years) was backed by a full, professional accessibility audit, run by an agency staffed with developers who use screen readers?&lt;/p&gt;
&lt;p&gt;Imagine how much impact it would have if the default Django admin interface had excellent, documented accessibility out of the box. It could improve things for hundreds of thousands of users, and set an excellent precedent for projects (and foundations) in the wider open source community.&lt;/p&gt;
&lt;p&gt;This also feels to me like something that should be inherently attractive to sponsors. A lot of agencies use Django for government work, where accessibility is a requirement with teeth. Would one of those agencies like to be the “accessibility sponsor” for a major Django release?&lt;/p&gt;
&lt;h4 id="django-fellows-continue-to-provide-outstanding-value"&gt;Django fellows continue to provide outstanding value&lt;/h4&gt;
&lt;p&gt;The &lt;a href="https://www.djangoproject.com/fundraising/#fellowship-program"&gt;DSF’s fellowship program&lt;/a&gt; remains one of the most impactful initiatives I’ve seen anywhere for ensuring the ongoing sustainability of a community-driven open source project.&lt;/p&gt;
&lt;p&gt;Both of the current fellows, Natalia Bidart and Sarah Boyce, were in attendance and gave talks. It was great getting to meet them in person.&lt;/p&gt;
&lt;p&gt;If you’re not familiar with the program, the fellows are contractors who are paid by the DSF to keep the Django project ticking over - handling many of the somewhat less glamorous tasks of responsible open source maintenance such as ticket triage, release management, security fixes and code review.&lt;/p&gt;
&lt;p&gt;The fellows program is in its tenth year, and is a key reason that Django continues to release new versions &lt;a href="https://www.djangoproject.com/download/#supported-versions"&gt;on a regular schedule&lt;/a&gt; despite having no single corporate parent with paid developers.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2024/django-roadmap.png" alt="Software release timeline: 4.2 LTS (April 2023), 5.0 (August 2024), 5.1 (2025), 5.2 LTS (2026), 6.0 (2027), 6.1 (2027), 6.2 LTS (2028), 7.0 (2029). LTS versions have extended support periods." style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;Unsurprisingly there is always more work than fellow capacity, hence Jacob’s desire to further expand the existing program.&lt;/p&gt;
&lt;p&gt;The fellows program launched with a policy that fellows should not work on new feature development. I believe this was partly related to interpretation of IRS nonprofit guidelines which have since been reconsidered, and there is a growing consensus now that this policy should be dropped.&lt;/p&gt;
&lt;h4 id="django-needs-feature-champions"&gt;Django needs feature champions&lt;/h4&gt;
&lt;p&gt;Django has a well deserved reputation for stability, reliability and a dependable release process. It has less of a reputation for constantly turning out ground-breaking new features.&lt;/p&gt;
&lt;p&gt;Long-time Django contributors who I talked to all had a similar position on this: the challenge here is that big new features need dedicated champions to both lead design and development on them and to push them through to completion.&lt;/p&gt;
&lt;p&gt;The pool of community members who are both willing and able to take on these larger projects is currently too small.&lt;/p&gt;
&lt;p&gt;There are a number of ways we could address this - most notably through investing financial resources in sponsoring feature development. This has worked well for Django in the past - Django’s migrations work was funded by &lt;a href="https://www.kickstarter.com/projects/andrewgodwin/schema-migrations-for-django"&gt;a Kickstarter campaign&lt;/a&gt; back in 2013.&lt;/p&gt;
&lt;p&gt;The Django Software Foundation will shortly be announcing details of elections for both the DSF board and the Django Steering Council. These are extremely influential positions for people who want to help solve some of these larger problems.&lt;/p&gt;
&lt;h4 id="htmx-fits-django-really-well"&gt;htmx fits Django really well&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://htmx.org/"&gt;htmx&lt;/a&gt; is an incredibly good fit for the uncodified Django community philosophy of building for the web. It came up in multiple talks. It feels like it may be a solution that the Django community has been seeking for years, as a very compelling alternative to writing everything in SPA JavaScript and using Django purely as a backend via something like Django REST Framework.&lt;/p&gt;
&lt;p&gt;I've been slightly resistant to embracing htmx myself purely because it's such a critical dependency and in the past I wasn't convinced of its staying power. It's now mature, stable and widely-enough used that I'm ready to consider it for my own long-term projects.&lt;/p&gt;
&lt;h4 id="django-ninja-has-positive-buzz"&gt;Django Ninja has positive buzz&lt;/h4&gt;
&lt;p&gt;I haven’t paid much attention to &lt;a href="https://django-ninja.dev/"&gt;Django Ninja&lt;/a&gt; but it had a lot of very positive buzz at the conference as well, as a tool for quickly building full-featured, performative API endpoints (thanks to Rust-backed &lt;a href="https://docs.pydantic.dev/"&gt;Pydantic&lt;/a&gt; for serialization) with &lt;a href="https://django-ninja.dev/#interactive-api-docs"&gt;interactive API docs&lt;/a&gt; powered by OpenAPI.&lt;/p&gt;
&lt;p&gt;I respect Django REST Framework a lot, but my personal programming style leans away from Class Based Views, which it uses quite a bit. Django Ninja looks like it might fit my function-view biases better.&lt;/p&gt;
&lt;p&gt;I wrote about Richard Terry’s excellent &lt;a href="https://github.com/radiac/nanodjango"&gt;nanodjango&lt;/a&gt; single-file Django application tool &lt;a href="https://simonwillison.net/2024/Sep/24/nanodjango/"&gt;the other day&lt;/a&gt; - Django Ninja comes baked into that project as well.&lt;/p&gt;
&lt;h4 id="valkey-as-a-last-minute-sponsor"&gt;Valkey as a last-minute sponsor&lt;/h4&gt;
&lt;p&gt;The three platinum sponsors for DjangoCon this year were &lt;a href="https://www.revsys.com/"&gt;REVSYS&lt;/a&gt;, &lt;a href="https://www.caktusgroup.com/"&gt;Caktus Group&lt;/a&gt; and &lt;a href="https://valkey.io/"&gt;Valkey&lt;/a&gt;. Valkey were a late and somewhat surprising addition to the sponsorship lineup.&lt;/p&gt;
&lt;p&gt;Valkey is the &lt;a href="https://www.linuxfoundation.org/press/linux-foundation-launches-open-source-valkey-community"&gt;Linux Foundation backed&lt;/a&gt; fork of Redis, created in response to Redis &lt;a href="https://redis.io/blog/redis-adopts-dual-source-available-licensing/"&gt;ditching their Open Source license&lt;/a&gt; (which I took quite personally, having contributed my own free effort to promoting and improving Redis in the past).&lt;/p&gt;
&lt;p&gt;Aside from expressing thanks to them, I usually don’t pay sponsors that much attention. For some reason this one hit differently - the fact that Valkey were ready to step in as a major sponsor despite being only a few months old has caused me to take that project a whole lot more seriously than I did before. I’ll certainly consider them next time I come across a Redis-shaped problem.&lt;/p&gt;
&lt;h4 id="durham-has-a-world-class-collection-of-tubas"&gt;Durham has a world-class collection of tubas&lt;/h4&gt;
&lt;p&gt;My favorite category of &lt;a href="https://www.niche-museums.com/"&gt;Niche Museum&lt;/a&gt; is one that's available by appointment only where the person who collected everything is available to show you around.&lt;/p&gt;
&lt;p&gt;I always check &lt;a href="https://www.atlasobscura.com/"&gt;Atlas Obscura&lt;/a&gt; any time I visit a new city, and this time I was delighted to learn about The Vincent and Ethel Simonetti Historic Tuba Collection!&lt;/p&gt;
&lt;p&gt;I promoted it in the DjangoCon US #outings Slack channel and got together a group of five conference attendees for a visit on Thursday, shortly before my flight.&lt;/p&gt;
&lt;p&gt;It was &lt;em&gt;peak&lt;/em&gt; Niche Museum. I’ve posted photos and notes over &lt;a href="https://www.niche-museums.com/112"&gt;on my Niche Museums&lt;/a&gt; website, the first new article there in quite a while.&lt;/p&gt;

&lt;p&gt;&lt;img alt="More than a dozen varied and beautiful tubas, each with a neat attached label." src="https://static.simonwillison.net/static/2024/tuba-collection-card.jpeg" /&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/conferences"&gt;conferences&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/djangocon"&gt;djangocon&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jacob-kaplan-moss"&gt;jacob-kaplan-moss&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/redis"&gt;redis&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dsf"&gt;dsf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pydantic"&gt;pydantic&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/htmx"&gt;htmx&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="accessibility"/><category term="conferences"/><category term="django"/><category term="djangocon"/><category term="jacob-kaplan-moss"/><category term="python"/><category term="redis"/><category term="dsf"/><category term="pydantic"/><category term="htmx"/></entry><entry><title>My @covidsewage bot now includes useful alt text</title><link href="https://simonwillison.net/2024/Aug/25/covidsewage-alt-text/#atom-tag" rel="alternate"/><published>2024-08-25T16:09:49+00:00</published><updated>2024-08-25T16:09:49+00:00</updated><id>https://simonwillison.net/2024/Aug/25/covidsewage-alt-text/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://fedi.simonwillison.net/@covidsewage/113023397159658020"&gt;My @covidsewage bot now includes useful alt text&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I've been running a &lt;a href="https://fedi.simonwillison.net/@covidsewage"&gt;@covidsewage&lt;/a&gt; Mastodon bot for a while now, posting daily screenshots (taken with &lt;a href="https://shot-scraper.datasette.io/"&gt;shot-scraper&lt;/a&gt;) of the Santa Clara County &lt;a href="https://publichealth.santaclaracounty.gov/health-information/health-data/disease-data/covid-19/covid-19-wastewater"&gt;COVID in wastewater&lt;/a&gt; dashboard.&lt;/p&gt;
&lt;p&gt;Prior to today the screenshot was accompanied by the decidedly unhelpful alt text "Screenshot of the latest Covid charts".&lt;/p&gt;
&lt;p&gt;I finally fixed that today, closing &lt;a href="https://github.com/simonw/covidsewage-bot/issues/2"&gt;issue #2&lt;/a&gt; more than two years after I first opened it.&lt;/p&gt;
&lt;p&gt;The screenshot is of a Microsoft Power BI dashboard. I hoped I could scrape the key information out of it using JavaScript, but the weirdness of their DOM proved insurmountable.&lt;/p&gt;
&lt;p&gt;Instead, I'm using GPT-4o - specifically, this Python code (run using a &lt;code&gt;python -c&lt;/code&gt; block in the GitHub Actions YAML file):&lt;/p&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-s1"&gt;base64&lt;/span&gt;, &lt;span class="pl-s1"&gt;openai&lt;/span&gt;
&lt;span class="pl-s1"&gt;client&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;openai&lt;/span&gt;.&lt;span class="pl-v"&gt;OpenAI&lt;/span&gt;()
&lt;span class="pl-k"&gt;with&lt;/span&gt; &lt;span class="pl-en"&gt;open&lt;/span&gt;(&lt;span class="pl-s"&gt;'/tmp/covid.png'&lt;/span&gt;, &lt;span class="pl-s"&gt;'rb'&lt;/span&gt;) &lt;span class="pl-k"&gt;as&lt;/span&gt; &lt;span class="pl-s1"&gt;image_file&lt;/span&gt;:
    &lt;span class="pl-s1"&gt;encoded_image&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;base64&lt;/span&gt;.&lt;span class="pl-en"&gt;b64encode&lt;/span&gt;(&lt;span class="pl-s1"&gt;image_file&lt;/span&gt;.&lt;span class="pl-en"&gt;read&lt;/span&gt;()).&lt;span class="pl-en"&gt;decode&lt;/span&gt;(&lt;span class="pl-s"&gt;'utf-8'&lt;/span&gt;)
&lt;span class="pl-s1"&gt;messages&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; [
    {&lt;span class="pl-s"&gt;'role'&lt;/span&gt;: &lt;span class="pl-s"&gt;'system'&lt;/span&gt;,
     &lt;span class="pl-s"&gt;'content'&lt;/span&gt;: &lt;span class="pl-s"&gt;'Return the concentration levels in the sewersheds - single paragraph, no markdown'&lt;/span&gt;},
    {&lt;span class="pl-s"&gt;'role'&lt;/span&gt;: &lt;span class="pl-s"&gt;'user'&lt;/span&gt;, &lt;span class="pl-s"&gt;'content'&lt;/span&gt;: [
        {&lt;span class="pl-s"&gt;'type'&lt;/span&gt;: &lt;span class="pl-s"&gt;'image_url'&lt;/span&gt;, &lt;span class="pl-s"&gt;'image_url'&lt;/span&gt;: {
            &lt;span class="pl-s"&gt;'url'&lt;/span&gt;: &lt;span class="pl-s"&gt;'data:image/png;base64,'&lt;/span&gt; &lt;span class="pl-c1"&gt;+&lt;/span&gt; &lt;span class="pl-s1"&gt;encoded_image&lt;/span&gt;
        }}
    ]}
]
&lt;span class="pl-s1"&gt;completion&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;client&lt;/span&gt;.&lt;span class="pl-s1"&gt;chat&lt;/span&gt;.&lt;span class="pl-s1"&gt;completions&lt;/span&gt;.&lt;span class="pl-en"&gt;create&lt;/span&gt;(&lt;span class="pl-s1"&gt;model&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;'gpt-4o'&lt;/span&gt;, &lt;span class="pl-s1"&gt;messages&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-s1"&gt;messages&lt;/span&gt;)
&lt;span class="pl-en"&gt;print&lt;/span&gt;(&lt;span class="pl-s1"&gt;completion&lt;/span&gt;.&lt;span class="pl-s1"&gt;choices&lt;/span&gt;[&lt;span class="pl-c1"&gt;0&lt;/span&gt;].&lt;span class="pl-s1"&gt;message&lt;/span&gt;.&lt;span class="pl-s1"&gt;content&lt;/span&gt;)&lt;/pre&gt;

&lt;p&gt;I'm base64 encoding the screenshot and sending it with this system prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Return the concentration levels in the sewersheds - single paragraph, no markdown&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Given this input image:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of a Power BI dashboard showing information that is described below" src="https://static.simonwillison.net/static/2024/covid-power-bi.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Here's the text that comes back:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The concentration levels of SARS-CoV-2 in the sewersheds from collected samples are as follows: San Jose Sewershed has a high concentration, Palo Alto Sewershed has a high concentration, Sunnyvale Sewershed has a high concentration, and Gilroy Sewershed has a medium concentration.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The full implementation can be found in &lt;a href="https://github.com/simonw/covidsewage-bot/blob/main/.github/workflows/post.yml"&gt;the GitHub Actions workflow&lt;/a&gt;, which runs on a schedule at 7am Pacific time every day.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/accessibility"&gt;accessibility&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/alt-text"&gt;alt-text&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/covid19"&gt;covid19&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/shot-scraper"&gt;shot-scraper&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/openai"&gt;openai&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/gpt-4"&gt;gpt-4&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="alt-text"/><category term="projects"/><category term="ai"/><category term="covid19"/><category term="shot-scraper"/><category term="openai"/><category term="generative-ai"/><category term="gpt-4"/><category term="llms"/></entry><entry><title>Reckoning</title><link href="https://simonwillison.net/2024/Aug/18/reckoning/#atom-tag" rel="alternate"/><published>2024-08-18T16:37:41+00:00</published><updated>2024-08-18T16:37:41+00:00</updated><id>https://simonwillison.net/2024/Aug/18/reckoning/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://infrequently.org/series/reckoning/"&gt;Reckoning&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Alex Russell is a self-confessed &lt;a href="https://en.wikipedia.org/wiki/Cassandra"&gt;Cassandra&lt;/a&gt; - doomed to speak truth that the wider Web industry stubbornly ignores. With this latest series of posts he is &lt;em&gt;spitting fire&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The series is an "investigation into JavaScript-first frontend culture and how it broke US public services", in four parts.&lt;/p&gt;
&lt;p&gt;In &lt;a href="https://infrequently.org/2024/08/object-lesson/"&gt;Part 2 — Object Lesson&lt;/a&gt; Alex profiles &lt;a href="https://benefitscal.com/"&gt;BenefitsCal&lt;/a&gt;, the California state portal for accessing SNAP food benefits (aka "food stamps"). On a 9Mbps connection, as can be expected in rural parts of California with populations most likely to need these services, the site takes 29.5 seconds to become usefully interactive, fetching more than 20MB of JavaScript (which isn't even correctly compressed) for a giant SPA that incoroprates React, Vue, the AWS JavaScript SDK, six user-agent parsing libraries and &lt;a href="https://infrequently.org/2024/08/object-lesson/#fn-receipts-1"&gt;a whole lot more&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It doesn't have to be like this! &lt;a href="https://www.getcalfresh.org/"&gt;GetCalFresh.org&lt;/a&gt;, the Code for America alternative to BenefitsCal, becomes interactive after 4 seconds. Despite not being the "official" site it has driven nearly half of all signups for California benefits.&lt;/p&gt;
&lt;p&gt;The fundamental problem here is the Web industry's obsession with SPAs and JavaScript-first development - techniques that make sense for a tiny fraction of applications (Alex &lt;a href="https://infrequently.org/2024/08/caprock/"&gt;calls out&lt;/a&gt; document editors, chat and videoconferencing and maps, geospatial, and BI visualisations as apppropriate applications) but massively increase the cost and complexity for the vast majority of sites - especially sites primarily used on mobile and that shouldn't expect lengthy session times or multiple repeat visits.&lt;/p&gt;
&lt;p&gt;There's so much great, quotable content in here. Don't miss out on the footnotes, like &lt;a href="https://infrequently.org/2024/08/caprock/#fn-omerta-as-market-failure-3"&gt;this one&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The JavaScript community's omertà regarding the consistent failure of frontend frameworks to deliver reasonable results at acceptable cost is likely to be remembered as one of the most shameful aspects of frontend's lost decade.&lt;/p&gt;
&lt;p&gt;Had the risks been prominently signposted, dozens of teams I've worked with personally could have avoided months of painful remediation, and hundreds more sites I've traced could have avoided material revenue losses.&lt;/p&gt;
&lt;p&gt;Too many engineering leaders have found their teams beached and unproductive for no reason other than the JavaScript community's dedication to a marketing-over-results ethos of toxic positivity.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In &lt;a href="https://infrequently.org/2024/08/the-way-out/"&gt;Part 4 — The Way Out&lt;/a&gt; Alex recommends the &lt;a href="https://www.gov.uk/service-manual"&gt;gov.uk Service Manual&lt;/a&gt; as a guide for building civic Web services that avoid these traps, thanks to the policy described in their &lt;a href="https://www.gov.uk/service-manual/technology/using-progressive-enhancement"&gt;Building a resilient frontend using progressive enhancement&lt;/a&gt; document.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/accessibility"&gt;accessibility&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/alex-russell"&gt;alex-russell&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/government"&gt;government&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/html"&gt;html&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-performance"&gt;web-performance&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/gov-uk"&gt;gov-uk&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="alex-russell"/><category term="government"/><category term="html"/><category term="javascript"/><category term="progressive-enhancement"/><category term="web-performance"/><category term="gov-uk"/></entry><entry><title>Serving a billion web requests with boring code</title><link href="https://simonwillison.net/2024/Jun/28/boring-code/#atom-tag" rel="alternate"/><published>2024-06-28T16:22:45+00:00</published><updated>2024-06-28T16:22:45+00:00</updated><id>https://simonwillison.net/2024/Jun/28/boring-code/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://notes.billmill.org/blog/2024/06/Serving_a_billion_web_requests_with_boring_code.html"&gt;Serving a billion web requests with boring code&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Bill Mill provides a deep retrospective from his work helping build a relaunch of the &lt;a href="https://www.medicare.gov/plan-compare/"&gt;medicare.gov/plan-compare&lt;/a&gt; site.&lt;/p&gt;
&lt;p&gt;It's a fascinating case study of the &lt;a href="https://boringtechnology.club/"&gt;choose boring technology&lt;/a&gt; mantra put into action. The "boring" choices here were PostgreSQL, Go and React, all three of which are so widely used and understood at this point that you're very unlikely to stumble into surprises with them.&lt;/p&gt;
&lt;p&gt;Key goals for the site were accessibility, in terms of users, devices and performance. Despite best efforts:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The result fell prey after a few years to a common failure mode of react apps, and became quite heavy and loaded somewhat slowly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I've seen this pattern myself many times over, and I'd love to understand why. React itself isn't a particularly large dependency but somehow it always seems to lead to architectural bloat over time. Maybe that's more of an SPA thing than something that's specific to React.&lt;/p&gt;
&lt;p&gt;Loads of other interesting details in here. The ETL details - where brand new read-only RDS databases were spun up every morning after a four hour build process - are particularly notable.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://lobste.rs/s/icigm4/serving_billion_web_requests_with_boring"&gt;Lobste.rs&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/go"&gt;go&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/postgresql"&gt;postgresql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scaling"&gt;scaling&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/react"&gt;react&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/boring-technology"&gt;boring-technology&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="go"/><category term="postgresql"/><category term="scaling"/><category term="react"/><category term="boring-technology"/></entry><entry><title>Transcripts on Apple Podcasts</title><link href="https://simonwillison.net/2024/Jun/13/transcripts-on-apple-podcasts/#atom-tag" rel="alternate"/><published>2024-06-13T20:14:19+00:00</published><updated>2024-06-13T20:14:19+00:00</updated><id>https://simonwillison.net/2024/Jun/13/transcripts-on-apple-podcasts/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://podcasters.apple.com/support/5316-transcripts-on-apple-podcasts"&gt;Transcripts on Apple Podcasts&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I missed this when it launched &lt;a href="https://www.apple.com/newsroom/2024/03/apple-introduces-transcripts-for-apple-podcasts/"&gt;back in March&lt;/a&gt;: the Apple Podcasts app now features searchable transcripts, including the ability to tap on text and jump to that point in the audio.&lt;/p&gt;
&lt;p&gt;Confusingly, you can only tap to navigate using the view of the transcript that comes up when you hit the quote mark icon during playback - if you click the Transcript link from the episode listing page you get a static transcript without the navigation option.&lt;/p&gt;
&lt;p&gt;Transcripts are created automatically server-side by Apple, or podcast authors can upload their own edited transcript using Apple Podcasts Connect.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/simonw/status/1801316274959749225"&gt;A few people on Twitter told me about this&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/apple"&gt;apple&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/podcasts"&gt;podcasts&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/transcripts"&gt;transcripts&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="apple"/><category term="podcasts"/><category term="transcripts"/></entry><entry><title>Experimenting with local alt text generation in Firefox Nightly</title><link href="https://simonwillison.net/2024/Jun/2/experimenting-with-local-alt-text-generation-in-firefox-nightly/#atom-tag" rel="alternate"/><published>2024-06-02T13:12:44+00:00</published><updated>2024-06-02T13:12:44+00:00</updated><id>https://simonwillison.net/2024/Jun/2/experimenting-with-local-alt-text-generation-in-firefox-nightly/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://hacks.mozilla.org/2024/05/experimenting-with-local-alt-text-generation-in-firefox-nightly/"&gt;Experimenting with local alt text generation in Firefox Nightly&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The PDF editor in Firefox (confession: I did not know Firefox ships with a PDF editor) is getting an experimental feature that can help suggest alt text for images for the human editor to then adapt and improve on.&lt;/p&gt;
&lt;p&gt;This is a great application of AI, made all the more interesting here because Firefox will run a local model on-device for this, using a custom trained model they describe as "our 182M parameters model using a Distilled version of GPT-2 alongside a Vision Transformer (ViT) image encoder".&lt;/p&gt;
&lt;p&gt;The model uses WebAssembly with ONNX running in &lt;a href="https://huggingface.co/docs/transformers.js/en/index"&gt;Transfomers.js&lt;/a&gt;, and will be downloaded the first time the feature is put to use.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/mozhacks/status/1796774672639336804"&gt;@mozhacks&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/alt-text"&gt;alt-text&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firefox"&gt;firefox&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mozilla"&gt;mozilla&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pdf"&gt;pdf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webassembly"&gt;webassembly&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/transformers-js"&gt;transformers-js&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="alt-text"/><category term="firefox"/><category term="javascript"/><category term="mozilla"/><category term="pdf"/><category term="ai"/><category term="webassembly"/><category term="llms"/><category term="transformers-js"/></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>Guidepup</title><link href="https://simonwillison.net/2024/Mar/14/guidepup/#atom-tag" rel="alternate"/><published>2024-03-14T04:07:49+00:00</published><updated>2024-03-14T04:07:49+00:00</updated><id>https://simonwillison.net/2024/Mar/14/guidepup/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/guidepup/guidepup"&gt;Guidepup&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I’ve been hoping to find something like this for years. Guidepup is “a screen reader driver for test automation”—you can use it to automate both VoiceOver on macOS and NVDA on Windows, and it can both drive the screen reader for automated tests and even produce a video at the end of the test.&lt;/p&gt;

&lt;p&gt;Also available: @guidepup/playwright, providing integration with the Playwright browser automation testing framework.&lt;/p&gt;

&lt;p&gt;I’d love to see open source JavaScript libraries both use something like this for their testing and publish videos of the tests to demonstrate how they work in these common screen readers.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/accessibility"&gt;accessibility&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/screen-readers"&gt;screen-readers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/playwright"&gt;playwright&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="screen-readers"/><category term="playwright"/></entry><entry><title>Ice Cubes GPT-4 prompts</title><link href="https://simonwillison.net/2023/Dec/6/ice-cubes-gpt-4-prompts/#atom-tag" rel="alternate"/><published>2023-12-06T19:38:07+00:00</published><updated>2023-12-06T19:38:07+00:00</updated><id>https://simonwillison.net/2023/Dec/6/ice-cubes-gpt-4-prompts/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/Dimillian/IceCubesApp/blob/4f9e23296fa9c8abb812bc24f0f9a1ce0c86b28a/Packages/Network/Sources/Network/OpenAIClient.swift#L86-L101"&gt;Ice Cubes GPT-4 prompts&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The &lt;a href="https://github.com/Dimillian/IceCubesApp"&gt;Ice Cubes&lt;/a&gt; open source Mastodon app recently grew a very good "describe this image" feature to help people add alt text to their images. I had a dig around in their repo and it turns out they're using GPT-4 Vision for this (and regular GPT-4 for other features), passing the image with this prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;What’s in this image? Be brief, it's for image alt description on a social network. Don't write in the first person.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&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/alt-text"&gt;alt-text&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/prompt-engineering"&gt;prompt-engineering&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/mastodon"&gt;mastodon&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/gpt-4"&gt;gpt-4&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vision-llms"&gt;vision-llms&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="alt-text"/><category term="ai"/><category term="prompt-engineering"/><category term="generative-ai"/><category term="mastodon"/><category term="gpt-4"/><category term="llms"/><category term="vision-llms"/></entry><entry><title>New Default: Underlined Links for Improved Accessibility (GitHub Blog)</title><link href="https://simonwillison.net/2023/Oct/19/new-default-underlined-links-for-improved-accessibility-github-b/#atom-tag" rel="alternate"/><published>2023-10-19T16:19:30+00:00</published><updated>2023-10-19T16:19:30+00:00</updated><id>https://simonwillison.net/2023/Oct/19/new-default-underlined-links-for-improved-accessibility-github-b/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.blog/changelog/2023-10-18-new-default-underlined-links-for-improved-accessibility/"&gt;New Default: Underlined Links for Improved Accessibility (GitHub Blog)&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
“By default, links within text blocks on GitHub are now underlined. This ensures links are easily distinguishable from surrounding text.”


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/accessibility"&gt;accessibility&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/design"&gt;design&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/github"&gt;github&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="design"/><category term="github"/></entry><entry><title>You can stop using user-scalable=no and maximum-scale=1 in viewport meta tags now</title><link href="https://simonwillison.net/2023/Aug/4/you-can-stop-using-user-scalableno-and-maximum-scale1-in-viewpor/#atom-tag" rel="alternate"/><published>2023-08-04T23:41:55+00:00</published><updated>2023-08-04T23:41:55+00:00</updated><id>https://simonwillison.net/2023/Aug/4/you-can-stop-using-user-scalableno-and-maximum-scale1-in-viewpor/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://lukeplant.me.uk/blog/posts/you-can-stop-using-user-scalable-no-and-maximum-scale-1-in-viewport-meta-tags-now/"&gt;You can stop using user-scalable=no and maximum-scale=1 in viewport meta tags now&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Luke Plant points out that your meta viewport tag should stick to just “width=device-width, initial-scale=1” these days—the user-scalable=no and maximum-scale=1 attributes are no longer necessary, and have a negative impact on accessibility, especially for Android users.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/accessibility"&gt;accessibility&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/html"&gt;html&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/luke-plant"&gt;luke-plant&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mobile"&gt;mobile&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mobileweb"&gt;mobileweb&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="html"/><category term="luke-plant"/><category term="mobile"/><category term="mobileweb"/></entry><entry><title>The anatomy of visually-hidden</title><link href="https://simonwillison.net/2023/Feb/11/the-anatomy-of-visually-hidden/#atom-tag" rel="alternate"/><published>2023-02-11T00:37:28+00:00</published><updated>2023-02-11T00:37:28+00:00</updated><id>https://simonwillison.net/2023/Feb/11/the-anatomy-of-visually-hidden/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.tpgi.com/the-anatomy-of-visually-hidden/"&gt;The anatomy of visually-hidden&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
James Edwards provides a detailed breakdown of the current recommended CSS for hiding content while keeping it available for assistive technologies in the browser accessibility and render trees. Lots of accumulated tricks and screen reader special cases in this.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/bendmyers/status/1624148295609114665"&gt;Ben Myers&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/css"&gt;css&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/screen-readers"&gt;screen-readers&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="css"/><category term="screen-readers"/></entry><entry><title>It looks like I'm moving to Mastodon</title><link href="https://simonwillison.net/2022/Nov/5/mastodon/#atom-tag" rel="alternate"/><published>2022-11-05T05:32:20+00:00</published><updated>2022-11-05T05:32:20+00:00</updated><id>https://simonwillison.net/2022/Nov/5/mastodon/#atom-tag</id><summary type="html">
    &lt;p&gt;Elon Musk laid off about half of Twitter this morning. There are &lt;a href="https://twitter.com/gergelyorosz/status/1588517143490199552"&gt;many terrible stories&lt;/a&gt; emerging about how this went down, but one that particularly struck me was that he laid off &lt;a href="https://twitter.com/gerardkcohen/status/1588584461398347777"&gt;the entire accessibility team&lt;/a&gt;. For me this feels like a microcosm of the whole situation. Twitter's priorities are no longer even remotely aligned with my own.&lt;/p&gt;
&lt;p&gt;I've been using Twitter since November 2006 - wow, that's 16 years! I've accumulated &lt;a href="https://twitter.com/simonw"&gt;42,804 followers there&lt;/a&gt;. It's been really good to me, and I've invested a lot of work generating content there to feed the machine.&lt;/p&gt;
&lt;p&gt;I can't see myself putting the same work in to help the world's (current) richest man pay the billion dollar annual interest on the loans he took out to buy the place on a weird narcissistic whim.&lt;/p&gt;
&lt;p&gt;So I've started to explore &lt;a href="https://joinmastodon.org/"&gt;Mastodon&lt;/a&gt; - and so far it's exceeding all of my expectations.&lt;/p&gt;
&lt;p&gt;My new profile is at &lt;a href="https://fedi.simonwillison.net/@simon"&gt;https://fedi.simonwillison.net/@simon&lt;/a&gt; - you can follow &lt;code&gt;@simon@simonwillison.net&lt;/code&gt; in your Mastodon client of choice.&lt;/p&gt;
&lt;p&gt;Not ready to sign up for Mastodon? It &lt;a href="https://fedi.simonwillison.net/web/@rysiek@mstdn.social/109288881107985329"&gt;turns out&lt;/a&gt; RSS support is baked in too - you can subscribe to &lt;a href="https://fedi.simonwillison.net/@simon.rss"&gt;https://fedi.simonwillison.net/@simon.rss&lt;/a&gt; in your feed reader (I really like NetNewsWire for macOS and iOS these days).&lt;/p&gt;
&lt;h4&gt;Why Mastodon?&lt;/h4&gt;
&lt;p&gt;The lesson I have learned from Twitter is that, even if a service you trust makes it past an IPO and becomes a public company, there's always a risk that it can be bought by someone who very much doesn't share your values.&lt;/p&gt;
&lt;p&gt;Mastodon has been designed to avoid this from the start. It operates as a federated network of independent servers, each of which is run by a different person or organization with the ability to set their own rules and standards.&lt;/p&gt;
&lt;p&gt;You can also host your own instance on your own domain.&lt;/p&gt;
&lt;p&gt;My initial nudge to try this out was from Jacob and Andrew, who figured out how to do exactly that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://aeracode.org/2022/11/01/fediverse-custom-domains/"&gt;The Fediverse, And Custom Domains&lt;/a&gt; - Andrew Godwin&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jacobian.org/til/my-mastodon-instance/"&gt;Setting up a personal Fediverse ID / Mastodon instance&lt;/a&gt; - Jacob Kaplan-Moss&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Andrew and Jacob both opted to pay &lt;a href="https://masto.host/"&gt;masto.host&lt;/a&gt; to run their instance for them. I've decided to do the same. It's on my domain, which means if I ever want to run it myself I can do so without any visible disruption.&lt;/p&gt;
&lt;p&gt;I'm paying $9/month. I find it darkly amusing that this is a dollar more than Elon has been planning to charge for users to keep their verified status on Twitter!&lt;/p&gt;
&lt;p&gt;If you don't want to use your own domain there are plenty of &lt;a href="https://joinmastodon.org/servers"&gt;good free options&lt;/a&gt;, though I recommend reading Ash Furrow's &lt;a href="https://ashfurrow.com/blog/mastodon-technology-shutdown/"&gt;post about his shutdown of mastodon.technology&lt;/a&gt; to help understand how much of a commitment it is for the admins who run a free instance.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mastodon.ie/@klillington/109287983727726762"&gt;This post&lt;/a&gt; by &lt;code&gt;@klillington@mastodon.ie&lt;/code&gt; has some good links for getting started understanding the system. I particularly enjoyed &lt;a href="https://docs.google.com/document/d/1D9gfeKg_-hlsU66R-dLEvUeyMsqEfyIx2pnfUeX0t_E/edit#"&gt;Nikodemus’ Guide to Mastodon&lt;/a&gt; as it matched most closely the questions I had at first.&lt;/p&gt;
&lt;h4&gt;Initial impressions&lt;/h4&gt;
&lt;p&gt;Despite taking the second hardest route to joining Mastodon (the hardest route is &lt;a href="https://docs.joinmastodon.org/user/run-your-own/"&gt;spinning up a new server from scratch&lt;/a&gt;) it took me just less than an hour to get started. I wrote up &lt;a href="https://til.simonwillison.net/mastodon/custom-domain-mastodon"&gt;a TIL describing what I did&lt;/a&gt; - more or less directly following the steps described by Andrew and Jacob.&lt;/p&gt;
&lt;p&gt;I signed into my new account and started following people, by pasting in their full Mastodon names (mine is &lt;code&gt;@simon@simonwillison.net&lt;/code&gt;). I was initially surprised that this did nothing: your timeline won't be populated until the people you follow have said something.&lt;/p&gt;
&lt;p&gt;And then people started to toot, and my timeline slowly kicked into life.&lt;/p&gt;
&lt;p&gt;And it was really, really pleasant.&lt;/p&gt;
&lt;p&gt;My fear was that everyone on Mastodon would spend all of their time talking about Mastodon - especially given the current news. And sure, there's some of that. (I'm obviously guilty here.)&lt;/p&gt;
&lt;p&gt;But there's lots of stuff that isn't that. The 500 character limit gives people a bit more space, and replies work much like they do on Twitter. I followed a bunch of people, replied to a few things, posted some pelican photos and it all worked pretty much exactly as I hoped it would.&lt;/p&gt;
&lt;p&gt;It's also attracting very much the kind of people I want to hang out with. Mastodon is, unsurprisingly, entirely populated by nerds. But the variety of nerds is highly pleasing to me.&lt;/p&gt;
&lt;p&gt;I've been checking in on the &lt;code&gt;#introduction&lt;/code&gt; hashtag and I'm seeing artists, academics, writers, historians. It's not just programmers. The variety of interest areas on Twitter is the thing I'll miss most about it, so seeing that start to become true on Mastodon too is a huge relief.&lt;/p&gt;
&lt;p&gt;Considering how complicated a federated network is, the fact that it's this smooth to use is really impressive. It helps that they've had six years to iron out the wrinkles - the network seems to be coping with the massive influx of new users over the past few days really well.&lt;/p&gt;
&lt;p&gt;I'm also appreciating how much thought has been put into the design of the system. Quote tweeting isn't supported, for reasons explained by Eugen Rochko &lt;a href="https://blog.joinmastodon.org/2018/07/cage-the-mastodon/"&gt;in this 2018 post&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Another feature that has been requested almost since the start, and which I keep rejecting is &lt;strong&gt;quoting messages&lt;/strong&gt;. Coming back to my disclaimer, of course it’s impossible to prevent people from sharing screenshots or linking to public resources, but quoting messages is immediately actionable. It makes it a lot easier for people to immediately engage with the quoted content… and it usually doesn’t lead to anything good. When people use quotes to reply to other people, conversations become performative power plays. “Heed, my followers, how I dunk on this fool!” When you use the reply function, your message is broadcast only to people who happen to follow you both. It means one person’s follower count doesn’t play a massive role in the conversation. A quote, on the other hand, very often invites the followers to join in on the conversation, and whoever has got more of them ends up having the upper hand and massively stressing out the other person.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Mastodon so far feels much more chilled out than Twitter. I get the impression this is by design. When there's no profit motive to "maximize engagement" you can design features to optimize for a different set of goals.&lt;/p&gt;
&lt;h4&gt;And there's an API&lt;/h4&gt;
&lt;p&gt;Unsurprisingly, Mastodon has a powerful API. It's necessary for the system itself to work - those toots aren't going to federate themselves!&lt;/p&gt;
&lt;p&gt;Poking around with it is &lt;em&gt;really fun&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;First, a friendly note. &lt;a href="https://bsd.network/@pamela/109287805657081451"&gt;@pamela@bsd.network wrote the following&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hacky folks, please resist finding ways to scrape the fediverse, build archives, automate tools and connect to people via bot without their consent.&lt;/p&gt;
&lt;p&gt;[...]&lt;/p&gt;
&lt;p&gt;Whatever your thing is, make it 100% opt-in. Make it appropriate for a significantly more at-risk user than you are. Make sure it forgets things, purges info about servers it can't contact, can't operate in any sort of logged-in mode where consent is an issue.&lt;/p&gt;
&lt;p&gt;We will straight up help advertise your cool thing if it respects users properly and takes the time to consider the safety and preferences of every person involved. There are a lot of fun, thoughtfully-designed toys! And there are a lot of people really tired of having to come and tell you off when you wanted to help, honestly. Help yourself and ask around before you flip on your cool new thing, let folks point out what you're missing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;(Read &lt;a href="https://bsd.network/@pamela/109287805657081451"&gt;the whole thing&lt;/a&gt;, it's great.)&lt;/p&gt;
&lt;p&gt;So far I've done a couple of things.&lt;/p&gt;
&lt;p&gt;I built &lt;a href="https://github.com/simonw/scrape-fediverse"&gt;a Git scraper&lt;/a&gt; to track the list of peer instances that various servers have picked up. This feels like a reasonable piece of public information to track, and it's a fun way to get a feel for how the network is growing.&lt;/p&gt;
&lt;p&gt;I also figured out how to &lt;a href="https://til.simonwillison.net/mastodon/export-timeline-to-sqlite"&gt;Export a Mastodon timeline to SQLite&lt;/a&gt; using the &lt;a href="https://docs.joinmastodon.org/methods/timelines/"&gt;timelines API&lt;/a&gt; and my &lt;a href="https://github.com/simonw/paginate-json"&gt;paginate-json&lt;/a&gt; and &lt;a href="https://sqlite-utils.datasette.io/"&gt;sqlite-utils&lt;/a&gt; CLI tools, so I could explore it in Datasette.&lt;/p&gt;
&lt;p&gt;Running my own instance means I have no ethical qualms at all about hammering away at my own API endpoint as fast as I like!&lt;/p&gt;
&lt;p&gt;I like to follow a lot of different people, and I don't like to feel committed to reading everything that crosses my timeline - so I expect that the feature I'll miss most from Twitter will be the algorithmic timeline! This is very much not in the spirit of Mastodon, which is firmly committed to a reverse chronological sort order.&lt;/p&gt;
&lt;p&gt;But with access to the raw data I can start experimenting with alternative timeline solutions myself.&lt;/p&gt;
&lt;p&gt;I'm somewhat intrigued by the idea of iterating on my own algorithmic timeline, to try and keep the variety of content high while hopefully ensuring I'm most likely to catch the highlights (whatever that means.)&lt;/p&gt;
&lt;p&gt;Past experience building recommendation systems has taught me that one of the smartest seeming things you can do is pick the top 100 most interesting looking things based on very loose criteria and then apply &lt;code&gt;random.shuffle()&lt;/code&gt; to produce a final feed!&lt;/p&gt;
&lt;p&gt;I have a hunch that this is going to be a lot of fun.&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/twitter"&gt;twitter&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mastodon"&gt;mastodon&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/fediverse"&gt;fediverse&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="accessibility"/><category term="twitter"/><category term="mastodon"/><category term="fediverse"/></entry><entry><title>WebAIM guide to using iOS VoiceOver to evaluate web accessibility</title><link href="https://simonwillison.net/2022/Apr/24/voiceover-web-accessibility/#atom-tag" rel="alternate"/><published>2022-04-24T19:35:59+00:00</published><updated>2022-04-24T19:35:59+00:00</updated><id>https://simonwillison.net/2022/Apr/24/voiceover-web-accessibility/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://webaim.org/articles/voiceover/mobile"&gt;WebAIM guide to using iOS VoiceOver to evaluate web accessibility&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I asked for pointers on learning to use VoiceOver on my iPhone for accessibility testing today and Matt Hobbs pointed me to this tutorial from the WebAIM group at Utah State University.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/TheRealNooshu/status/1518309500037513216"&gt;@TheRealNooshu&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/screen-readers"&gt;screen-readers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ios"&gt;ios&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="screen-readers"/><category term="ios"/></entry><entry><title>Weeknotes: Distracted by Playwright</title><link href="https://simonwillison.net/2022/Mar/12/weeknotes-playwright/#atom-tag" rel="alternate"/><published>2022-03-12T00:30:26+00:00</published><updated>2022-03-12T00:30:26+00:00</updated><id>https://simonwillison.net/2022/Mar/12/weeknotes-playwright/#atom-tag</id><summary type="html">
    &lt;p&gt;My goal for this week was to unblock progress on Datasette by finally finishing the dash encoding implementation I described last week. I was getting close, and then I got very distracted by &lt;a href="https://playwright.dev/"&gt;Playwright&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Dash encoding v2&lt;/h4&gt;
&lt;p&gt;In &lt;a href="https://simonwillison.net/2022/Mar/5/dash-encoding/"&gt;Why I invented “dash encoding”, a new encoding scheme for URL paths&lt;/a&gt; I described a new mechanism I had invented for handling the gnarly problem of including table names with &lt;code&gt;/&lt;/code&gt; characters in the URL path on Datasette. The very short version: you can't use URL encoding in a path, because common proxies (including Apache and Nginx) will decode them before they get to your application.&lt;/p&gt;
&lt;p&gt;Thanks to feedback on that post I actually changed my design: I'm now using a variant of percent encoding that uses the &lt;code&gt;-&lt;/code&gt; instead of the &lt;code&gt;%&lt;/code&gt;. More &lt;a href="https://github.com/simonw/datasette/issues/1439#issuecomment-1059851259"&gt;details in the issue&lt;/a&gt; - and I'll write this up fully once I've finished landing the change.&lt;/p&gt;
&lt;h4&gt;shot-scraper and Playwright&lt;/h4&gt;
&lt;p&gt;I thoroughly &lt;a href="https://xkcd.com/356/"&gt;nerd-sniped&lt;/a&gt; myself with this one. I started investigating possibilities for automatically generating screeshots for documentation, and realized that &lt;a href="https://playwright.dev/"&gt;Playwright&lt;/a&gt; made this substantially easier than it has been in the past.&lt;/p&gt;
&lt;p&gt;The result was &lt;strong&gt;&lt;a href="https://simonwillison.net/2022/Mar/10/shot-scraper/"&gt;shot-scraper&lt;/a&gt;&lt;/strong&gt; - a new command-line utility for taking screenshots of web pages, or portions of web pages - and for running through a set of screenshots defined in a YAML file.&lt;/p&gt;
&lt;p&gt;I still can't quite believe how quickly this came together.&lt;/p&gt;
&lt;p&gt;Every now and then a tool comes along which adds a fundamental new set of capabilities to your toolbox, and can be multiplied against other tools to open up a huge range of possibilities.&lt;/p&gt;
&lt;p&gt;Playwright feels like one of those tools.&lt;/p&gt;
&lt;p&gt;A quick &lt;code&gt;pip install playwright&lt;/code&gt; is all it takes to start writing robust browser automation tools, using dedicated standalone headless instances of multiple browsers that are installed for you using &lt;code&gt;playwright install&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It's easy to run in CI - getting it working in GitHub Actions was trivial.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;shot-scraper&lt;/code&gt; is my first project built on Playwright, but there will definitely be more.&lt;/p&gt;
&lt;h4&gt;shot-scraper accessibility&lt;/h4&gt;
&lt;p&gt;I started &lt;a href="https://twitter.com/simonw/status/1502044953836503048"&gt;a Twitter conversation&lt;/a&gt; asking for ways to write automated tests that exercise screen readers - not just running audit rules, but actually simulating what happens when a screen reader user attempts to navigate through a specific flow within an application.&lt;/p&gt;
&lt;p&gt;The most interesting answer I had was &lt;a href="https://twitter.com/bmustillrose/status/1502066504401141767"&gt;from Ben Mustill-Rose&lt;/a&gt;, who built a system for automating tests against an Android screen reader while working on BBC iPlayer - &lt;a href="https://youtu.be/-vEHOiIggss?t=253"&gt;demo here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;@fardarter &lt;a href="https://twitter.com/fardarter/status/1502045993667280905"&gt;pointed me&lt;/a&gt; back to Playwright again, which turns out to have an &lt;a href="https://playwright.dev/python/docs/api/class-accessibility"&gt;Accessibility snapshot&lt;/a&gt; mechanism that can dump out the current state of the Chromium accessibility tree.&lt;/p&gt;
&lt;p&gt;I couldn't resist &lt;a href="https://github.com/simonw/shot-scraper/issues/22"&gt;adding that to shot-scraper&lt;/a&gt; - so now you can run the following to see the accessibility tree for a web page:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;~ % shot-scraper accessibility https://datasette.io
{
    "role": "WebArea",
    "name": "Datasette: An open source multi-tool for exploring and publishing data",
    "children": [
        {
            "role": "link",
            "name": "Uses"
        },
        {
            "role": "link",
            "name": "Documentation"
        },
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://gist.github.com/simonw/431e9075441463236850bab042b9d20d"&gt;Full output here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As a really fun bonus trick: since the output is JSON, you can pipe it into &lt;a href="https://sqlite-utils.datasette.io/en/stable/cli.html#inserting-json-data"&gt;sqlite-utils insert&lt;/a&gt; to get a SQLite database:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;shot-scraper accessibility https://datasette.io \
    | jq .children | sqlite-utils insert \
    /tmp/accessibility.db nodes - --alter
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then open it in &lt;a href="https://datasette.io/desktop"&gt;Datasette Desktop&lt;/a&gt; and start faceting by role and heading level!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2022/datasette-desktop-accessibility.jpg" alt="Datasette Desktop browsing the nodes table - it has text, link, heading, button and textbox roles and four different heading levels." style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;h4&gt;sqlite-utils documentation improvements&lt;/h4&gt;
&lt;p&gt;I complained on Twitter that the way type information was displayed in the Sphinx &lt;a href="https://sqlite-utils.datasette.io/en/stable/reference.html"&gt;sqlite-utils API reference documentation&lt;/a&gt; was ugly:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2022/docs-ugly.png" alt="Really long ugly type signatures" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;p&gt;Adam Johnson &lt;a href="https://twitter.com/AdamChainz/status/1502311047612575745"&gt;pointed me&lt;/a&gt; to the &lt;code&gt;autodoc_typehints = "description"&lt;/code&gt; option which fixes this. I spent a while tidying up the documentation to work better with this, mainly by adding a whole bunch of &lt;code&gt;:param name: description&lt;/code&gt; tags that I had previously omitted. That work happenen in &lt;a href="https://github.com/simonw/sqlite-utils/issues/413"&gt;this issue&lt;/a&gt;. I think it looks &lt;em&gt;much&lt;/em&gt; better now:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2022/docs-pretty.png" alt="Type signatures are much easier to read now, and there's a detailed list of parameters with descriptions." style="max-width:100%;" /&gt;&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/image-diff"&gt;image-diff&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/image-diff/releases/tag/0.2.1"&gt;0.2.1&lt;/a&gt; - (&lt;a href="https://github.com/simonw/image-diff/releases"&gt;3 releases total&lt;/a&gt;) - 2022-03-11
&lt;br /&gt;CLI tool for comparing images&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/sqlite-utils"&gt;sqlite-utils&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/sqlite-utils/releases/tag/3.25.1"&gt;3.25.1&lt;/a&gt; - (&lt;a href="https://github.com/simonw/sqlite-utils/releases"&gt;98 releases total&lt;/a&gt;) - 2022-03-11
&lt;br /&gt;Python CLI utility and library for manipulating SQLite databases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/shot-scraper"&gt;shot-scraper&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/shot-scraper/releases/tag/0.4"&gt;0.4&lt;/a&gt; - (&lt;a href="https://github.com/simonw/shot-scraper/releases"&gt;5 releases total&lt;/a&gt;) - 2022-03-10
&lt;br /&gt;Automated website screenshots using GitHub Actions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/django-sql-dashboard"&gt;django-sql-dashboard&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/django-sql-dashboard/releases/tag/1.0.2"&gt;1.0.2&lt;/a&gt; - (&lt;a href="https://github.com/simonw/django-sql-dashboard/releases"&gt;34 releases total&lt;/a&gt;) - 2022-03-08
&lt;br /&gt;Django app for building dashboards using raw SQL queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/geojson-to-sqlite"&gt;geojson-to-sqlite&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/geojson-to-sqlite/releases/tag/1.0"&gt;1.0&lt;/a&gt; - (&lt;a href="https://github.com/simonw/geojson-to-sqlite/releases"&gt;8 releases total&lt;/a&gt;) - 2022-03-04
&lt;br /&gt;CLI tool for converting GeoJSON files to SQLite (with SpatiaLite)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/xml-analyser"&gt;xml-analyser&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/xml-analyser/releases/tag/1.3"&gt;1.3&lt;/a&gt; - (&lt;a href="https://github.com/simonw/xml-analyser/releases"&gt;4 releases total&lt;/a&gt;) - 2022-03-01
&lt;br /&gt;Simple command line tool for quickly analysing the structure of an arbitrary XML file&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-dateutil"&gt;datasette-dateutil&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/datasette-dateutil/releases/tag/0.3"&gt;0.3&lt;/a&gt; - (&lt;a href="https://github.com/simonw/datasette-dateutil/releases"&gt;4 releases total&lt;/a&gt;) - 2022-03-01
&lt;br /&gt;dateutil functions for Datasette&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/datasette/crawling-datasette-with-datasette"&gt;Crawling Datasette with Datasette&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/homebrew/latest-sqlite"&gt;Running the latest SQLite in Datasette using Homebrew&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/macos/python-installer-macos"&gt;Installing Python on macOS with the official Python installer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/gis/natural-earth-in-spatialite-and-datasette"&gt;Natural Earth in SpatiaLite and Datasette&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/pytest/coverage-with-context"&gt;pytest coverage with context&lt;/a&gt;&lt;/li&gt;
&lt;/ul&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/documentation"&gt;documentation&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sphinx-docs"&gt;sphinx-docs&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/playwright"&gt;playwright&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/shot-scraper"&gt;shot-scraper&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="accessibility"/><category term="documentation"/><category term="datasette"/><category term="weeknotes"/><category term="sphinx-docs"/><category term="playwright"/><category term="shot-scraper"/></entry><entry><title>A Complete Guide To Accessible Front-End Components</title><link href="https://simonwillison.net/2021/Mar/23/a-complete-guide-to-accessible-front-end-components/#atom-tag" rel="alternate"/><published>2021-03-23T01:06:04+00:00</published><updated>2021-03-23T01:06:04+00:00</updated><id>https://simonwillison.net/2021/Mar/23/a-complete-guide-to-accessible-front-end-components/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.smashingmagazine.com/2021/03/complete-guide-accessible-front-end-components/"&gt;A Complete Guide To Accessible Front-End Components&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I’m so excited about this article: it brings together an absolute wealth of resources on accessible front-end components, including many existing component implementations that are accessible out of the box. Date pickers, autocomplete widgets, modals, menus—all sorts of things that I’ve been dragging my heels on implementing because I didn’t fully understand their accessibility implications.


    &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;/p&gt;



</summary><category term="accessibility"/><category term="javascript"/></entry><entry><title>Using the HTML lang attribute</title><link href="https://simonwillison.net/2019/Apr/18/using-html-lang-attribute/#atom-tag" rel="alternate"/><published>2019-04-18T21:09:31+00:00</published><updated>2019-04-18T21:09:31+00:00</updated><id>https://simonwillison.net/2019/Apr/18/using-html-lang-attribute/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://developer.paciellogroup.com/blog/2016/06/using-the-html-lang-attribute/"&gt;Using the HTML lang attribute&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
TIL the HTML lang attribute is used by screen readers to understand how to provide the correct accent and pronunciation.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/LittleKope/status/1118953008610652167"&gt;Lindsey Kopacz&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/html"&gt;html&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/l10n"&gt;l10n&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/screen-readers"&gt;screen-readers&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="html"/><category term="l10n"/><category term="screen-readers"/></entry><entry><title>Quoting Bruce Lawson</title><link href="https://simonwillison.net/2018/Dec/19/bruce-lawson/#atom-tag" rel="alternate"/><published>2018-12-19T13:07:14+00:00</published><updated>2018-12-19T13:07:14+00:00</updated><id>https://simonwillison.net/2018/Dec/19/bruce-lawson/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://www.brucelawson.co.uk/2018/the-practical-value-of-semantic-html/"&gt;&lt;p&gt;If you wrap your main content – that is, the stuff that isn’t navigation, logo and main header etc – in a &lt;main&gt; tag, a screen reader user can jump immediately to it using a keyboard shortcut. Imagine how useful that is – they don’t have to listen to all the content before it, or tab through it to get to the main meat of your page.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://www.brucelawson.co.uk/2018/the-practical-value-of-semantic-html/"&gt;Bruce Lawson&lt;/a&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/bruce-lawson"&gt;bruce-lawson&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/html"&gt;html&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="bruce-lawson"/><category term="html"/></entry><entry><title>How technology helped a blind athlete run free at the New York Marathon</title><link href="https://simonwillison.net/2017/Nov/6/how-technology-helped-a-blind-athlete-run-free/#atom-tag" rel="alternate"/><published>2017-11-06T16:58:57+00:00</published><updated>2017-11-06T16:58:57+00:00</updated><id>https://simonwillison.net/2017/Nov/6/how-technology-helped-a-blind-athlete-run-free/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.theverge.com/2017/11/6/16610728/2017-new-york-marathon-blind-runner-wearworks-wayband-simon-wheatcroft"&gt;How technology helped a blind athlete run free at the New York Marathon&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Fascinating piece on technology to help blind people better navigate the world—combing GPS and chest-mounted ultrasonic sonar.


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



</summary><category term="accessibility"/></entry><entry><title>Quoting Daniel Göransson</title><link href="https://simonwillison.net/2017/Nov/6/skip-titles/#atom-tag" rel="alternate"/><published>2017-11-06T16:56:59+00:00</published><updated>2017-11-06T16:56:59+00:00</updated><id>https://simonwillison.net/2017/Nov/6/skip-titles/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://axesslab.com/alt-texts/"&gt;&lt;p&gt;Skip the title text! Nobody uses them – they don’t work on touch screens and on desktop they require that the user hovers for a while over an image, which nobody does. Also, adding a title-text makes some screen readers both read the title-text and the alt-text, which becomes redundant.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://axesslab.com/alt-texts/"&gt;Daniel Göransson&lt;/a&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/alt-text"&gt;alt-text&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="alt-text"/></entry><entry><title>Alt-texts: The Ultimate Guide</title><link href="https://simonwillison.net/2017/Nov/6/alt-texts-the-ultimate-guide/#atom-tag" rel="alternate"/><published>2017-11-06T16:54:19+00:00</published><updated>2017-11-06T16:54:19+00:00</updated><id>https://simonwillison.net/2017/Nov/6/alt-texts-the-ultimate-guide/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://axesslab.com/alt-texts/"&gt;Alt-texts: The Ultimate Guide&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
By Daniel Göransson, a web developer with vision impairment who uses a screen reader. This is the best, most practical guide to writing image alt text I’ve seen. Just one of the neat tips contained within: consider ending your alt text in a period, so the screen user knows to pause.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/accessibility"&gt;accessibility&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/alt-text"&gt;alt-text&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="alt-text"/></entry><entry><title>My First Week with the iPhone</title><link href="https://simonwillison.net/2010/Oct/3/iphone/#atom-tag" rel="alternate"/><published>2010-10-03T12:20:00+00:00</published><updated>2010-10-03T12:20:00+00:00</updated><id>https://simonwillison.net/2010/Oct/3/iphone/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://behindthecurtain.us/2010/06/12/my-first-week-with-the-iphone/"&gt;My First Week with the iPhone&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
A blind user describes the experience of using VoiceOver on the iPhone, including the joy of discovering the Color Identifier app which speaks the names of colours picked up by the iPhone’s camera. “ I used color cues to find my pumpkin plants, by looking for the green among the brown and stone. I spent ten minutes looking at my pumpkin plants, with their leaves of green and lemon-ginger.”


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/accessibility"&gt;accessibility&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/iphone"&gt;iphone&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/recovered"&gt;recovered&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="iphone"/><category term="recovered"/></entry><entry><title>Accessibility and Degradation in Cappuccino</title><link href="https://simonwillison.net/2009/Feb/26/rossbouchercom/#atom-tag" rel="alternate"/><published>2009-02-26T21:39:33+00:00</published><updated>2009-02-26T21:39:33+00:00</updated><id>https://simonwillison.net/2009/Feb/26/rossbouchercom/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://rossboucher.com/2009/02/26/accessibility-degradation-in-cappuccino/"&gt;Accessibility and Degradation in Cappuccino&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Ross Boucher from 280 North responds to Drew McLellan.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/280north"&gt;280north&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/accessibility"&gt;accessibility&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cappuccino"&gt;cappuccino&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/drew-mclellan"&gt;drew-mclellan&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ross-boucher"&gt;ross-boucher&lt;/a&gt;&lt;/p&gt;



</summary><category term="280north"/><category term="accessibility"/><category term="cappuccino"/><category term="drew-mclellan"/><category term="javascript"/><category term="ross-boucher"/></entry><entry><title>The Cost of Accessibility</title><link href="https://simonwillison.net/2009/Feb/25/cost/#atom-tag" rel="alternate"/><published>2009-02-25T22:31:47+00:00</published><updated>2009-02-25T22:31:47+00:00</updated><id>https://simonwillison.net/2009/Feb/25/cost/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://allinthehead.com/retro/337/the-cost-of-accessibility"&gt;The Cost of Accessibility&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Drew McLellan comments on the seemingly inevitable march towards JavaScript dependent applications, and argues that JavaScript frameworks such as Cappuccino have a duty to integrate accessibility in to their core.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/accessibility"&gt;accessibility&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cappuccino"&gt;cappuccino&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/drew-mclellan"&gt;drew-mclellan&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="cappuccino"/><category term="drew-mclellan"/><category term="javascript"/></entry></feed>