App offline-first per gestire una roadmap di prodotto, in un unico file HTML. Niente build step, niente backend, niente dipendenze NPM. Apri e usa.
- Due viste della stessa roadmap: per Quarter (Q1-Q4 di un anno) o per Now / Next / Later.
- Drag-and-drop per spostare gli item tra colonne o riordinarli dentro la stessa colonna.
- Filtri combinabili: per tag (con logica AND / OR), per status, per testo libero.
- Persistenza locale in
localStorage— i dati restano tra refresh, niente cloud. - Bilingue IT / EN — toggle in topbar, scelta persistita, auto-detect della lingua del browser al primo avvio.
- Import / Export in JSON, export PNG (snapshot della board), stampa PDF via dialog di stampa del browser.
- Undo dopo eliminazione (toast con 6 secondi per ripristinare).
- Validazione live, char-counter sui campi, autocomplete dei tag basato sui tag esistenti.
- Tema dark, glassmorphism, completamente responsive (mobile incluso).
- Apri
index.htmlin un browser moderno (Chrome, Firefox, Safari, Edge). - Clicca + New (o premi
N) per aggiungere il primo item. - Trascina le card tra le colonne per spostarle.
- Clicca su un tag o sul badge di status per filtrare.
Per testare l'app con dati realistici, importa sample-roadmap.json:
- Apri il menu Export ▾ in alto a destra
- Import JSON…
- Seleziona
sample-roadmap.json
Contiene 14 item su tutti i quarter, status e owner.
| Tasto | Azione |
|---|---|
N |
Nuovo item (quando fuori dai campi di input) |
⌘K / Ctrl+K |
Focus sulla barra di ricerca |
⌘⏎ / Ctrl+Enter |
Salva (dentro il modal) |
Esc |
Chiudi modal / menu Export |
L'interfaccia è disponibile in italiano e inglese. Cambi lingua con il toggle IT / EN in alto a destra nella topbar.
- La scelta è salvata in
localStorage(chiaveroadmap_lang) e persiste tra le sessioni. - Al primo avvio, se non c'è una preferenza salvata, l'app prova a dedurre la lingua dal browser (
navigator.language), con fallback su italiano. - Viene tradotta solo la chrome (bottoni, label, menu, toast, scorciatoie). Il contenuto degli item (titoli, descrizioni, owner, tag) resta come l'hai scritto — non viene tradotto.
{
id: "uid-string",
title: "Faster onboarding flow", // required, max 120 char
desc: "Cut signup → first value...", // optional, max 500 char
status: "idea" | "planned" | "in_progress" | "done",
owner: "Growth", // optional, max 80 char
tags: ["Retention", "UX", "iOS"], // 0-3 tag, max 40 char ciascuno
quarter: "2026-Q2", // formato YYYY-Q[1-4]
nnl: "now" | "next" | "later",
order: { "2026-Q2": 0, "next": 1 },
createdAt: "2026-02-15T11:00:00.000Z",
updatedAt: "2026-04-22T10:15:00.000Z"
}Single-file vanilla JS/HTML/CSS. Nessun framework, nessun bundler, nessun build step.
index.html ~1600 righe
├── <style> CSS variables + glassmorphism dark theme
├── <body> markup statico: topbar, filter row, board, modal, toast
└── <script> tutto il logic in un IIFE-like blocco
├── Constants LS keys, VIEWS, NNL_KEYS, TAG_MODES, STATUSES, LIMITS
├── i18n STRINGS (it/en), t(), applyStaticI18n(), setLang()
├── Helpers uid, escape, debounce, safe localStorage wrappers
├── State single mutable object, persisted on every commit
├── Filtering itemMatchesScope + itemMatchesSearch + matchesFilters
├── Rendering render() rebuilds the board on every state change
└── Wiring event listeners at the end
| Funzione | Cosa fa |
|---|---|
sanitizeItem(raw, year) |
Valida e normalizza un item importato. Filtra status invalidi, quarter mal formati, tag duplicati. |
commitAndRender() |
Helper unificato: save() + render(). Usato ovunque cambi lo state. |
handleDrop(itemId, colKey, insertIndex) |
Drag-drop tra/dentro colonne. Calcola (prev + next) / 2 come nuovo order, poi normalizza. |
showToast(msg, type, opts) |
Notifiche bottom-right. Supporta actionLabel + onAction per gli undo. |
t(key, vars) |
Traduzione con interpolazione {placeholder}. Fallback: lingua corrente → en → chiave stessa. |
Dipendenza esterna: html2canvas 1.4.1 via CDN — solo per l'export PNG.
- Palette: scala di grigi su sfondo
#0b0c10, accenti vetrosi con 3 radial-gradient. - Status color: idea (viola pallido), planned (azzurro), in_progress (ambra), done (verde). Riflessi sul left-border delle card.
- Tipografia: SF Pro / system font, 11→22px su 6 step.
- Glassmorphism:
backdrop-filter: blur(8-12px)su topbar, modal, toast.
| Campo | Limite |
|---|---|
| Item title | 120 caratteri (obbligatorio) |
| Item description | 500 caratteri |
| Tag per item | massimo 3 |
| Lunghezza tag | 40 caratteri |
- Refactor critico (rinomina
el, dedup filter logic, costanti,commitAndRender) - Bug fix di robustezza (dedup tag, debounce save, schema migration, validation defensiva)
- Polish GUI fase 1 (3-cluster topbar, filter row dedicata, board header, status-as-left-border)
- Polish GUI fase 2 (tipografia, densità, focus-visible, drag-handle, loading states)
- Feature finali (validation modal, autocomplete tag, shortcuts footer, drag-sort dentro colonna, mobile)
- Internazionalizzazione (i18n IT/EN con toggle,
STRINGS+t(), persistenza lingua)
MIT — fai quello che vuoi, niente garanzie.
An offline-first app to manage a product roadmap — a single HTML file. No build step, no backend, no NPM dependencies. Open and use.
- Two views of the same roadmap: by Quarter (Q1–Q4 of a given year) or by Now / Next / Later.
- Drag-and-drop to move items between columns or reorder them within the same column.
- Combinable filters: by tag (AND / OR logic), by status, by free-text search.
- Local persistence via
localStorage— data survives page refreshes, no cloud required. - Bilingual IT / EN — toggle in the topbar, persisted choice, auto-detects browser language on first launch.
- Import / Export as JSON, PNG export (board snapshot), PDF print via the browser's print dialog.
- Undo after deletion (toast with 6 seconds to restore).
- Live validation, char-counter on fields, tag autocomplete based on existing tags.
- Dark theme, glassmorphism, fully responsive (mobile included).
- Open
index.htmlin a modern browser (Chrome, Firefox, Safari, Edge). - Click + New (or press
N) to add the first item. - Drag cards between columns to move them.
- Click a tag or a status badge to filter.
To test the app with realistic data, import sample-roadmap.json:
- Open the Export ▾ menu in the top right
- Import JSON…
- Select
sample-roadmap.json
It contains 14 items across all quarters, statuses, and owners.
| Key | Action |
|---|---|
N |
New item (when outside input fields) |
⌘K / Ctrl+K |
Focus the search bar |
⌘⏎ / Ctrl+Enter |
Save (inside the modal) |
Esc |
Close modal / Export menu |
The interface is available in Italian and English. Switch with the IT / EN toggle in the top right of the topbar.
- The choice is saved in
localStorage(keyroadmap_lang) and persists across sessions. - On first launch, the app auto-detects the browser language (
navigator.language), falling back to Italian. - Only the chrome is translated (buttons, labels, menus, toasts, shortcuts). Item content stays as written.
{
id: "uid-string",
title: "Faster onboarding flow", // required, max 120 chars
desc: "Cut signup → first value...", // optional, max 500 chars
status: "idea" | "planned" | "in_progress" | "done",
owner: "Growth", // optional, max 80 chars
tags: ["Retention", "UX", "iOS"], // 0–3 tags, max 40 chars each
quarter: "2026-Q2", // format YYYY-Q[1-4]
nnl: "now" | "next" | "later",
order: { "2026-Q2": 0, "next": 1 },
createdAt: "2026-02-15T11:00:00.000Z",
updatedAt: "2026-04-22T10:15:00.000Z"
}Single-file vanilla JS/HTML/CSS. No framework, no bundler, no build step.
index.html ~1600 lines
├── <style> CSS variables + glassmorphism dark theme
├── <body> static markup: topbar, filter row, board, modal, toast
└── <script> all logic in an IIFE-like block
├── Constants LS keys, VIEWS, NNL_KEYS, TAG_MODES, STATUSES, LIMITS
├── i18n STRINGS (it/en), t(), applyStaticI18n(), setLang()
├── Helpers uid, escape, debounce, safe localStorage wrappers
├── State single mutable object, persisted on every commit
├── Filtering itemMatchesScope + itemMatchesSearch + matchesFilters
├── Rendering render() rebuilds the board on every state change
└── Wiring event listeners at the end
| Function | What it does |
|---|---|
sanitizeItem(raw, year) |
Validates and normalises an imported item. Filters invalid statuses, malformed quarters, duplicate tags. |
commitAndRender() |
Unified helper: save() + render(). Used wherever state changes. |
handleDrop(itemId, colKey, insertIndex) |
Drag-drop between/within columns. Computes (prev + next) / 2 as new order, then normalises. |
showToast(msg, type, opts) |
Bottom-right notifications. Supports actionLabel + onAction for undo. |
t(key, vars) |
Translation with {placeholder} interpolation. Fallback: current lang → en → key itself. |
External dependency: html2canvas 1.4.1 via CDN — PNG export only.
- Palette: greyscale on
#0b0c10background, glassy accents with 3 radial gradients. - Status colours: idea (pale purple), planned (blue), in_progress (amber), done (green). Reflected on the card's left border.
- Typography: SF Pro / system font, 11–22 px over 6 steps.
- Glassmorphism:
backdrop-filter: blur(8-12px)on topbar, modal, toast.
| Field | Limit |
|---|---|
| Item title | 120 characters (required) |
| Item description | 500 characters |
| Tags per item | maximum 3 |
| Tag length | 40 characters |
- Critical refactor (rename
el, dedup filter logic, constants,commitAndRender) - Robustness bug fixes (tag dedup, debounce save, schema migration, defensive validation)
- GUI polish phase 1 (3-cluster topbar, dedicated filter row, board header, status-as-left-border)
- GUI polish phase 2 (typography, density, focus-visible, drag-handle, loading states)
- Final features (modal validation, tag autocomplete, shortcuts footer, drag-sort within column, mobile)
- Internationalisation (i18n IT/EN with toggle,
STRINGS+t(), language persistence)
MIT — do whatever you want, no warranties.