A visual bookmark Speed Dial that replaces the Chrome New Tab page.
Instead of the default blank tab, LUPX Bookmark greets you with a clean grid of bookmark cards — each showing a favicon and a title — organized into collapsible, drag-and-drop groups. Everything is stored locally in your browser; no account, no server, no tracking.
The built-in Chrome bookmark bar is linear and hard to scan at a glance. Third-party Speed Dial extensions are either bloated, require sign-in, or inject ads. LUPX Bookmark is intentionally minimal: a single-purpose extension that does exactly one thing and stays out of your way.
chrome.storage.onChanged| Layer | Technology |
|---|---|
| Language | TypeScript 5 (strict mode) |
| UI framework | React 18 |
| Bundler | Vite 8 + vite-plugin-web-extension |
| Styling | CSS Modules |
| Chrome APIs | chrome.storage.sync · chrome.storage.local · chrome.storage.onChanged · chrome.history |
| Manifest | Version 3 (MV3) |
| Package manager | npm |
No runtime dependencies beyond React. No Redux, no router, no UI library.
npm install
npm run dev
Vite rebuilds into dist/ on every file save.
npm run build
npm run typecheck
chrome://extensionsdist/ folder generated by the buildTo apply code changes during development: after each save the dist/ folder is updated automatically; click the ↺ refresh icon on the extension card in chrome://extensions to reload.
lupx-bookmark/
├── public/
│ └── icons/
│ ├── lupx_logo.png # Extension icon (16 / 48 / 128 px)
│ └── pin.svg # Fallback favicon placeholder
├── src/
│ ├── newtab/
│ │ ├── newtab.html # New Tab page entry point
│ │ ├── newtab.tsx # React root — layout, drag state, event wiring
│ │ ├── newtab.module.css
│ │ ├── useAccordions.ts # Accordion groups CRUD + chrome.storage.local
│ │ ├── useSettings.ts # User settings + chrome.storage.sync
│ │ ├── useBackground.ts # Background rendering + file upload to storage.local
│ │ └── useWallpapers.ts # Built-in wallpaper gallery loader
│ ├── options/
│ │ ├── options.html # Extension options page
│ │ ├── options.tsx
│ │ └── options.module.css
│ ├── components/
│ │ ├── AccordionGroup/ # Collapsible group with drag handles
│ │ ├── BookmarkCard/ # Individual bookmark card (all 8 style variants)
│ │ ├── AddSlotModal/ # Add-bookmark dialog with history suggestions
│ │ ├── SearchBar/ # Search bar with engine picker
│ │ └── SettingsPanel/ # Slide-in settings drawer
│ ├── types/
│ │ └── index.ts # Shared TypeScript types and constants
│ └── utils/
│ └── favicon.ts # favicon resolution: chrome://favicon2/ → Google S2 → pin.svg
├── manifest.json
├── vite.config.ts
└── tsconfig.json
All logic runs inside the New Tab page. There is no background service worker.
chrome.storage.sync) — synced across devices, small payload: layout prefs, theme, card style, search engine, background config.chrome.storage.local) — stored locally only; can hold the larger bookmark payload without the sync quota limits.chrome.storage.local, key "backgroundImage") — stored as a base-64 data-URL to survive browser restarts without requiring a host permission.newtab.tsx so cross-group drops work without prop-drilling callbacks through multiple levels.BookmarkCard and AccordionGroup; the active style is passed as a prop from settings, never read from DOM.MIT — see LICENSE.