A New Tab extension for Chrome, Edge & Firefox that greets every new tab with a fresh dad joke.
Powered by icanhazdadjoke.com Β· four card themes Β· a sky that changes with the hour Β· recent-bookmark shortcuts Β· on-device translation Β· one-click image export.
- Features
- Themes
- The time-of-day gradient
- Translation
- Download as image
- Privacy & permissions
- Install
- Project structure
- Development
- Build & package
- Release / publish
- Roadmap
- Contributing
- License
- π A fresh dad joke on every new tab, with an Another one button for the next.
- π¨ Four card themes β Basic, Terminal, DOS, and Twitter β switchable from a dock at the bottom.
- π Dark mode β
auto(follows your OS),light, ordark. - π Dreamy fluid gradient that shifts colour with the hour, from midnight indigo to dawn rose to midday sky to dusk magenta.
- π Time & date β a live clock on the Basic card, a timestamp on the Twitter card, both localized.
- π Recent bookmarks β your five latest bookmarks as quick-launch circles under the card, with site favicons (a coloured monogram where none is available).
- π Auto-translation into your browser's language, running on-device (Chrome 138+).
- β¬οΈ Download the current card as a PNG, rendered locally β nothing is uploaded.
- π On-device only β no accounts, no tracking, no data collection; two read-only permissions, no network beyond the joke API.
Every preference β theme, dark-mode setting, and translation toggle β is saved in
localStorage, so the extension needs no storage permission. It reads your recent
bookmarks (and their cached favicons) locally to render the shortcuts; nothing
about them ever leaves your device.
Every theme presents the same joke as a card with its own personality. All cards share one width, and the Another one button sits centred just below the card (so it stays out of the exported image).
| Theme | Look |
|---|---|
| Basic | Clean frosted card with a brand + live clock on the dreamy gradient |
| Terminal | macOS terminal window β $ dadjoke --random with a blinking caret |
| DOS | Retro MS-DOS blue screen β C:\JOKES>, box border, block cursor |
| Presented as a tweet β avatar, verified badge, timestamp, action row |
Terminal and DOS use their own opaque cards; the time-of-day gradient shows softly around them. Basic and Twitter sit directly on it. Twitter follows dark mode with a proper light/dark card.
The background is a smooth function of the clock. Eight colour anchors spaced every three hours β midnight β deep night β dawn β morning β midday β afternoon β dusk β evening β are linearly interpolated by the exact minute, so 07:45 sits three quarters of the way from dawn toward morning. Three slowly drifting, blurred blobs paint the sky, and the foreground ink colour is chosen from the resulting luminance so text stays legible at every hour and in every mode.
See lib/daylight.ts.
Jokes arrive from the API in English. When your browser is set to another language, Dad Joke translates the joke (and the small UI labels) into it using Chrome's built-in Translator API (Chrome 138+). The model runs on-device β no network request, no API key, and no permission.
- A
DE/FR/ β¦ chip appears in the switcher; tap it to flip back to the original English at any time. - The first translation of a new language may need a one-time model download, which the browser grants on your first click; until then the joke shows in English.
- Where the API isn't available (e.g. Firefox), the joke simply stays in English β nothing breaks.
See lib/translate.ts.
The β¬ button in the switcher exports the current card as a PNG
(dad-joke-<theme>.png). Rendering is done locally with
html-to-image β the card is drawn to a
canvas in your browser and downloaded directly, so nothing is uploaded and no
capture permission is required. The Another one button lives outside the card,
so it never appears in the exported image.
Dad Joke declares just two permissions, both used entirely on-device:
| Permission | Why |
|---|---|
bookmarks |
Read your five most recent bookmarks to show them as shortcuts. Never transmitted. |
favicon (Chrome/Edge) |
Show each bookmark's icon from the local favicon cache β no network request. |
Everything else needs no permission:
- The joke API returns
Access-Control-Allow-Origin: *, so the New Tab page fetches a joke directly β no host permission. - Preferences live in
localStorageβ nostoragepermission. - Translation (built-in Translator API) and PNG export (
html-to-image) both run on-device β no network, no permission.
The only network request is the single joke fetch per new tab. No accounts, no analytics, no trackers, no data collection β and your bookmarks never leave your device. Full details in PRIVACY.md.
From the stores:
From source (unpacked):
npm install
npm run build # Chrome/Edge β .output/chrome-mv3
npm run build:firefox # Firefox β .output/firefox-mv2- Chrome/Edge: open
chrome://extensions, enable Developer mode, click Load unpacked, and select.output/chrome-mv3. - Firefox: open
about:debuggingβ This Firefox β Load Temporary Add-on, and pick any file inside.output/firefox-mv2.
Version 2 is built with WXT and TypeScript. WXT generates a
per-browser manifest from wxt.config.ts plus the files in entrypoints/, and
handles building, zipping, and store submission from a single codebase. The New Tab
page is auto-wired as chrome_url_overrides.newtab.
entrypoints/
newtab/
index.html # card markup (chrome for each theme lives here)
main.ts # wiring: fetch, theme/mode/lang switcher, clock, download
style.css # all four themes + the gradient + switcher
lib/
joke.ts # fetch a random joke from the API
config.ts # API endpoint + user agent
daylight.ts # time-of-day gradient palette engine
themes.ts # the four theme definitions
translate.ts # on-device translation (built-in Translator API)
bookmarks.ts # recent bookmarks + local favicons
prefs.ts # theme + dark-mode + translate prefs (localStorage)
public/icon/ # 16 / 32 / 48 / 128 px extension icons
screenshots/ # theme screenshots used in this README
wxt.config.ts # manifest config
.github/workflows/ # CI (build) + Release (zip & publish)
Runtime dependency: html-to-image
(card β PNG export). Everything else is dev-only (wxt, typescript).
Requires Node 22+.
npm install # installs deps and runs `wxt prepare`
npm run dev # live-reload dev build for Chrome
npm run dev:firefox # live-reload dev build for Firefox
npm run compile # type-check only (tsc --noEmit)npm run build # production build (Chrome, .output/chrome-mv3)
npm run build:firefox # production build (Firefox, .output/firefox-mv2)
npm run zip # Chrome store zip
npm run zip:firefox # Firefox store zip + sources zip
npm run zip:edge # Edge store zip
npm run package # all three at onceZips land in .output/.
Publishing is automated by .github/workflows/release.yml.
Bump version in package.json, then push a matching tag:
# e.g. for version 2.0
git tag v2.0
git push origin v2.0The workflow verifies the tag matches package.json, type-checks, zips all three
targets, uploads them as build artifacts, and runs wxt submit for any store whose
secrets are configured. Steps for stores without secrets are skipped, so
Chrome-only or Firefox-only releases work out of the box.
| Store | Required repository secrets |
|---|---|
| Chrome | CHROME_EXTENSION_ID, CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN |
| Firefox | FIREFOX_EXTENSION_ID, FIREFOX_JWT_ISSUER, FIREFOX_JWT_SECRET |
| Edge | EDGE_PRODUCT_ID, EDGE_CLIENT_ID, EDGE_API_KEY |
Continuous integration (.github/workflows/ci.yml)
type-checks and builds both browsers on every push and pull request.
Firefox note: set your own
browser_specific_settings.gecko.idinwxt.config.tsbefore publishing to AMO.
- Dad joke preview + Another one
- Four card themes (Basic, Terminal, DOS, Twitter)
- Dark mode (auto / light / dark)
- Time-of-day fluid gradient
- Time & date view
- On-device auto-translation
- Download card as PNG
- Recent-bookmark quick-launch shortcuts
- Random background
- More themes
Issues and pull requests are welcome. Please keep the permission footprint
minimal (and on-device β no new network calls) and run npm run compile
before opening a PR. Adding a theme is mostly a new entry in lib/themes.ts plus
a data-theme block in entrypoints/newtab/style.css.
AGPL-3.0-or-later Β© Amir Douzandeh.
Jokes courtesy of icanhazdadjoke.com.


