Sections & sticky notes inside Gmail — built so it can't read your email. No OAuth, no API, no server.
HTML
0
161 commits
updated Aug 18, 2026
Group threads under your own section headers inside Gmail label views (e.g. Action → "IGNITE work", "Talent"), and attach a private note to any thread ("waiting on Laura's reply"). Everything renders in Gmail's own visual language.
Shelf never gets access to your Gmail account, and nothing leaves your browser. It's a page-level extension: no Gmail API, no OAuth grant, no server, no analytics, and zero network requests. It can't create/delete labels, send, archive, or read anything server-side. What it does see is the thread list already rendered on your screen — the same as any extension that draws into Gmail, which is exactly what Chrome's install prompt tells you at install time. The point isn't that it can't see the page; it's that it was never given your account, and there's nowhere for anything it sees to go. Your sections, groupings, and notes live on your device in Chrome's extension storage (mirrored to Chrome sync when it fits). Uninstall it and Gmail is exactly as it was.
I live in Gmail. I work in education, which means my inbox is a firehose of parent emails, student questions, forms, and follow-ups — half of them "waiting on someone," with context I'd otherwise have to reconstruct every single time I reopened the thread. I keep a tight inbox and label everything, and for years I kept hitting the same wall: a label is just a smaller pile. Opening my "Action" label and seeing thirty undifferentiated threads told me nothing about what I was waiting on, what was urgent, or what I'd already dealt with in my head.
I tried the big organizer tools. Every one of them wanted full access to my mailbox — read, send, delete — to draw their interface over it, and most of them turned Gmail into a different app entirely. I didn't want a different app. I wanted Gmail, plus shelves.
So I built the lightest possible version for myself: drag threads under your own headers, stick a note on a conversation, in Gmail's own visual language — and architecturally unable to touch the mail itself, because it never asks for any access to it. Then my wife started using it — she's the opposite of me, things just vanish into her inbox — and it stuck for her too. That's when friends told me to publish it.
This repo is the whole thing. It's small on purpose — read it, and you'll see there's no server, no analytics, and nowhere for anything it sees to go.
chrome://extensionsshelf-for-gmail)After any code update, hit the ↻ refresh icon on the Shelf card in chrome://extensions, then reload the Gmail tab.
Shelf activates in label views and the inbox (search results stay untouched). The inbox keeps its own set of sections, separate from every label's.
j/k, or the row under the pointer if you last moved the mouse — a pointer left sitting somewhere never overrides the keyboard. In an open conversation, Alt+N edits its note in place. Alt combos never collide with Gmail's own single-key shortcuts.j/k and ↑/↓ follow your order — in Labs. By default Gmail's cursor walks its own date order, so in a shelved label it appears to jump around. labs.cursor fixes it by steering Gmail's own cursor through the order you see: one cursor, still Gmail's, so Enter, e, #, r all keep acting on the row you're looking at. If Gmail ever stops honoring the mechanism, Shelf hands the keys straight back rather than guessing. Enable at options.html?labs=1; background in ARCHITECTURE.md.Notes and groupings live in local extension storage (~10MB — effectively unlimited for this use) and are mirrored to Chrome sync so your other desktops pick them up. If your data outgrows sync's 100KB cap, Shelf keeps working locally and just skips the mirror. Nothing appears on mobile — that's the tradeoff for requiring zero email permissions.
Backup: right-click the Shelf icon in chrome://extensions → Options (or Details → Extension options) → Export/Import a JSON backup. Importing merges — newer items win, nothing gets deleted.
#inbox or #label/...), and that the extension is enabled and the tab was reloaded after install.localStorage.setItem('shelfDebug','1') and reload. Logs are prefixed [Shelf].content.js./u/0/, /u/1/, …) keeps its own sections.manifest.json — MV3 manifest; only permission is storage + running on mail.google.comcontent.js — all Gmail-page logic (sections, drag & drop, notes, rendering)shelf.css — styling, light + dark themeoptions.html / options.js — the backup (export/import) pagepopup.html / popup.js — the toolbar-icon popup (how-to, shortcuts, links)tools/gen_icons.py — regenerates icons/ (pure Python, no deps)HTML
47.4%
JavaScript
37.6%
Python
7.2%
CSS
7.2%
Sections & sticky notes inside Gmail — built so it can't read your email. No OAuth, no API, no server.
HTML
0
161 commits
updated Aug 18, 2026
Group threads under your own section headers inside Gmail label views (e.g. Action → "IGNITE work", "Talent"), and attach a private note to any thread ("waiting on Laura's reply"). Everything renders in Gmail's own visual language.
Shelf never gets access to your Gmail account, and nothing leaves your browser. It's a page-level extension: no Gmail API, no OAuth grant, no server, no analytics, and zero network requests. It can't create/delete labels, send, archive, or read anything server-side. What it does see is the thread list already rendered on your screen — the same as any extension that draws into Gmail, which is exactly what Chrome's install prompt tells you at install time. The point isn't that it can't see the page; it's that it was never given your account, and there's nowhere for anything it sees to go. Your sections, groupings, and notes live on your device in Chrome's extension storage (mirrored to Chrome sync when it fits). Uninstall it and Gmail is exactly as it was.
I live in Gmail. I work in education, which means my inbox is a firehose of parent emails, student questions, forms, and follow-ups — half of them "waiting on someone," with context I'd otherwise have to reconstruct every single time I reopened the thread. I keep a tight inbox and label everything, and for years I kept hitting the same wall: a label is just a smaller pile. Opening my "Action" label and seeing thirty undifferentiated threads told me nothing about what I was waiting on, what was urgent, or what I'd already dealt with in my head.
I tried the big organizer tools. Every one of them wanted full access to my mailbox — read, send, delete — to draw their interface over it, and most of them turned Gmail into a different app entirely. I didn't want a different app. I wanted Gmail, plus shelves.
So I built the lightest possible version for myself: drag threads under your own headers, stick a note on a conversation, in Gmail's own visual language — and architecturally unable to touch the mail itself, because it never asks for any access to it. Then my wife started using it — she's the opposite of me, things just vanish into her inbox — and it stuck for her too. That's when friends told me to publish it.
This repo is the whole thing. It's small on purpose — read it, and you'll see there's no server, no analytics, and nowhere for anything it sees to go.
chrome://extensionsshelf-for-gmail)After any code update, hit the ↻ refresh icon on the Shelf card in chrome://extensions, then reload the Gmail tab.
Shelf activates in label views and the inbox (search results stay untouched). The inbox keeps its own set of sections, separate from every label's.
j/k, or the row under the pointer if you last moved the mouse — a pointer left sitting somewhere never overrides the keyboard. In an open conversation, Alt+N edits its note in place. Alt combos never collide with Gmail's own single-key shortcuts.j/k and ↑/↓ follow your order — in Labs. By default Gmail's cursor walks its own date order, so in a shelved label it appears to jump around. labs.cursor fixes it by steering Gmail's own cursor through the order you see: one cursor, still Gmail's, so Enter, e, #, r all keep acting on the row you're looking at. If Gmail ever stops honoring the mechanism, Shelf hands the keys straight back rather than guessing. Enable at options.html?labs=1; background in ARCHITECTURE.md.Notes and groupings live in local extension storage (~10MB — effectively unlimited for this use) and are mirrored to Chrome sync so your other desktops pick them up. If your data outgrows sync's 100KB cap, Shelf keeps working locally and just skips the mirror. Nothing appears on mobile — that's the tradeoff for requiring zero email permissions.
Backup: right-click the Shelf icon in chrome://extensions → Options (or Details → Extension options) → Export/Import a JSON backup. Importing merges — newer items win, nothing gets deleted.
#inbox or #label/...), and that the extension is enabled and the tab was reloaded after install.localStorage.setItem('shelfDebug','1') and reload. Logs are prefixed [Shelf].content.js./u/0/, /u/1/, …) keeps its own sections.manifest.json — MV3 manifest; only permission is storage + running on mail.google.comcontent.js — all Gmail-page logic (sections, drag & drop, notes, rendering)shelf.css — styling, light + dark themeoptions.html / options.js — the backup (export/import) pagepopup.html / popup.js — the toolbar-icon popup (how-to, shortcuts, links)tools/gen_icons.py — regenerates icons/ (pure Python, no deps)HTML
47.4%
JavaScript
37.6%
Python
7.2%
CSS
7.2%