A tiny, fast notes app as a pure frontend demo. No backend or bundler required β just open it via a local web server.
Pairs with notes-backend. This frontend is fully standalone (localStorage), the backend is optional.
Tech stack: Vanilla JS (ES Modules), HTML5, CSS (Design Tokens, Light/Dark Mode), localStorage, ESLint (Flat Config), Prettier
- Features
- Demo
- Requirements
- Quick Start
- Project Structure
- Theming
- Searching with #tags
- Backup (Export/Import)
- Keyboard Shortcuts
- Linting & Formatting
- Scripts
- Commits & Changelog
- Data & Privacy
- Roadmap
- Deployment
- Troubleshooting
- License
- Related
- βοΈ Create / edit / delete notes
- π Pin important notes to keep them on top
- π Live search (title & content)
- #οΈβ£ #tags in search (AND filter; tags are extracted on-the-fly from title/content)
- β¨ Search highlighting (
<mark>
for text; highlighted tag chips) - β¬οΈβ¬οΈ Export / Import (JSON; merge or replace existing notes)
- π Dark Mode with system detection & toggle (persisted in
localStorage
) - πΎ Persistence via
localStorage
- β¨οΈ Shortcut:
Ctrl/Cmd + Enter
saves a new note - βΏ A11y:
aria-live
for the list, visually hidden labels, clear focus ring - π± Responsive: two-column layout, stacks on mobile
- Browser: recent versions of Chrome/Edge, Firefox, or Safari.
- Node.js (optional β dev tools only): Node 18+ (recommended 20+) and npm. Not needed to use the app, but helpful for
npm run lint
/format
and CI. - Local web server: required because of ES Modules (see Quick Start for options).
You do not need a bundler. Simply run a local server in the project root.
# 1) Clone the repo
git clone https://github.com/rluetken-dev/notes-frontend.git
cd notes-frontend
# 2) (optional) Install dev dependencies for lint/format
npm install
# 3) Start a local server (pick one)
# β VS Code: "Live Server" extension β open index.html β "Go Live"
# β npx: npx http-server -c-1 -p 5173
# β python: python -m http.server 5173
# 4) Open in the browser
# http://localhost:5173
Why a server? Because of ES Modules (
<script type="module" src="src/app.js">
). Openingindex.html
viafile://
blocks module loading due to CORS and file protocol restrictions.
notes-frontend/
ββ index.html # App shell & markup (DE UI, English code comments)
ββ styles.css # Design tokens, layout, components, dark mode
ββ src/
β ββ app.js # Orchestration: state, render, events
β ββ storage.js # loadNotes()/saveNotes() (localStorage)
β ββ theme.js # Theme controller (toggle, system, persistence)
β ββ time.js # now(), timeAgo() (de-DE)
β ββ dialogs.js # confirmDialog() using the modal
β ββ backup.js # exportNotes()/parseImportedFile()/mergeNotes()
β ββ utils.js # generateId(), escapeHtml(), sort, match, tags, highlight
ββ assets/
β ββ screenshot-light.png
β ββ screenshot-dark.png
ββ eslint.config.mjs # ESLint Flat Config (ESM, browser)
ββ .prettierrc.json # Prettier config
ββ .prettierignore
ββ package.json # npm scripts (lint/format), dev deps
ββ COMMITS.md # Conventional Commits Leitfaden
ββ .github/
β ββ workflows/
β ββ ci.yml # minimal CI (Node 20 + Prettier check)
ββ README.md # you are here
- Design tokens in
:root
(light defaults) + dark variants via@media (prefers-color-scheme: dark)
. - Explicit override via
html[data-theme="light|dark"]
(set by the toggle button). - No hard-coded colors in components β everything derives from tokens.
Token highlights: --bg
, --text
, --muted
, --card
, --border
, --primary
, --on-primary
, --danger
, --on-danger
, --input-border
, --accent
, --accent-bg
, --accent-ring
, --empty-border
, --empty-bg
, --empty-text
.
- Write tags anywhere in title or content using
#like-this
(letters, digits,_
and-
). - Query supports free text and tags together.
- All tags must match (AND). Free text matches if it appears in title or content.
Examples
#work
β notes tagged#work
#work #inbox
β notes that have both tagsmeeting
β full-text searchmeeting #work
β full-text and tag filter
- Export (.json): downloads a file like
notes-frontend-YYYYMMDD-HHMMSS.json
. - Import (.json): choose a file; you can Replace all notes or Merge with existing ones.
- Merge rule: for identical
id
s, the item with the newerupdatedAt
wins.
- Merge rule: for identical
- The Export button is disabled when there are no notes yet.
JSON shape
{
"app": "notes-frontend",
"version": 1,
"exportedAt": 1690000000000,
"notes": [
{
"id": "β¦",
"title": "β¦",
"content": "β¦",
"createdAt": 1690000000000,
"updatedAt": 1690000000000,
"pinned": false
}
]
}
Tip: localStorage
is origin-scoped β http://localhost:5173
and http://127.0.0.1:5173
are different stores.
- Save new note:
Ctrl/Cmd + Enter
- Theme toggle: click
π
- Right-click on
π
β reset to System
- Right-click on
# Lint (Flat Config)
npm run lint
# Format (Prettier)
npm run format
Notes:
- ESLint uses the Flat Config (
eslint.config.mjs
). Remove legacy.eslintrc.*
files. - Prettier handles formatting; ESLint focuses on code quality.
npm run format
β format code with Prettiernpm run format:check
β verify formattingnpm run lint
β ESLint (Flat Config)
This repo follows Conventional Commits to keep history and changelogs clean.
See COMMITS.md for the rules, allowed types, scopes, and examples.
Examples
feat(ui): add search highlight for tags
fix(storage): handle empty import file gracefully
docs: update README with screenshots
chore(prettier): format markdown
Releases
- Tag releases as
vX.Y.Z
(SemVer). - Keep release notes concise and link the compare view.
- All data is stored locally in the browser via
localStorage
. - No data is sent to any server.
- You can clear storage manually in your browser at any time.
- Backups are plain JSON files; review them before sharing.
- Note: storage is origin-scoped (
http://localhost:5173
βhttp://127.0.0.1:5173
).
- Tag management (rename/delete, suggestions while typing)
- Result snippets (show a short context around search hits)
- Sorting options (e.g., by
updatedAt
,title
) - Markdown preview (read-only render)
- PWA (offline, installable)
- IndexedDB storage (scales better than localStorage)
- Tests: unit (Vitest) & end-to-end (Playwright)
- A11y polish: focus trap in modals, improved keyboard navigation
- GitHub Pages: static hosting β publish the
main
branch or adocs/
folder. - Netlify/Vercel: set up as a βStatic Siteβ, leave the build command empty, publish the repo root.
ES Modules wonβt load?
- Use
http://localhost:β¦
(notfile://
). - Ensure your HTML has:
<script type="module" src="src/app.js"></script>
- Check the server root: the browser must find
/src/app.js
relative toindex.html
.
ESLint complains about import/export
?
- Confirm you use the Flat Config:
eslint.config.mjs
at the project root. - Remove legacy
.eslintrc.*
files. - If needed, set
languageOptions.sourceType = "module"
in the flat config.
Dark Mode doesnβt change?
- Make sure the
#theme-toggle
button exists inindex.html
. - Check for a hard-coded
data-theme
on<html>
that could force a theme. - Open DevTools β Console for
localStorage
errors (e.g., private mode).
Export button is disabled?
- Thatβs by design when there are no notes yet. Create a note first.
Import seems to do nothing?
- Verify the JSON format (see Backup section).
- On conflicts (same
id
), the note with the newerupdatedAt
wins. - Reminder:
localStorage
is origin-scoped (localhost
β127.0.0.1
).
Search feels off?
- Tag filters are AND combined:
#work #inbox
requires both. - Free-text matches title or content; highlighting is simple substring matching.
MIT β feel free to use, learn, and extend.