尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证A browser button that turns a recipe page into big, step-by-step instructions you can read from across the kitchen
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
A free browser button that turns a recipe page into big, step-by-step instructions you can read from across the kitchen.
Live: https://kitchen-mode.com · Try it on a sample recipe: https://kitchen-mode.com/#try
Click the bookmark on a recipe page and you get:查看完整 README ↗A free browser button that turns a recipe page into big, step-by-step instructions you can read from across the kitchen.
Live: https://kitchen-mode.com · Try it on a sample recipe: https://kitchen-mode.com/#try
Click the bookmark on a recipe page and you get:
It runs in the browser. The bookmark loads the code from this site each time it's clicked, on computers and phones alike, so everyone has the latest version. Each time it opens it sends one anonymous count: the site's name, whether it found a recipe, and which build it is. Never the page address, the recipe or anything about you (see Usage counts).
Recipe sites publish a structured copy of each recipe (schema.org Recipe JSON-LD) so Google can show rich results. Kitchen Mode reads that instead of the page layout, which is why ads, pop-ups and long introductions never get in the way, and why it works on any site that publishes the data.
km.js, so it makes no request and needs no permission, and the pieces are booked on the audio clock with the beeps. A step past 30, or a browser that can't decode the MP3, gets the browser's own speech synthesis instead. The landing page's "Try it" always uses the browser's voice.CloseWatcher, Chrome only).| File | What it is |
|---|---|
kitchen-mode.js | Readable source of the bookmarklet. Edit this one. |
build.mjs | Strips comments from the source and writes it to km.js, then builds index.html: the landing page with the bookmark (loader, a short javascript: URL that loads km.js) and the sample recipe, which "Try it" runs from a copy of the code inside the page. HANDLE, SITE_URL and POSTHOG_KEY are set at the top. node build.mjs --check writes nothing and fails if the committed files don't match the source. |
sites.mjs | The recipe sites checked to work (tested), the 12 featured on the landing page and the 4 named in the hero. |
km.js | Generated. The same code as a file, loaded by the bookmark on every click, on computers and phones. Don't edit by hand. |
vercel.json | Forwards /relay/* to PostHog's EU servers, so ad blockers that block posthog.com don't drop the counts. Skips the install step, so package.json doesn't turn deploys into builds. |
tests/ | Playwright tests, run offline against the built km.js and index.html. serve.js answers the site's requests from this checkout, and is shared with the scripts. |
scripts/shots.mjs | Screenshots of the landing page and cook view into shots/ (not committed), and og.png. |
voice/alice.mp3, voice/alice.json | Alice's recorded pieces in one MP3, and where each piece starts. build.mjs puts both inside km.js. |
scripts/voice.mjs | Builds voice/ from the pieces made by scripts/voice.py in the kitchen-mode-video repo (Kokoro-82M, voice bf_alice, run locally). Needs ffmpeg. |
scripts/check-sites.mjs | Runs this checkout's km.js on real recipe pages and reports what it found. |
package.json | Dev tooling only: Playwright, pinned. No script, since Vercel would run it. |
npm install && npx playwright install chromium # once
node build.mjs # rebuild index.html and km.js after changing kitchen-mode.js or build.mjs
npm test # the committed build matches the source, then the Playwright tests (offline, about 20 s)
npm run shots # screenshots of the landing page and cook view, phone and desktop, light and dark, in shots/
open index.html # "Try it on a sample recipe" runs the current code; index.html#test is the test recipe
The tests run the real loader bookmark, read out of the built index.html, on made-up recipe pages in headless Chrome, with the site's requests answered from this checkout. Timers run on Playwright's fake clock, so a test can run one to the end and see the screen turn red. There are no retries: a test that only passes sometimes is a bug.
node scripts/check-sites.mjs runs this checkout's km.js on every page in sites.mjs (or on the URLs given, and --before prints each "Before you start" list). It needs the internet, answers the counts itself so nothing reaches PostHog, and takes about two minutes.
Regenerate the preview image after changing og-image.html (it refuses if the web fonts didn't load):
npm run shots -- og
Test on Android in the emulator (a Pixel 8 with Android 17 and Chrome 145, from the Play Store image, so it's the real Chrome for Android):
~/Library/Android/sdk/emulator/emulator -avd kitchen_pixel & # boots in about 30 s; close the window to stop it
adb reverse tcp:8765 tcp:8765 && python3 -m http.server 8765 # its Chrome can then open http://localhost:8765
chrome://inspect in Chrome on the Mac attaches DevTools to the emulator's tabs. adb is in ~/Library/Android/sdk/platform-tools. It was installed with Homebrew (openjdk@21, android-commandlinetools), then sdkmanager "platform-tools" "emulator" "system-images;android-37.0;google_apis_playstore;arm64-v8a" and avdmanager create avd -n kitchen_pixel -d pixel_8. It isn't signed in to Google, so Chrome doesn't update and the Play Store can't install other browsers.
Deploy by pushing main: Vercel's Git integration publishes it to production in about 30 seconds (static files, no build step on Vercel). Run npm test and commit index.html and km.js first, since Vercel serves them as committed. The Test workflow runs the same checks on every pull request; with main protected to require it, a red build can't reach anyone. Every bookmark loads km.js from the live site on each open, so a push reaches everyone at once (except computers set up before 30 Sep 2026, whose bookmarks hold the old full code). Check it landed:
git push origin main
vercel ls # newest row: Production, Ready
diff <(curl -s "https://kitchen-mode.com/km.js?v=$(date +%s)") km.js && echo live
CLAUDE.md holds the rules Claude can't work out from the code. Hooks in .claude/settings.json enforce the ones that matter most:
km.js or index.html by hand is refused, with a pointer to the source. Editing kitchen-mode.js or build.mjs rebuilds both, and a build that wouldn't parse is reported straight away.npm install when a session starts, so the tests work from the first prompt.Two skills: /ship goes through the checks, pushes and checks the live copy; only you can start it. /check-sites re-runs the site check and updates the lists.
Two free tools, neither using cookies:
/relay/ on this site. Each name starts with kitchen_mode_.| Event | Sent when | Properties |
|---|---|---|
page_viewed | the landing page loads | from: the referring site |
tried | "Try it" is pressed, or the page opens at #try | |
button_clicked | someone clicks the install button instead of dragging it | |
drag_started | the install button is picked up | |
drag_ended | it's let go | effect: none if the drag was cancelled; anything else means it landed, most likely on the bookmarks bar |
code_copied | "Copy the code" worked (phone setup) | |
test_opened | the test recipe (#test) opens, from the last setup step | |
test_passed | a bookmark opened Kitchen Mode on the test recipe ("Try it" doesn't count) | loader: whether the bookmark loaded km.js (false only for a computer bookmark set up before 30 Sep 2026); touch: whether it's a phone or tablet |
opened | the bookmarklet opens, on any site | site, found (whether it found a recipe), build (build date), loader (whether a bookmark loaded it from km.js, rather than an old full-code bookmark or "Try it"), touch (whether it's a phone or tablet: (pointer: coarse), the test the landing page uses to pick phone or computer steps) |
Until 29 Sep 2026 only phones had the loader bookmark, so loader alone meant a phone. Since then computers have it too: tell them apart with touch, which events from before then don't have (read loader: true there as a phone).
The landing page events share one random ID per visit, so they work as a funnel. opened gets a new random ID every time, so it counts opens, not people.
Limits: copies installed before 27 Sep 2026 never report, since full-code bookmarks don't update. Computer bookmarks set up from then until 30 Sep 2026 report with their own build, loader: false and no touch, and still to the old shared PostHog project (see Decisions); bookmarks set up since load km.js, so they always run the current build. Sites whose security policy blocks outside requests drop the count silently. opened on kitchen-mode.com (or kitchen-mode.vercel.app, which serves the same page) is "Try it". Nothing is sent when the page is opened from a file.
Checked on 27 Sep 2026 by loading a recipe page and running the same data lookup the bookmarklet uses. Sites that turn away plain requests were loaded in headless Chrome instead. node scripts/check-sites.mjs now does the whole check, running the bookmarklet itself. Run again on 9 Oct 2026 before shipping the new sentence and timer code: 70 of the 73 opened a recipe. delicious.com.au and taste.com.au turned the check away with a 403, and Waitrose with a connection error, so those three are unknown for now rather than broken.
tested in sites.mjs. The 12 names in featured show on the landing page; the rest sit behind "N more sites".Android setup says to open Chrome first and check the bookmark name (9 Oct 2026). Two phone setup attempts on 4 and 5 Oct showed that the bookmark never ran; the counts recorded the attempts but no execution. The steps now put the Custom Tab escape hatch before the numbered instructions, warn when the page sees Android's WebView marker, ask people to check kitchenmode has no space, and tell them to edit the name if the address-bar list has no star. What it costs: one warning on the setup page for in-app browsers and a little more text in the Android steps; the bookmark and privacy promise do not change.
The Safari parse error is fixed (9 Oct 2026). Two lookbehind patterns stopped the whole bookmark file parsing in older Safari, before its load error handler could help. Rewrote sentence splitting and timer boundaries without lookbehind; the existing behaviour tests remain the check. Tried only in headless Chrome. The cost is a little extra boundary handling in the timer parser.
A quick second bookmark click counts as the same tap (9 Oct 2026). It used to open and close Kitchen Mode at once. The loader now ignores a second run during the opening moment; a later click still closes it. Tried a short delay and kept it at 1.5 seconds, since an intentional close that soon is unlikely. The cost is that closing within 1.5 seconds requires waiting or using Esc.
A bookmarklet rather than an extension. No install, store fee or review, and nothing leaves the page but an anonymous count. The costs: it's desktop-first, phone setup means pasting code into a bookmark by hand, and a site's security policy can refuse the script the bookmark loads. Every bookmark runs the live km.js, so test before pushing.
Every bookmark is the short loader, computers included (29 Sep 2026). Phones got it first, on 27 Sep: the first phone users couldn't get the 34 KB bookmark to run (8 "Copy the code" presses and no phone opens), so they paste a 224-character bookmark that loads km.js from this site. It's short enough to check by eye, always runs the current build, and if the script can't load it says "Kitchen Mode could not load on this page." instead of doing nothing. Computers kept the full-code drag at first, because it runs even on sites whose security policy only allows scripts from listed hosts. They now drag the same loader, because a full-code bookmark never updates: everything shipped since, like "Before you start" and glance mode, reached phone users only. The drag, the ticket and the bookmark's name are unchanged. The cost, now on computers too: each open fetches km.js from this site (Vercel's default max-age=0, must-revalidate, so a quick revalidation), and a site with that kind of policy shows the message instead of opening. None of the 50 listed sites that answered a plain request has one, and BBC Food, whose policy is strict, works. Computer bookmarks set up earlier keep the old code and can't be changed from here. With about a dozen opens so far, most of them likely the author's own, the landing page doesn't ask anyone to set it up again. The landing page also shrank from 216 KB to 117 KB, since it no longer carries the whole code twice as a URL.
km.js and opens the test recipe, "It works" shows for a bookmark but not for "Try it", tapping again closes it, and a failed load shows its message. Checked the same day on a real Android phone (Chrome): typing kitchenmode in the address bar and tapping the suggestion with the star opens the test recipe, and tapping the bookmark in the bookmarks menu does nothing. Checked on 28 Sep 2026 in the Android emulator (Pixel 8, Android 17, Chrome 145) by following the Android steps as written: the message after bookmarking said "Bookmark saved", with no Edit button, and went after a few seconds (tapping it opens the editor); pressing and holding the URL highlighted all of it, with no Select all; kitchenmode: put the star third and opened the test recipe; the bookmarks list still did nothing. The back gesture was checked on BBC Good Food with the new km.js run through DevTools: it closes Kitchen Mode, and the next back leaves the page as normal; Esc and the ✕ also leave back working normally. Not checked: anything on iOS; Samsung Internet and Firefox on Android (the emulator needs a Google account to install them); in-app browsers like Instagram's.index.html: on the test recipe it loads km.js, opens, turns the banner to "It works" and sends test_passed with loader and touch: false (true with touch emulation), and a second run closes it; "Try it" still doesn't pass the test; on BBC Good Food and BBC Food it opens the recipe with both the live and the new km.js; when km.js can't load (a failed request, or a page whose policy allows only its own scripts) it shows "Kitchen Mode could not load on this page." BBC Food's own policy turns off the screen wake lock, so the view there says "Screen may sleep".share_target in its manifest, it would appear in Android's share sheet, including in apps where a bookmark can't run, and setup would be one tap. The share sheet only passes the link, so the server has to fetch the page, which breaks the "never the page address" promise. On 28 Sep 2026 a plain fetch from a home connection found the recipe on 58 of the 73 tested sites at best: 54 with an honest KitchenMode/1.0 user agent, and 48 posing as Chrome, since Cloudflare challenged the fake Chrome on 10 sites that let the honest one through. Allrecipes and the other 6 Dotdash Meredith sites answer any non-browser with a 402; Cloudflare, rate limits or a captcha stopped 7 more, and Joshua Weissman's fetched page has no steps in its data. A server's datacenter address would likely do worse, so the bookmark would stay as the fallback. Not yet measured from Vercel.MIT (see LICENSE). You can copy it, change it and share it, as long as the copyright notice goes with it. That includes Alice's voice in voice/.
If you fork it:
HANDLE, SITE_URL and POSTHOG_KEY at the top of build.mjs. Otherwise your bookmarks load this site's code, your counts come here, and your page credits the wrong person.Made by @richyjudge.
buildCLAUDE.md, .claude/ | Instructions, hooks and skills for Claude Code (see Working with Claude Code). |
.github/workflows/test.yml | Runs npm test on pull requests and on main. |
index.html | The generated landing page. Don't edit by hand. |
og.png | Link preview image (1200×630) used by X and others. |
og-image.html | Source for og.png. |
android-address-bar.png, android-address-bar-dark.png | Chrome's address bar with kitchenmode: typed, shown in the last Android setup step (light and dark). Taken on the Android emulator, cropped to 720×400. |
Setup ends with a test. The last setup step, on phones and computers, opens the test recipe (#test), which puts the sample recipe on the page and shows a banner saying what to do, then "It works" once a bookmark opens it. People find out at once, with the steps still in front of them. On a computer the page also offers the test after every drag, since it can't tell a ticket that landed on the bar from a cancelled drag (see drag_ended). Android steps name the bookmark kitchenmode so it's the only address-bar match: the landing page's title is also "Kitchen Mode", and the history entry for it outranks the bookmark. Chrome on Android does nothing when a code bookmark is tapped in the bookmarks list and only runs it from an address-bar suggestion, so the last Android step and the test banner say so. The first real Android test, on 27 Sep 2026, tried the bookmarks menu first. Touch screens (including tablets, whose Chrome sends a desktop user agent) get only the phone steps, since links can't be dragged to a bookmarks bar there.
Android users type kitchenmode:, with a colon. Chrome on Android lists Google's search suggestions first and puts bookmarks after as many of them as fit on screen. With plain kitchenmode, the star came ninth, half hidden behind the keyboard, under "kitchen modern" and the like (Chrome 145 on the emulator, 28 Sep 2026). Chrome doesn't send address-like input (word:) to Google for suggestions, so with the colon the list is just the typed text twice, then the star, third. Other names didn't help: kmode, kmkm, cookmode, zzkm and others still drew about eight suggestions each, and those vary by country; kitchenmode/ left six rows. If Chrome ever does suggest searches for it, the star is still in the list, just lower. The step shows a screenshot of that list, light and dark, with the row to tap ringed.
Android's back gesture closes Kitchen Mode. Back used to leave the recipe page, which is easy to do by accident when swiping from the screen edge to go back a step. CloseWatcher (Chrome 120 and up) exists for this and leaves the site's history alone. history.pushState would work in more browsers, but recipe sites that route with the History API could react to it. Browsers without CloseWatcher behave as before.
Pages opened from other apps need Chrome itself. Links opened from another app often open in a Custom Tab. It has the bookmark star, but tapping its address bar shows site info instead of letting you type, so Kitchen Mode can't be opened there. The Android steps say to tap ⋮, then Open in Chrome browser, and that the same goes for recipes.
Timers outlive the view. Closing Kitchen Mode (✕, Esc, the bookmark again, or Android's back gesture, which is easy to trigger by accident) used to end every running timer without a word. Now the timers move to the bottom-right corner of the recipe page and ring there. Tapping one goes back to the step you were on, and stopping the last one closes Kitchen Mode for good; the screen stays on until then, since a timer can't ring on a sleeping phone. Esc stops a ringing timer before it closes anything, since that's the key people reach for. Each time in the text runs at most one timer, and stays highlighted while it runs.
"Before you start" uses word lists, not an AI model (29 Sep 2026). A model would mean sending the recipe off the page, which the privacy promise rules out, and a server and a bill besides. The lists answer instantly and work offline. Run against the tested recipes that loaded (42), it listed 18 things on 14 of them, among them "4 cup cooked rice", "leftover slow-cooked lamb", pie crusts to soften and butter at room temperature; the other 28 show nothing. The false alarms it found (toasted sesame oil, a freezer note, the same step twice) were fixed. A code review the same day found three more with made-up recipes and fixed them: toasted sesame oil named in a step, a long bake "until risen" counted as a wait, and a step's item cut to one word ("the cooked basmati" for basmati rice). None of them occurred in the real recipes, whose 49 lists came out the same after the fix. The sample recipe serves the stew with "crusty bread or cooked rice", so "Try it" shows the list. A decision model such as TypeSafe's Jev could grade the lists against those recipes once, for well under a dollar, without changing the product.
Glance mode keeps the sentence on screen, and the next one. It takes over only after 10 seconds untouched while a timer runs. The current sentence stays underneath the countdown, and the next one sits below it in grey, with its step number when it starts a new step: with the countdown filling the screen, it was easy to forget what to do while waiting. A tap brings the full view back and never moves on, so a tap from across the room can't skip a step. Keys, pedals and clickers do move on, since pressing one is deliberate. Until 29 Sep 2026 glance hid itself as soon as a finger touched it, so on touch screens the tap then landed on the step underneath and moved on. Now the press is left to the big timer's own tap.
"Keep timer small" lasts until Kitchen Mode closes. The button in the corner of glance mode keeps timers in the footer, where "Big timer: off" turns it back on. It isn't saved, since storage belongs to the recipe site and this preference should follow the cook from site to site.
A ringing timer takes over the screen. It used to show only as the pulsing pill in the footer, which is hard to see from across the room at the moment it matters most. Now the view turns red with the timer's name, "Time's up" and what to do next (the sentence after the one the timer came from), whether or not glance mode was on and even while you're touching the screen. A tap anywhere stops that timer, and with two ringing, the one that finished first shows first. Esc still stops them all. While Kitchen Mode is closed, timers ring in the corner as before.
Spoken timers may come late in a background tab. Speech can't be booked on the audio clock the way the beeps are, so when Chrome slows a hidden tab's timers it can come up to a minute after the beeps. The beeps stay on time. Since 2 Oct 2026 this only applies to the browser's voice, the fallback: Alice's pieces are booked on the audio clock.
The timer speaks in a recorded voice, Alice (2 Oct 2026). The browser's voice differs by device and is often robotic, and the promo film needed a voice that sounds like the tool. Alice is Kokoro-82M's bf_alice (Apache 2.0, so free for commercial use with no credit needed), run locally. Tried first: macOS say voices, then all 8 of Kokoro's British voices, chosen by ear. Cloud voices were ruled out for now: Google Chirp 3 HD needs a billing account, ElevenLabs needs a paid plan for commercial use, and OpenAI's terms require telling listeners the voice is AI. Each piece is rendered with no closing punctuation, at speed 1.1, because a full stop or comma made Kokoro end on a creaky syllable (the "up" in "Time's up" sounded rough). The pieces are joined at play time (verb, 50 ms, step, 180 ms, "Time's up"), the same way the film joins them. The MP3 lives inside km.js rather than as a separate file, so the privacy promise stays as it is and sites that block outside requests can't block the voice. Costs: km.js goes from 35 KB to 379 KB (13 KB to about 270 KB compressed), downloaded once per release (Vercel serves it with max-age=0, must-revalidate, so later opens only re-check it), and about 12 MB of memory for the decoded audio once a timer starts. The landing page's "Try it" keeps the browser's voice, so the page stays at 122 KB.
The alarm is a square wave at 1760 Hz, not the 880 Hz sine it started as: closer to a kitchen timer's buzzer, and nearer the 2–5 kHz where hearing is most sensitive (the ISO 226 equal-loudness contours) and phone speakers are loudest.
The grip dots, tear line and pan on the install button are drawn by CSS, so the dragged bookmark is named plain "Kitchen Mode". Chrome always shows its own globe icon for bookmarklets and there's no way to set a different one.
The headline leads with the pain: "No clutter. Just the next step." (29 Sep 2026). It replaced "Read the recipe from across the kitchen.", which sold legibility and left ads, pop-ups and life stories, what everyone knows about recipe sites, to the third section. The lede names them and carries the legibility. The second half says what you get instead, which a plain "no clutter" line wouldn't, and is what sets Kitchen Mode apart from a "Jump to recipe" button. Three lines rather than four ("No ads. No scrolling." was tried first), so the hero is shorter. The hero also names four sites people know ("Works on BBC Good Food, Allrecipes…"), the "Order 001" tag became "Free", and the headline's size is capped by the window's height so the install button shows without scrolling on a 1280×720 laptop window. The link preview (og.png) matches.
Kitchen Mode moves to kitchen-mode.com (1 Oct 2026). A promo video fixes the site's address for good, and people type what they see, so the address in the video should be one worth typing. The page, the bookmark, km.js and the count relay all use https://kitchen-mode.com now, and www.kitchen-mode.com redirects to it. The first plan kept the bookmark on kitchen-mode.vercel.app, so that no bookmark would depend on a domain renewal. It was dropped the same day: there were no real users yet, so nothing had to move, and a vercel.app bookmark only works while the site stays on Vercel. A renewal is the easier promise to keep. Bought on Cloudflare at cost: US$10.46 a year, auto-renew on. The DNS is two CNAME records to Vercel with Cloudflare's proxy off. Considered: "Cook Mode" (cookmode.com, cook-mode.com and cookmode.app are taken, and Cook Mode is already the name of the keep-the-screen-on switch in the Tasty Recipes and WP Recipe Maker cards, so people would confuse the two); kitchenmode.app (clashes with "No app, no account"); kitchenmode.co.uk (US$5.30, but reads as UK-only); kitchenmode.com (taken since 2005). What it costs: the renewal must never lapse, since whoever bought the domain next could run their own code on every recipe page where someone clicks the bookmark. kitchen-mode.vercel.app must stay on the Vercel project, because bookmarks set up before today load km.js from there. It isn't redirected, so /km.js and /relay/ can never be caught by a redirect, and a canonical link points search engines at kitchen-mode.com. Counts are unchanged except their address: they go to PostHog through /relay/ on kitchen-mode.com, and "Try it" now reports its site as kitchen-mode.com, where it said kitchen-mode.vercel.app.
Counts go to Kitchen Mode's own PostHog project (2 Oct 2026). They used to share a project with another app, so totals mixed the two, and a crawler spike after the domain move was hard to read. PostHog's free plan allows one project per organisation, so Kitchen Mode has its own free organisation rather than a paid second project. What it costs: the 301 events from 27 Sep to 2 Oct 2026 stay in the old project, and computer bookmarks set up from 27 to 30 Sep 2026 carry the old key, so they keep reporting there. Those are almost all the author's own.
MIT licence (2 Oct 2026). Someone on X wanted to fork it and found no licence. With no licence, nobody may legally reuse the code, even though it's public here and in km.js. MIT lets anyone copy, change or sell it, as long as the copyright notice goes with it. Considered: no licence (keeps every right, but turns away the people who ask first) and GPL (forks must stay open too, but only if someone enforces it). The cost: anyone can run a copy. The licence covers the files, not the name or the domain, and the README asks forks to use their own name. It covers Alice's voice too: she was generated with Kokoro-82M (Apache 2.0), not recorded from a person.
The eyebrow names the recipe page, and the hero ticket shows the whole step (30 Sep 2026). A design audit found that nothing above the fold said "recipe": the headline sells the pain, and on phones the ticket sat below the buttons, the phone note and the sites line. Now the eyebrow reads "A button for any recipe page · No app, no account" ("Any recipe page · No app" on phones), the page title and link-preview title say "Kitchen Mode: any recipe page, one big step at a time", and one-column layouts put the ticket straight after the buttons (and the drag steps, on a mouse), with the sites line and phone note after it, so a phone's first screen reaches the rail. The ticket shows both sentences of step 3, the current one in ink and the other at 50%, as the real view does. That also fixes the largest contentful paint: it used to be recorded again at about 3.7 s, when the animation faded in the longer second sentence, since Chrome ignores text at opacity 0 until it appears. Both sentences paint on load now, and the step text dropped from 2.25em to 2em so "You'll need" stays above the fold on a 1440×900 window. Clicking the install ticket already wrote a hint, but below the fold; it now sits under the buttons. The favicon is the pan mark on a tomato tile rather than an emoji, so it reads on light and dark tab bars; the footer links to this section; and the hero counts the sites the way the receipt does ("73 sites, including…") instead of "and 69 more". Still open from the audit: self-hosting the fonts, a custom domain (since done: see 1 Oct 2026), and phone setup as tabs rather than an accordion.
The sample recipe's JSON-LD is only added when someone presses "Try it", so Google never reads the landing page as a recipe.
Landing page design: "The Pass". Each recipe step is an order ticket clipped to the steel rail at a restaurant pass. The bookmarks bar is the rail, and installing means hanging the Kitchen Mode ticket on it. Cream ground, white tickets with a clip and drop shadow, tomato red (#d92d20) for actions, amber (#ffb020) for timers, espresso bands; steel is the only cool colour. Fonts are Archivo condensed for headings, IBM Plex Mono for labels and Atkinson Hyperlegible Next for body text (legibility is the point of the tool). The hero ticket plays through a step the way the real view does. Every text and background pair is at least 4.5:1, or 3:1 for text 24px and up, in light and dark mode.
The tool's palette matches the landing page: cream background, tomato red for the step counter, progress and buttons, amber chips for running timers with a light amber tint on tappable times, and "You'll need" and the finish screen as white ticket stubs. Labels (title bar, eyebrows, pills, timers, the serves and cooking-time line, key hints) use the system monospace font; the step text and headings are system-ui. Buttons match the landing page: 10px corners, red with a darker red shadow, or an ink outline. There are no web fonts in the tool, because they're unreliable on other sites' pages, and nothing is rotated or torn, because reading comes first. Ringing timers turn red, so the "screen may sleep" warning is an inverted ink pill instead. Dimmed sentences sit at 50–55% opacity so they still reach 3:1.
The tool was restyled before launch, not after, because installed bookmarklets never update: whatever look ships first is the one early users keep. It also means the ticket on the landing page shows what people actually get.
km.js is written as ASCII (30 Sep 2026). A script loaded without a charset in its Content-Type is read in the page's own encoding. On a Windows-1252 page, the "½" and "¼–¾" in the timer and quantity patterns turned into junk, one pattern stopped parsing and a click did nothing at all: no view, no message, no count, since the loader's message only covers a script that fails to load. The build now writes every non-ASCII character as a \u escape (0.3 KB more), so the page's encoding no longer matters, including for bookmarks already installed. Found by the tests, on a test page without a <meta charset>. Whether Vercel sends charset=utf-8 with km.js, which would have hidden it on most sites, wasn't checked.
Tests run the shipped files in a real browser, offline (30 Sep 2026). A bug in a bookmarklet reaches everyone on the next click, and most of what matters here (glance mode, a timer ringing, the privacy promise, the loader's own code) only shows in a browser. So the Playwright tests click the real loader, read out of the built index.html, on made-up recipe pages, rather than testing the functions one by one, which would have meant splitting kitchen-mode.js into modules and a bundler. Writing them turned up the ASCII problem above, a stray "0" on the first screen of a recipe with no ingredient list, a ringing corner timer that kept pulsing with reduced motion on, and ✕ buttons whose only name for screen readers was the ✕ itself (now "Close" and "Cancel timer"). All four are fixed. The cost is a pinned dev dependency, which vercel.json keeps out of deploys. A test for each false alarm listed under "Before you start" guards the word lists.
Name: kept "Kitchen Mode" because it says what it does. "Recipease" was considered and dropped: it was Jamie Oliver's cookery shop brand, and it sounds identical to "recipes".
km.js on a real site that isn't UTF-8, the ringing corner timer holding still with reduced motion on, and the ✕ buttons' names in VoiceOver and TalkBack.none in drag_ended: all 11 drags on 27 Sep 2026 reported none, so it may not tell a landed drop from a cancelled one even in Chrome