A modern, interactive quick reference sheet for Dungeons & Dragons 5th Edition, supporting both the 2014 and 2024 rulesets.
Built on crobi/dnd5e-quickref with a revamped TypeScript architecture, offline PWA support, and a rich feature set for players and DMs.
Live Demo — natsumeaoii.github.io/dnd5e-quickref
- Dual Ruleset Support — Switch between 2014 and 2024 rules instantly
- Offline PWA — Service Worker caches everything for full offline use
- Customization — Linking, Favorites, Notes (with import/export), and Themes
- Deep Linking — Share specific rules directly via URL
- Modern Stack — Built with Vite 6 + TypeScript 5.7
- Performance — LightningCSS optimizations + zero runtime dependencies
- Accessibility — Full keyboard support, screen reader optimized, reduced motion
| Layer | Technology |
|---|---|
| Build | Vite 6 |
| Language | TypeScript 5.7 (strict) |
| CSS | Vanilla CSS + LightningCSS |
| Icons | Game-icons.net (WebP images) |
| Hosting | GitHub Pages (static) |
- Node.js 22+ and npm (required for development and building).
- A modern web browser (Edge, Chrome, Firefox, Safari).
- Git (optional, for cloning the repository).
-
Clone the repository:
git clone https://github.com/natsumeaoii/dnd5e-quickref.git cd dnd5e-quickref -
Install dependencies:
npm install
-
Start the development server:
npm run dev
Vite opens the app automatically at
http://localhost:5173/. File changes trigger hot-reload.
-
Build the project:
npm run build
npm runs
prebuildfirst, which syncs the app version fromCHANGELOG.md, copies the changelog intopublic/, updates service-worker cache version metadata, then type-checks withtsc --noEmitand produces an optimized bundle indist/.For release version changes, update the top semantic-version heading in
CHANGELOG.md, then run:npm run sync-version
sync-versionalso keeps the root package-lock metadata aligned. -
Preview the build locally:
npm run preview
Serves
dist/on a local HTTP server for final verification before deployment.
For environments without Node.js:
- Build the project using
npm run build, or download a release artifact. - Copy the contents of
dist/(notsrc/) to your web server's public directory (e.g.C:/xampp/htdocs/dnd5e). - Open the corresponding URL (e.g.
http://localhost/dnd5e).
Note: The app must be served over HTTP/HTTPS. Opening via
file://will not work due to browser security restrictions on ES modules and Service Workers.
| Script | Command | Description |
|---|---|---|
dev |
vite |
Start the Vite dev server with HMR |
sync-version |
node scripts/prebuild.js |
Sync version from CHANGELOG.md to package.json, package-lock.json, src/config.ts, public/sw.js, and copy CHANGELOG.md to public/ |
prebuild |
npm run sync-version |
Automatic version/changelog sync before npm run build |
build |
tsc --noEmit && vite build |
Type-check then produce production bundle in dist/; npm runs prebuild first |
preview |
vite preview |
Serve the production build locally |
type-check |
tsc --noEmit |
Run TypeScript type-checking without emitting |
lint |
eslint "src/**/*.ts" |
Lint TypeScript files via ESLint |
lint:css |
stylelint "src/**/*.css" |
Lint CSS files via Stylelint |
audit:data |
node scripts/audit-data.js |
Validate rule data mirrors, icon mappings, optional markers, environment tags, and bullet/table shapes |
test |
vitest run |
Run the Vitest test suite once |
npm run buildcan mutate tracked version/changelog files throughprebuild. Checkgit statusafter release builds.
Project Structure
dnd5e-quickref/
├── index.html # Main HTML entry point (Vite root)
├── package.json # Node metadata, scripts, dependencies
├── vite.config.ts # Vite build configuration
├── tsconfig.json # TypeScript compiler options
├── eslint.config.js # ESLint flat config (typescript-eslint)
│
├── src/ # TypeScript source (modern layer)
│ ├── main.ts # Application bootstrap & initialization
│ ├── error-handler.ts # Global error boundary module
│ ├── config.ts # Centralized configuration constants
│ ├── types.ts # Shared TypeScript interfaces
│ ├── css/
│ │ ├── quickref.css # Main application styles
│ │ └── icons.css # Icon sprite definitions
│ ├── services/ # Business logic & infrastructure
│ │ ├── DataService.ts # Rule data fetching, caching, & validation
│ │ ├── DBService.ts # IndexedDB wrapper for notes storage
│ │ ├── UserDataService.ts# Import/export notes (GZIP, Web Share API)
│ │ ├── SettingsService.ts# Settings persistence (localStorage)
│ │ ├── PersistenceService.ts # Session state persistence
│ │ ├── SyncService.ts # Cross-tab sync via BroadcastChannel
│ │ ├── KeyboardShortcutsService.ts # Keyboard shortcut handling
│ │ ├── GamepadService.ts # Gamepad input support
│ │ ├── OnboardingService.ts # First-visit guided tour
│ │ ├── A11yService.ts # Accessibility helpers
│ │ ├── WakeLockService.ts# Screen Wake Lock API
│ │ ├── ErrorService.ts # Structured error logging
│ │ ├── PerformanceOptimizer.ts # Performance helpers
│ │ ├── ServiceWorkerMessenger.ts # SW communication bridge
│ │ └── DOMProvider.ts # Cached DOM element references
│ ├── state/
│ │ └── StateManager.ts # Pub/sub event bus for app state
│ ├── ui/ # Presentation layer
│ │ ├── UIController.ts # Top-level UI orchestration
│ │ ├── ViewRenderer.ts # Section & item rendering
│ │ ├── WindowManager.ts # Popup lifecycle (open/close/minimize/resize)
│ │ ├── PopupFactory.ts # Popup DOM construction
│ │ ├── TemplateService.ts# HTML template cloning & population
│ │ └── DragDropManager.ts# Drag-and-drop for favorites reordering
│ └── utils/
│ └── Utils.ts # Shared utilities & Trusted Types policy
│
├── js/ # Legacy JavaScript layer
│ ├── quickref.js # Legacy entry point (still bundled)
│ ├── data/ # Rule data JSON files
│ │ ├── data_*.json # 2014 ruleset (6 categories)
│ │ └── 2024_data_*.json # 2024 ruleset (6 categories)
│ └── modules/ # Legacy JS modules
│ ├── Config.js
│ ├── DataService.js
│ ├── Services.js
│ ├── StateManager.js
│ ├── UIComponents.js
│ └── Utils.js
│
├── css/ # Legacy CSS (consumed by legacy JS layer)
│ ├── quickref.css
│ └── icons.css
│
├── public/ # Static assets (copied verbatim to dist/)
│ ├── sw.js # Service Worker (cache-first strategy)
│ ├── manifest.json # PWA manifest
│ ├── 404.html # Custom 404 page
│ ├── 404.js # Custom 404 redirect/theme helper
│ ├── favicon.ico
│ ├── img/ # Icons & rule images (WebP, PNG, SVG)
│ ├── themes/
│ │ ├── themes.json # Theme registry
│ │ ├── sepia.css
│ │ ├── high-contrast.css
│ │ ├── nord.css
│ │ ├── cyberpunk.css
│ │ └── steampunk.css
│ └── js/data/ # (Mirrors js/data/ in public for SW pre-caching)
│
├── config/ # Linter configurations
│ ├── .eslintrc.json # Legacy ESLint config (superseded by root flat config)
│ ├── .eslintignore
│ ├── .stylelintrc.json # Stylelint config
│ └── .stylelintignore
│
├── scripts/ # Build & automation scripts
│ ├── prebuild.js # Version sync: changelog, package metadata, config, and service worker
│ └── audit-data.js # Rule data mirror/schema/icon audit
│ └── build.js # Legacy build script (not wired to npm scripts)
│
├── .github/workflows/
│ └── deploy.yml # GitHub Actions: build → deploy to GitHub Pages
│
├── CHANGELOG.md # Version history (Keep a Changelog format)
├── LICENSE.md # MIT License
└── dist/ # Build output (gitignored)
The codebase has a dual-layer architecture:
- Modern layer (
src/): TypeScript modules processed by Vite. This is the primary application code. Entry point issrc/main.ts, loaded via<script type="module">inindex.html. - Legacy layer (
js/,css/): The original vanilla JavaScript from the upstreamcrobi/dnd5e-quickrefproject. Thejs/data/directory contains the rule data JSON files consumed by both layers. The JS modules injs/modules/are bundled via the legacyjs/quickref.jsentry point.
Data flows through a service-oriented architecture:
DataServicefetches and caches JSON rule data fromjs/data/StateManagerprovides a pub/sub event busUIControllerorchestrates the presentation layerViewRendererrenders sections and items from the dataWindowManagermanages the popup lifecycle
Rules are stored in js/data/. To add your own:
-
Open
js/data/data_*.json(2014) orjs/data/2024_data_*.json(2024). If you are editing built-in shipped data, mirror the same change inpublic/js/data/. -
Insert a JSON object into the array:
{ "title": "My Custom Rule**", "optional": "Homebrew rule", "icon": "magicswirl", "subtitle": "Short card subtitle", "reference": "PHB p. 123", "description": "One sentence shown near the top of the popup.", "summary": "A concise quick-reference ruling for the popup summary box.", "bullets": [ { "type": "paragraph", "content": "Use paragraph bullets for short explanatory text." }, { "type": "list", "items": [ "Use list bullets for compact rule steps.", "Limited inline markup such as <b>bold</b> and <i>italic</i> is supported." ] }, { "type": "table", "headers": ["Case", "Result"], "rows": [["Example", "Outcome"]] } ] }
optional: Controls visibility."Standard rule"— Always shown."Optional rule"— Hidden unless "Show Optional Rules" is enabled in Settings."Homebrew rule"— Hidden unless "Show Homebrew Rules" is enabled in Settings.
icon: Corresponds to an image filename inpublic/img/(without extension).titlemarkers: Use one trailing*for optional rules and two trailing**for homebrew rules.bullets: Supportsparagraph,list, andtableobjects. Keep rule text concise and source-backed.
- Create a CSS file in
public/themes/(e.g.my-theme.css). - Override CSS custom properties. See
public/themes/sepia.cssfor an example. - Register the theme in
public/themes/themes.json:{ "id": "my-theme", "displayName": "My Custom Theme" } - Reload — the new theme appears in the Settings dropdown.
Pushing to master triggers the GitHub Actions workflow (.github/workflows/deploy.yml):
- Checks out the repository.
- Sets up Node.js (version read from
package.jsonenginesfield). - Runs
npm ciandnpm run build. - Deploys
dist/to GitHub Pages.
Run npm run build and upload the dist/ directory to any static hosting provider (Netlify, Vercel, Cloudflare Pages, S3, etc.). The app uses relative paths (base: './' in Vite config), so it works in any subdirectory.
How do I install this as an App (PWA)?
- Android (Chrome): Menu (⋮) → "Install App" or "Add to Home Screen".
- iOS (Safari): Share button → "Add to Home Screen".
- Desktop (Chrome/Edge): Click the Install icon in the address bar.
How do I switch between 2014 and 2024 rules?
Open Settings (bottom of the page) → toggle "Use 2024 Rules". The app reloads with the new dataset.
Why does the app still show the old version after update?
The Service Worker caches aggressively for offline support. To force an update:
- PC:
Ctrl + Shift + R(Windows) orCmd + Shift + R(Mac). - Mobile: Settings → Privacy → Clear Browsing Data (Cached Images and Files).
- Advanced: DevTools (F12) → Application → Service Workers → "Unregister", then reload.
Can I backup and restore my notes?
Yes. Notes are stored in your browser's IndexedDB.
- Export: Settings → "Export Notes" → save the
.json.gzfile. - Import: Settings → "Import Notes" → select the backup. Notes merge; duplicates overwrite by ID.
- Limits: Max 500 notes, 10 KB per note, 5 MB per import file.
What keyboard shortcuts are available?
Press ? (or click the floating ? button on desktop) to open the shortcuts panel. Available shortcuts:
| Shortcut | Action |
|---|---|
? |
Toggle keyboard shortcuts panel |
Esc |
Close topmost popup |
Ctrl+W |
Close all popups |
Ctrl+E |
Expand/collapse all sections |
Ctrl+P |
Toggle print mode |
T |
Scroll to top |
← → |
Navigate between items in a section |
↑ ↓ |
Navigate between sections |
Enter / Space |
Activate focused item |
Shortcuts are disabled while typing in inputs, textareas, or select fields.
Does the app support gamepad navigation?
Yes. Connect any standard gamepad — the app auto-detects it via the Gamepad API.
- Left stick — Navigate between items (X axis) and sections (Y axis).
- A button (button 0) — Activate the focused item.
How does deep linking work?
Each rule popup has a link icon (🔗) in its header. Clicking it copies a URL containing the rule title as a hash parameter. When someone opens that URL, the app scrolls to the matching section and opens the popup automatically.
How do favorites work?
Click the star (★) on any rule item to add it to your Favorites section (appears at the top of the page). Favorites are stored in localStorage and persist across sessions. You can drag and drop favorites to reorder them.
How do I change the theme or enable dark mode?
Open Settings → use the Color Theme dropdown (Original, Sepia, High Contrast, Nord, Cyberpunk, Steampunk) and the Dark Mode toggle. Both persist in localStorage. See the Adding Custom Themes section to create your own.
What does "Display Density" do?
Settings → Display Density adjusts the spacing and sizing of rule items. Options: Compact, Normal (default), Comfortable. Useful for different screen sizes or personal preference.
What does "Keep Screen On" do?
Enabling Keep Screen On in Settings uses the Screen Wake Lock API to prevent your device's screen from dimming or locking while the app is visible. Useful during game sessions. The lock is automatically released when the tab loses visibility and re-acquired when it becomes visible again. Not all browsers support this API.
How do I use print mode?
Press Ctrl+P or use the print button. Print mode expands all sections and hides interactive UI (popups, FABs, settings) so the page prints cleanly. Press Ctrl+P again to exit print mode.
Does the app sync across tabs?
Yes. The app uses BroadcastChannel to synchronize settings changes (theme, ruleset, etc.) across open tabs in the same browser. Changes made in one tab are reflected instantly in others, provided both tabs are running the same app version.
git clone https://github.com/natsumeaoii/dnd5e-quickref.git
cd dnd5e-quickref
npm install
npm run dev- TypeScript: Strict mode enabled. Run
npm run type-checkbefore committing. - ESLint: Flat config in
eslint.config.js. Enforcesconsistent-type-imports,no-eval,eqeqeq, andno-console(exceptwarn/error/info). - Stylelint: CSS linting via
npm run lint:cssusing config inconfig/.stylelintrc.json. - Vitest: Focused regression tests for services, state, utilities, data guardrails, and runtime behavior.
- TypeScript source lives in
src/. Do not add new.jsfiles tojs/modules/. - Rule data (JSON) lives in
js/data/. Each category has paired files:data_<category>.json(2014) and2024_data_<category>.json(2024). - Public data mirrors live in
public/js/data/; keep mirrors identical when editing shipped rules. - Static assets belong in
public/. Vite copies them todist/verbatim. - Version is single-sourced from
CHANGELOG.md. Thesync-versionscript propagates it topackage.jsonandsrc/config.ts, then copies the changelog topublic/CHANGELOG.md.
Note: This project uses Vitest for unit testing core logic (services, utilities, state handlers).
- Run tests once:
npm run test- Run tests in watch mode:
npx vitest
- Test coverage is incomplete: Vitest coverage is focused on core state, services, utilities, data guardrails, and selected runtime behaviors. Full visual regression coverage and legacy JS coverage are not present.
- Legacy dual-layer: The
js/andcss/directories contain the original vanilla JS codebase. These are still present for backwards compatibility but are progressively being superseded bysrc/. scripts/build.jsis a legacy artifact: It references dependencies (fs-extra,glob,esbuild,html-minifier-terser) that are not listed inpackage.json. It is not wired to any npm script. The active build pipeline usesvite build.- Service Worker caching: The SW uses a stale-while-revalidate strategy. Users may see stale content until the background update completes on next navigation. Hard-refresh forces a fresh load.
file://protocol unsupported: ES modules and Service Workers require HTTP(S).
- Original Project — crobi/dnd5e-quickref
- Live Demo: https://crobi.github.io/dnd5e-quickref/preview/quickref.html
- License: MIT
- 2024 Rules Content — nico-713/dnd5e-quickref-2024
- Live Demo: https://nico-713.github.io/dnd5e-quickref-2024/
- License: MIT
- Based Inspiration — mfriik/dnd5e-quickref
- Live Demo: https://dnd.milobedzki.pl/
- License: MIT
- Icons — Game-icons.net
- Favicon — IconDuck
- Sources:
- Player's Handbook 2014 & 2024 (PHB)
- Dungeon Master's Guide 2014 & 2024 (DMG)
- Monster Manual 2014 & 2025 (MM)
- Xanathar's Guide to Everything (XGE)
- Tasha's Cauldron of Everything (TCE)
MIT — Copyright © 2016–2026 NatsumeAoii and contributors.