DevIdiot!
Making Immersive LLM Chat Experiences More Accessible
I have had the privilege of helping to improve the accessibility of multiple immersive LLM chat experiences in the course of my career. Now I want to share this knowledge with you.Accessibility is a core part of a functional digital experience. As the LLM chat pattern continues to spread like wildfire across the web, we risk excluding people if these experiences are not built with accessibility as a core consideration. It is also worth re-mentioning what “exclusion” means here: It’s the loss of
LifeOS: Building and Shipping a Full-Stack App the DevOps Way
IntroductionI built LifeOS because I wanted one place for my goals, the projects that move them forward, and the tasks I need to do today. Most apps I tried did one of those well and ignored the other two.It started as a frontend app and it's now a React app having a FastAPI backend in one Docker container, with Postgres on Supabase, authentication through Firebase, and a GitHub Actions pipeline that tests everything and pushes the image to GitHub Container Registry. Render pulls that imag
Automating the AWS Console with an AI Agent: Everything Worked Except Four Save Buttons
What I didAs part of a server migration, I let an AI agent drive its built-in browser against the AWS Lightsail console and a payment provider's admin panel. The boundary between "safe to delegate" and "a human must click this" turned out to be much sharper than I expected, so here is a record of what worked, what got rejected, and why. What workedSurprisingly many operations went through end to end:Instance creation: platform tab → blueprint → plan radio button → name field → create
Add AI video generation to a Next.js app: route handlers, spend caps, polling and webhooks
Disclosure: I work on Revana, a pay-as-you-go API for image and video models. The code uses its SDK (npm install revana, MIT) and REST API, but the structure applies to any video API you put behind a web app.Putting a "Generate video" button in a Next.js app sounds like one fetch. In practice video generation has three properties that shape the whole design:It's slow. A 5-second clip takes one to several minutes. A route handler that waits for it will hit your platform's function time limit, and
Sixty-three of our pages are one 208-line component, and seven of the eight routes behind them are one-liners
Nakodo publishes 76 pages. 63 of them come out of one React component, ContentPage, which is 208 lines including three exported helpers. The routes that use it look like this, and seven of the eight are this short:export default async function Page({ params }: { params: Promise<{ slug: string }> }) { return <ContentPage entry={requireEntry("guide", (await params).slug)} />;}Guides, YouTube niches, Instagram niches, TikTok niches, audience pages, competitor comparisons and blog posts
Zooming a web editor without losing your place
Put your cursor over a small piece of text in an editor and zoom in. You should be able to keep looking at the same text.It's a small test, but it catches a surprising amount of unfinished zoom behavior. The percentage changes, the document gets bigger, and the detail you wanted to inspect slides somewhere else.I build Repceipt, a receipt editor that runs in the browser. Receipts have plenty of small text packed into a narrow space. Zooming in on a price or a line of text needs to keep that deta
Your Chevron Looks Fine. Your Button Has No Name.
A chevron can be perfectly aligned, beautifully drawn, and completely unhelpful when its button receives keyboard focus.You see a control that opens something. Assistive technology needs a name that explains what that control does.That gap is easy to miss in a screenshot review. It is also easy to repeat across an entire application when the button lives inside a reusable component.Before polishing another icon, inspect the name of the button around it. A real report, a familiar integration
Building Calculators That Show Their Own Formula (with Dynamic OG Images in Next.js)
Most ROI calculators end in a pitch. The number is a black box. I wanted the opposite: a calculator that shows the formula next to the result, so a reader can check the math, and a crawler can read a worked example without running the form.These are seven free planning calculators for service businesses. No signup. Nothing is saved unless someone emails themselves the estimate. They live at keencraft.tech/tools. Pure functions for the mathEach formula lives in a small function, separate fro
Our developer-only page is a prerendered 404 served from the edge cache, and the only part of it that shipped is its name
PubTrivia has a page I use for judging the bits of the product that are purely visual: the mascot, the celebration effects, the player screens and the host's big screen, all on one scrolling page with made-up data so I can compare them side by side without starting a quiz.It is not supposed to exist in production, and the way it does not exist is one line:export const metadata: Metadata = { title: 'Mascot and effects lab', robots: { index: false, follow: false },}export default function LabPag
There is not one pixel size on our big screen: 128 container-query values, and the same component is a card on our homepage
PubTrivia has a screen that goes on a pub television. The host runs the quiz from a laptop, mirrors or screen-shares it to whatever is on the wall, and the room reads the question off it from twenty feet away. The same component also appears, scaled down to about 300 pixels wide, inside the homepage: the demo there is not a screenshot or a video, it is the real host view with a made-up round in it.One component, two sizes two orders of magnitude apart, and no media queries. The trick is that the
Our break-even number is a loop over sixty integers, because the line it sits on has a kink in it
PubTrivia has a handful of free tools that run entirely in the browser, and the newest one is a quiz night profit calculator: a landlord types in their room, their entry fee and their margin, and it says what a weekly quiz is worth per night, per year, and how many teams it needs before it stops losing money.It is 114 lines of arithmetic in one module, 259 lines of component around it, and 8 tests. No API route, no server action, nothing stored. The interesting part is not the code volume, it is
Normalize Parcel Dimensions Before They Reach a Shipping Adapter
A dimension of 21.5 is incomplete input for a shipping adapter until its unit is known. Sending centimeters to a field interpreted as millimeters changes the parcel description even though the numeric value passes a basic positive-number check.Carry the unit with the measurement at intake, then convert at a defined boundary. The example below uses millimeters as its internal representation. It deliberately accepts only dimensions that convert to positive whole millimeters; another system may nee
AI crawlers don't run your JavaScript
This note has an interactive demo. Try it in the originalIf your page builds its content with JavaScript in the browser, the crawlers behind ChatGPT, Claude and Perplexity see an empty shell. They download the HTML, they may even download your bundles, but they don't execute them. Googlebot does run JavaScript, which is why this goes unnoticed for so long: your page can rank fine in Google and still doesn't exist for a big part of AI search. The demo above puts both readers side by side, a real
One database query that fixed my race condition
Connuit is an app I recently built. Pronounced in a french way, kind of the same way the french say "bonne nuit". Connuit automates repetitive tasks as workflows, and lets AI make decisions inside them. Said AI runs on a free tier with a daily limit. This seems safe; however, the underlying issue here is that 20 requests could hit the limit at the same time.The AI used here is Gemini, and in order to implement an app-wide cap on the usage limit that won't exceed gemini's 500 hits per day limit,
Building a Free Javanese Script (Aksara Jawa) Converter in Vanilla JavaScript javascript, webdev, i18n, beginners
A few months ago I set out to build something small but surprisingly deep: a converter that turns Latin text into Aksara Jawa — the traditional script of the Javanese language, spoken by ~80 million people in Indonesia. What looked like a simple "map letters to symbols" task turned into a fun lesson in writing systems. Here's what I learned.You can try the finished tool here: aksarajava.id. It's not an alphabet — it's an abugidaMy first mistake was assuming Javanese script works like the La
Spot the bug: one character breaks this JSON
Quick puzzle đź§©The JSON in the image looks fine, but JSON.parse() rejects it, and so will most APIs.One character is the problem. Which line is it on?Hint: it's a habit JavaScript allows and JSON doesn't.Comment your line number. We'll post the answer this evening.When you hit "Unexpected token" for real, paste the JSON into a validator before you hunt through a 500-line file: https://seopeck.com/tool/json-validator
Retries and backoff FAQ: 11 questions about retrying failed jobs without making things worse
Retries look like the easy part of background jobs. Something failed? Try again. But badly designed retries turn a small outage into a big one, hide real bugs, and occasionally charge a customer twice.Here are the questions that come up most, with short answers and code. 1. Why not just retry immediately?Because whatever caused the failure is probably still happening. If an API is overloaded and returns errors, every client retrying instantly makes it more overloaded. You get a retry storm:
JavaScript Anti-Debugging and Tamper Detection
Reading code is only half of what attackers do. The other half is running it under a debugger and changing it: skipping a license check, freezing a timer, or hooking a function to steal its output. Anti-debugging and tamper detection try to make that harder. This guide covers the common techniques, why most of them fail on their own, and how to use them so they actually raise the cost. A realistic goalNo client-side check can stop a determined person who controls the browser. They can patch
I tilted a scanned page 1 , 3 and 5 : Tesseract shrugged, a browser OCR scrambled the lines
![ ](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/wShould a deskew step come before OCR at all? I wanted to know what a small tilt really costs, so I rotated two clean public-domain scans by 1°, 3° and 5° in code and ran three engines over them. Short answer: on the English page Tesseract went from 98.9% to 96.2% at 5°, the built-in text recognition in macOS barely reacted at all, and a browser-based PP-OCR kept almost every letter but lost the line order from 3° on. On the
OCR said done and returned half the page, so count lines before trusting it
I keep circling the idea of adding "upload a scanned PDF, get the text back" to one of my small tool sites, and the failure that worries me is not a crash. A crash shows up in the logs. What worries me is an OCR step that finishes cleanly and hands back text with a chunk of the page missing. On 2026-11-04 I ran a small test that produced exactly that: a scanned Chinese page tilted by 5° went through a browser-side OCR engine, roughly half of its 14 lines came back, and the status still said proc